Kubernetes Level 03 Day 02: Deploy LAMP Stack on Kubernetes Cluster¶
This document outlines the solution for Kubernetes Level 03 Day 02. The objective was to provision a dynamic PHP website backed by a MySQL database, all running within a single multi-container Pod (the LAMP stack).
Table of Contents¶
- Kubernetes Level 03 Day 02: Deploy LAMP Stack on Kubernetes Cluster
- Table of Contents
- Task Overview
- Step-by-Step Solution
- Deep Dive: Multi-Container Architectures
Task Overview¶
Objective: Deploy a WordPress-ready LAMP stack using Kubernetes native objects.
- Deployment Name:
lamp-wp - Containers: *
httpd-php-container:webdevops/php-apache:alpine-3-php7mysql-container:mysql:5.6
- ConfigMap:
php-config(setsvariables_order = "EGPCS") - Services:
lamp-service: NodePort30008(HTTP)mysql-service: Port3306(Internal)
- Secrets Handling: Pull database credentials from existing secrets into environment variables for both containers.
Step-by-Step Solution¶
1. Create the ConfigMap¶
PHP needs the variables_order set to "EGPCS" to ensure it correctly populates the $_ENV superglobal from environment variables.
Command:
kubectl create configmap php-config --from-literal=php.ini='variables_order = "EGPCS"'
2. Define the Unified Manifest¶
We will create a YAML file defining the Deployment and both Services.
Create generic secret:
kubectl create secret generic database \
--from-literal=MYSQL_ROOT_PASSWORD=admin123 \
--from-literal=MYSQL_DATABASE=kodekloud \
--from-literal=MYSQL_USER=kodekloud \
--from-literal=MYSQL_PASSWORD=admin123 \
--from-literal=MYSQL_HOST=mysql-service
Edit index.php file:
<?php
$dbname = $_ENV["MYSQL_DATABASE"];
$dbuser = $_ENV["MYSQL_USER"];
$dbpass = $_ENV["MYSQL_PASSWORD"];
$dbhost = $_ENV["MYSQL_HOST"];
$connect = mysqli_connect($dbhost, $dbuser, $dbpass) or die("Unable to Connect to '$dbhost'");
$test_query = "SHOW TABLES FROM $dbname";
$result = mysqli_query($test_query);
if ($result->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
echo "Connected successfully";
Command:
vi lamp-stack.yaml
Content:
apiVersion: apps/v1
kind: Deployment
metadata:
name: lamp-wp
spec:
replicas: 1
selector:
matchLabels:
app: lamp-app
template:
metadata:
labels:
app: lamp-app
spec:
volumes:
- name: php-config-volume
configMap:
name: php-config
containers:
- name: httpd-php-container
image: webdevops/php-apache:alpine-3-php7
ports:
- containerPort: 80
volumeMounts:
- name: php-config-volume
mountPath: /opt/docker/etc/php/php.ini
subPath: php.ini
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 } }
- 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 } }
---
apiVersion: v1
kind: Service
metadata:
name: lamp-service
spec:
type: NodePort
selector:
app: lamp-app
ports:
- port: 80
targetPort: 80
nodePort: 30008
---
apiVersion: v1
kind: Service
metadata:
name: mysql-service
spec:
selector:
app: lamp-app
ports:
- port: 3306
targetPort: 3306
Apply:
kubectl apply -f lamp-stack.yaml
3. Deploy the PHP Application¶
Copy the provided index.php from the jump host into the running container's document root (/app).
Command:
kubectl cp /tmp/index.php lamp-wp-<POD_ID>:/app/index.php -c httpd-php-container
Note: Ensure the index.php utilizes getenv('MYSQL_USER') etc., rather than hardcoded strings.
4. Verification¶
Verify the pods are running and test the database connection.
Check Status:
kubectl get pods
# Wait for 2/2 READY
Test Accessibility:
Open the web browser and access the NodePort 30008.
* Expected Result: "Connected successfully"
Deep Dive: Multi-Container Architectures¶
Shared Network Namespace¶
In this task, MySQL and Apache/PHP reside in the same Pod.
* Networking: They share the same IP address and localhost interface.
* Connectivity: Even though we created a mysql-service, the PHP application should ideally connect to 127.0.0.1:3306. If the MYSQL_HOST secret points to the service name, Kubernetes DNS handles the resolution, but localhost is more efficient for intra-pod communication.
ConfigMap Mounting with subPath¶
When mounting a ConfigMap to a path that already contains files (like /opt/docker/etc/php/), using a standard mount would overwrite the entire directory, potentially deleting other configuration files.
* subPath: By specifying subPath: php.ini, we only project that single specific file into the directory, leaving the rest of the target folder's contents intact.