?? Deploying a Laravel Application with MySQL and Nginx on Kubernetes! ??
1. Introduction
"Deploying modern web applications on Kubernetes can be challenging, but it's incredibly rewarding once you get it right. I recently completed deploying a Laravel application with MySQL and Nginx on Kubernetes using MicroK8"
2. Project Overview
3. Kubernetes Components Used
? Namespace – Isolated environment for better resource management
? Deployment – Managed app lifecycle with replica scaling
? ConfigMap & Secret – Centralized app configuration and sensitive data handling
? Persistent Volume (PV) & Persistent Volume Claim (PVC) – Ensured data persistence for MySQL
? Service – Facilitated internal communication between pods
? Ingress – Managed external access and routing
4 .?? How to Install MicroK8s on Ubuntu
? 4.1: Update System Packages
sudo apt update
sudo apt upgrade -y
? 4.2 : Install Snap (if not installed)
Ensure that Snap is installed (MicroK8s is installed via Snap):
sudo apt install snapd -y
? 4.3: Install MicroK8s
sudo snap install microk8s --classic
install a specific version:
sudo snap install microk8s --classic --channel=1.30/stable
? 4.4 : Add User to MicroK8s Group
Add your user to the MicroK8s group to avoid needing sudo for every command:
udo usermod -aG microk8s $USER
newgrp microk8s
? 4.5 : Verify Installation
microk8s status --wait-ready
? 4.6: Enable Core Add-ons
Enable some essential Kubernetes components:
microk8s enable dns storage ingress
For other add-ons, you can list them with:
microk8s enable --help
? Step 4.7: Check Kubernetes Nodes
microk8s kubectl get nodes
? Step 4.8: Start and Stop MicroK8s
microk8s start
microk8s stop
microk8s start
microk8s stop
alias kubectl="microk8s kubect
To make this alias permanent, add it to your ~/.bashrc
echo 'alias kubectl="microk8s kubectl"' >> ~/.bashrc
source ~/.bashrc
Now MicroK8s is installed and ready to use! You can deploy applications, create services, and manage Kubernetes resources. ??
? Step 5: Create Namespace:
apiVersion: v1
kind: Namespace
metadata:
name: laravel-app
Apply the Namespace file:
kubectl apply -f Namespace.yml
Verify the Namespace
microk8s kubectl get namespaces
If you want to set the namespace as the default for all future commands
kubectl config set-context --current --namespace=laravel-app
? Step 6: Create PV and PVC for MySQL
You can create the directory under /data/mysql or any other suitable location:
sudo mkdir -p /data/mysql
Set Permissions: Ensure that Kubernetes has permission to read and write to this directory:
sudo chown -R $USER:$USER /data/mysql
sudo chmod -R 775 /data/mysql
Create a file named pv.yml
apiVersion: v1
kind: PersistentVolume
metadata:
name: mysql-pv
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
hostPath:
path: /data/mysql
create a file named pvc.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pvc
namespace: laravel-app
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
? Apply the Changes:
kubectl apply -f pv.yml -f pvc.yml
Check the pv and pvc status
kubectl get pvc -n laravel-app
kubectl get pv
? Step 7: Create MySQL ConfigMap
Create a file named mysql-configmap.yml:
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql-config
namespace: laravel-app
data:
MYSQL_DATABASE: "laravel"
MYSQL_USER: "laravel_user"
? Step 7.1: Create MySQL Secret
Create a file named mysql-secret.yml:
apiVersion: v1
kind: Secret
metadata:
name: mysql-secret
namespace: laravel-app
type: Opaque
data:
MYSQL_PASSWORD: cGFzc3dvcmQ= # base64 encoded value of 'password'
MYSQL_ROOT_PASSWORD: cm9vdHBhc3N3b3Jk # base64 encoded value of 'rootpassword'
? Step 7.2: Create MySQL Deployment
Create a file named Deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
namespace: laravel-app
spec:
replicas: 1
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8
ports:
- containerPort: 3306
envFrom:
- configMapRef:
name: mysql-config
env:
- name: MYSQL_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: MYSQL_PASSWORD
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: MYSQL_ROOT_PASSWORD
volumeMounts:
- name: mysql-data
mountPath: /var/lib/mysql
volumes:
- name: mysql-data
persistentVolumeClaim:
claimName: mysql-pvc
? Apply the Changes:
#kubectl apply -f mysql-configmap.yml -f mysql-secret.yml -f Deployment.yml
Check the status of the MySQL ConfigMap, Secret, and Deployment.
? Step 7.3: check the mysql pod running or not
kubectl get pods -n laravel-app
Access the MySQL container and import the SQL file into the database. The SQL file is present in the db_backup directory.
? Step 7.4: Create MySQL Service
Create a file named Service.yml
apiVersion: v1
kind: Service
metadata:
name: mysql
namespace: laravel-app
spec:
ports:
- port: 3306
targetPort: 3306
selector:
app: mysql
Check status of t mysql service.
? Step 8: Create pv and pvc for the laravel application and nginx
Create a file named pv.yml and pvc.yml
apiVersion: v1
kind: PersistentVolume
metadata:
name: laravel-pv
namespace: laravel-app
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
hostPath:
path: "/data/laravel"
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: laravel-pvc
namespace: laravel-app
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
Check Status
? Step 9: Create Deployment, Service, ConfigMap, and Secret for the Laravel application.
laravel-configmap.yml
apiVersion: v1
kind: ConfigMap
metadata:
name: laravel-config
namespace: laravel-app
data:
DB_CONNECTION: "mysql"
DB_HOST: "mysql"
DB_PORT: "3306"
DB_DATABASE: "laravel"
DB_USERNAME: "laravel_user"
APP_ENV: "production"
APP_DEBUG: "false"
APP_KEY: "base64:H2b7fzOGniGCzfQDF2DlCvTPpcU00QOoplmbP3F8SqA="
laravel-secret.yml
apiVersion: v1
kind: Secret
metadata:
name: laravel-secret
namespace: laravel-app
type: Opaque
data:
DB_PASSWORD: cGFzc3dvcmQ= # base64 encoded value of 'password'
#cGFzc3dvcmQ=
Service.yml
apiVersion: v1
kind: Service
metadata:
name: laravel
namespace: laravel-app
spec:
selector:
app: laravel
ports:
- protocol: TCP
port: 9000
targetPort: 9000
Deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: laravel
namespace: laravel-app
spec:
replicas: 1
selector:
matchLabels:
app: laravel
template:
metadata:
labels:
app: laravel
spec:
containers:
- name: laravel
image: akhileshpatel123/simple-laravel_application:v2
imagePullPolicy: Always
ports:
- containerPort: 9000
envFrom:
- configMapRef:
name: laravel-config
- secretRef:
name: laravel-secret
volumeMounts:
- name: app-data
mountPath: /var/www/html
volumes:
- name: app-data
persistentVolumeClaim:
claimName: laravel-pvc
Apply the YAML files and check the status.
Access the Laravel app container. No need to clear the cache because it is already cleared in the Dockerfile.
? Step 10 :Create Deployment, Service, Nginx ConfigMap, and Ingress for Nginx.
nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
namespace: laravel-app
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
volumeMounts:
- name: nginx-config
mountPath: /etc/nginx/conf.d/default.conf
subPath: nginx.conf
- name: laravel-code
mountPath: /var/www/ #
volumes:
- name: nginx-config
configMap:
name: nginx-config
- name: laravel-code
persistentVolumeClaim:
claimName: laravel-pvc #
nginx-service.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx
namespace: laravel-app
spec:
type: NodePort
ports:
- port: 80
targetPort: 80
nodePort: 30080
selector:
app: nginx
nginx-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-config
namespace: laravel-app
data:
nginx.conf: |
server {
listen 80;
server_name example.local;
root /var/www/public;
index index.php index.html;
# Handle static files and routes
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# ? Forward API requests to Laravel service (now `/resume`)
location /resume {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
fastcgi_pass laravel:9000;
fastcgi_param SCRIPT_FILENAME $document_root/index.php;
include fastcgi_params;
}
# Forward PHP files to PHP-FPM
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass laravel:9000;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SERVER_NAME $host;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_ADDR $server_addr;
}
# Deny access to .htaccess files
location ~ /\.ht {
deny all;
}
}
ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: multi-path-ingress
namespace: laravel-app
spec:
rules:
- host: example.local
http:
paths:
- path: /resume
pathType: Prefix
backend:
service:
name: nginx
port:
number: 80
- path: /
pathType: Prefix
backend:
service:
name: laravel
port:
number: 9000
Apply the YAML files and check the status.
? Step 11: Add the domain name on the host machine in the /etc/hosts file with the Nginx service Cluster IP for the Ingress.
Now you can access the application using your local domain example.local. I have configured the register page as the default.
? Step 12:Now we can check it our Ingress is working or not.
The full path is https://example.local/resume.
We can see our Ingress is working properly.
I am not experienced in controller services in a Laravel application. I have just demonstrated the Kubernetes service by creating a Laravel application. In the future, I will better optimize the Laravel controller service. For now, I have only created a registration page and another controller named ResumeController.php. The application is definitely not perfect, but I am trying my best. After login, it should ideally redirect to another page, but I am currently redirecting it to the home page.
Thank You !!
Fellow MT@SCEH | IM-BHU | PGDHCM | Healthcare
40 分钟前Great work ??