# Assignment The Nautilus DevOps team wants to deploy a PHP website on a Kubernetes cluster. They plan to use Apache as the web server and MySQL for the database. The team has already gathered the requirements and now wants to make the website live. More details can be found below: 1) Create a ConfigMap named php-config containing the data variables_order = "EGPCS" for the php.ini file. 2) Create a Deployment named lamp-wp. 3) Within this Deployment, create two containers. The first container should be named httpd-php-container and utilize the image webdevops/php-apache:alpine-3-php7. The second container should be named mysql-container and use the image mysql:5.6. Mount the php-config ConfigMap in the httpd container at the location /opt/docker/etc/php/php.ini. 4) Note that secrets have already been created for the following MySQL-related values: MySQL root password, MySQL user, MySQL password, MySQL host, and MySQL database. These secrets are securely stored and can be accessed as needed. 5) Add the following environment variables for both containers: MYSQL_ROOT_PASSWORD, MYSQL_DATABASE, MYSQL_USER, MYSQL_PASSWORD, and MYSQL_HOST. Ensure that their values are sourced from the secrets created earlier. Please utilize the env field (do not use envFrom) to define the name-value pairs of the environment variables. 6) Create a NodePort type Service named lamp-service to expose the web application, specifying the NodePort as 30008. 7) Create a Service for MySQL named mysql-service, ensuring its port is set to 3306. 8) A file named /tmp/index.php is available on the jump-host. a) Copy this file into the httpd container under the Apache document root at /app, replacing the dummy values for MySQL-related variables with the corresponding environment variables you have defined. Ensure that the MySQL-related details are not hardcoded in this file, and utilize environment variables to retrieve those values. b) You should be able to access the index.php file through NodePort 30008. Upon accessing this page, the message Connected successfully should be displayed. Note: The kubectl utility on the jump-host has been configured to work with the Kubernetes cluster. # Solution # Build LAMP WordPress Stack — `lamp-wp` + `php-config` + services A two-container LAMP pod (Apache/PHP + MySQL), a ConfigMap for `php.ini`, two Services, and a PHP page wired to the DB via env vars. Manifests applied via a multi-document heredoc, then the file copied in. ## Step 0 — Confirm the secret names and keys (do this first) The env vars source from pre-created Secrets. Verify their exact names/keys and adjust the manifest if yours differ: ```bash kubectl get secrets for s in mysql-root-pass mysql-user-pass mysql-host mysql-db-url; do echo "== $s =="; kubectl get secret "$s" -o jsonpath='{.data}' | tr ',' '\n' done ``` This solution uses the standard mapping: | Env var | Secret name | Key | |---------|-------------|-----| | `MYSQL_ROOT_PASSWORD` | `mysql-root-pass` | `password` | | `MYSQL_DATABASE` | `mysql-db-url` | `database` | | `MYSQL_USER` | `mysql-user-pass` | `username` | | `MYSQL_PASSWORD` | `mysql-user-pass` | `password` | | `MYSQL_HOST` | `mysql-host` | `host` | ## Step 1 — Apply ConfigMap + Deployment + Services (heredoc) ```bash kubectl apply -f - <<'EOF' apiVersion: v1 kind: ConfigMap metadata: name: php-config data: php.ini: | variables_order = "EGPCS" --- apiVersion: apps/v1 kind: Deployment metadata: name: lamp-wp labels: app: lamp spec: replicas: 1 selector: matchLabels: app: lamp tier: frontend strategy: type: Recreate template: metadata: labels: app: lamp tier: frontend spec: containers: - name: httpd-php-container image: webdevops/php-apache:alpine-3-php7 ports: - containerPort: 80 env: - name: MYSQL_ROOT_PASSWORD valueFrom: secretKeyRef: { name: mysql-root-pass, key: password } - name: MYSQL_DATABASE valueFrom: secretKeyRef: { name: mysql-db-url, key: database } - name: MYSQL_USER valueFrom: secretKeyRef: { name: mysql-user-pass, key: username } - name: MYSQL_PASSWORD valueFrom: secretKeyRef: { name: mysql-user-pass, key: password } - name: MYSQL_HOST valueFrom: secretKeyRef: { name: mysql-host, key: host } volumeMounts: - name: php-config-volume mountPath: /opt/docker/etc/php/php.ini subPath: php.ini - name: mysql-container image: mysql:5.6 ports: - containerPort: 3306 env: - name: MYSQL_ROOT_PASSWORD valueFrom: secretKeyRef: { name: mysql-root-pass, key: password } - name: MYSQL_DATABASE valueFrom: secretKeyRef: { name: mysql-db-url, key: database } - name: MYSQL_USER valueFrom: secretKeyRef: { name: mysql-user-pass, key: username } - name: MYSQL_PASSWORD valueFrom: secretKeyRef: { name: mysql-user-pass, key: password } - name: MYSQL_HOST valueFrom: secretKeyRef: { name: mysql-host, key: host } volumes: - name: php-config-volume configMap: name: php-config --- apiVersion: v1 kind: Service metadata: name: lamp-service labels: app: lamp spec: type: NodePort selector: app: lamp tier: frontend ports: - port: 80 targetPort: 80 nodePort: 30008 --- apiVersion: v1 kind: Service metadata: name: mysql-service labels: app: lamp spec: selector: app: lamp tier: frontend ports: - port: 3306 targetPort: 3306 EOF kubectl rollout status deployment/lamp-wp ``` ## Step 2 — Point index.php at the env vars (no hardcoded DB values) Edit `/tmp/index.php` on the jump-host so the MySQL values come from `getenv(...)` instead of dummy literals. The corrected connection block: ```php ``` Keep the rest of the file as-is; only swap the four dummy assignments for the `getenv()` calls. ## Step 3 — Copy the file into the httpd container's document root (`/app`) ```bash POD=$(kubectl get pod -l app=lamp -o jsonpath='{.items[0].metadata.name}') kubectl cp /tmp/index.php "$POD":/app/index.php -c httpd-php-container ``` ## How it works ### ConfigMap → php.ini via subPath `php-config` holds one key, `php.ini`, whose value is the directive `variables_order = "EGPCS"`. It's mounted into the httpd container with **`subPath: php.ini`** at `/opt/docker/etc/php/php.ini`. `subPath` mounts a **single file** into an existing directory rather than replacing the whole directory — essential here, since `/opt/docker/etc/php/` contains other files that must stay. Without `subPath`, the mount would hide everything else in that directory. ### Two containers, one pod Apache/PHP and MySQL run as two containers in the **same pod**, sharing the pod's network namespace. Both get the identical five env vars, each sourced from a Secret via **`valueFrom.secretKeyRef`** — using the `env` field per the requirement (not `envFrom`, which would bulk-import a whole Secret under its own key names). The MySQL container reads `MYSQL_*` to initialize the database and user; the httpd container reads the same values so PHP can connect with matching credentials. ### The two Services - **`lamp-service`** — `NodePort`, `targetPort: 80` (Apache's port), `nodePort: 30008`. This is the public entry point: `:30008` → Apache `:80`. - **`mysql-service`** — default ClusterIP, `port: 3306`. It gives MySQL a stable in-cluster DNS name (`mysql-service`) so the app can reach the DB by name. Both Services select the same pod (`app: lamp, tier: frontend`); each routes to the port relevant to its container. Whatever the `mysql-host` Secret contains (e.g. `mysql-service` or `localhost`) is what `MYSQL_HOST`/`$dbhost` becomes — so the app connects through the correct host without hardcoding it. ### Why the PHP file uses getenv() Requirement 8a forbids hardcoding DB details. `getenv('MYSQL_HOST')` etc. pull the values from the container's environment (the Secret-sourced env vars) at request time. So credentials live only in Secrets and env — never in the file — and the page renders `Connected successfully` once the connection succeeds. The webdevops php-apache image serves from **`/app`**, so `index.php` copied there is reachable at the web root. ## Verify ```bash # Pod running with both containers kubectl get pods -l app=lamp # READY 2/2 # php.ini mounted correctly kubectl exec "$POD" -c httpd-php-container -- cat /opt/docker/etc/php/php.ini # Services present kubectl get svc lamp-service mysql-service # The page returns the success message curl -s http://:30008/index.php ``` Expected — pod `READY 2/2`, `php.ini` showing `variables_order = "EGPCS"`, both services present, and the page printing **`Connected successfully`** at `:30008/index.php`. > If it shows a connection error instead, MySQL 5.6 may still be initializing (first boot is slow) — > retry after a minute. If it persists, re-check that the Secret names/keys in Step 0 match the > manifest and that `mysql-host` resolves to a reachable host.