Top Tags

Deploy Wordpress with Innodb Cluster in K3s

Deploy Wordpress with Innodb Cluster in K3s

Overview

This guide demonstrates deploying WordPress with MySQL InnoDB Cluster on K3s, a lightweight Kubernetes distribution ideal for edge, IoT, and development environments. MySQL InnoDB Cluster provides high availability with Group Replication, automatic failover, and consistent data across multiple nodes.

Prerequisites:

Architecture Overview

When deploying WordPress with MySQL InnoDB Cluster in K3s, the typical architecture consists of:

  • WordPress Deployment: Single or multi-replica pods running the WordPress application
  • InnoDB Cluster: 3+ MySQL nodes with Group Replication for HA
  • LoadBalancer Service: Exposes WordPress externally on port 80
  • Service Discovery: K3s DNS resolves the InnoDB Cluster endpoint automatically

Network Communication

WordPress pods communicate with the InnoDB Cluster primary node using the MySQL Protocol (TCP/3306). K3s provides automatic service discovery through DNS FQDN: <service-name>.<namespace>.svc.cluster.local

Deploy Wordpress

bash
1nano wordpress.yaml
yaml
1apiVersion: apps/v1
2kind: Deployment
3metadata:
4 name: wordpress
5 namespace: default
6spec:
7 replicas: 1
8 selector:
9 matchLabels:
10 app: wordpress
11 template:
12 metadata:
13 labels:
14 app: wordpress
15 spec:
16 containers:
17 - name: wordpress
18 image: wordpress:latest
19 env:
20 - name: WORDPRESS_DB_HOST
21 value: "10.11.0.102:3306"
22 - name: WORDPRESS_DB_NAME
23 value: "db2"
24 - name: WORDPRESS_DB_USER
25 value: "root"
26 - name: WORDPRESS_DB_PASSWORD
27 value: "123"
28 ports:
29 - containerPort: 80
30---
31apiVersion: v1
32kind: Service
33metadata:
34 name: wordpress
35 namespace: default
36spec:
37 ports:
38 - port: 80
39 selector:
40 app: wordpress
41 type: LoadBalancer

Enhanced WordPress Deployment with Best Practices

For production environments, implement the following enhancements:

1. Using Kubernetes Secrets for Credentials

Instead of hardcoding credentials in environment variables, use Kubernetes Secrets:

bash
1# Create a secret for database credentials
2kubectl create secret generic wordpress-db-secret \
3 --from-literal=db_name=db2 \
4 --from-literal=db_user=root \
5 --from-literal=db_password=your-secure-password

2. Production-Ready Deployment Manifest

yaml
1apiVersion: v1
2kind: Namespace
3metadata:
4 name: wordpress
5---
6apiVersion: v1
7kind: PersistentVolumeClaim
8metadata:
9 name: wordpress-pvc
10 namespace: wordpress
11spec:
12 accessModes:
13 - ReadWriteOnce
14 storageClassName: local-path
15 resources:
16 requests:
17 storage: 20Gi
18---
19apiVersion: apps/v1
20kind: Deployment
21metadata:
22 name: wordpress
23 namespace: wordpress
24 labels:
25 app: wordpress
26 version: v1
27spec:
28 replicas: 2
29 strategy:
30 type: RollingUpdate
31 rollingUpdate:
32 maxSurge: 1
33 maxUnavailable: 0
34 selector:
35 matchLabels:
36 app: wordpress
37 template:
38 metadata:
39 labels:
40 app: wordpress
41 version: v1
42 spec:
43 securityContext:
44 fsGroup: 33
45 runAsNonRoot: false
46 containers:
47 - name: wordpress
48 image: wordpress:6.4.2-apache
49 imagePullPolicy: IfNotPresent
50 ports:
51 - name: http
52 containerPort: 80
53 protocol: TCP
54 env:
55 - name: WORDPRESS_DB_HOST
56 # Use the InnoDB Cluster service endpoint
57 value: "mysql-innodbcluster.default.svc.cluster.local:3306"
58 - name: WORDPRESS_DB_NAME
59 valueFrom:
60 secretKeyRef:
61 name: wordpress-db-secret
62 key: db_name
63 - name: WORDPRESS_DB_USER
64 valueFrom:
65 secretKeyRef:
66 name: wordpress-db-secret
67 key: db_user
68 - name: WORDPRESS_DB_PASSWORD
69 valueFrom:
70 secretKeyRef:
71 name: wordpress-db-secret
72 key: db_password
73 - name: WORDPRESS_TABLE_PREFIX
74 value: "wp_"
75 resources:
76 requests:
77 memory: "128Mi"
78 cpu: "100m"
79 limits:
80 memory: "512Mi"
81 cpu: "500m"
82 livenessProbe:
83 httpGet:
84 path: /
85 port: http
86 initialDelaySeconds: 30
87 periodSeconds: 10
88 timeoutSeconds: 5
89 failureThreshold: 3
90 readinessProbe:
91 httpGet:
92 path: /
93 port: http
94 initialDelaySeconds: 10
95 periodSeconds: 5
96 timeoutSeconds: 3
97 failureThreshold: 2
98 volumeMounts:
99 - name: wordpress-storage
100 mountPath: /var/www/html
101 securityContext:
102 allowPrivilegeEscalation: false
103 readOnlyRootFilesystem: false
104 capabilities:
105 drop:
106 - ALL
107 volumes:
108 - name: wordpress-storage
109 persistentVolumeClaim:
110 claimName: wordpress-pvc
111 affinity:
112 podAntiAffinity:
113 preferredDuringSchedulingIgnoredDuringExecution:
114 - weight: 100
115 podAffinityTerm:
116 labelSelector:
117 matchExpressions:
118 - key: app
119 operator: In
120 values:
121 - wordpress
122 topologyKey: kubernetes.io/hostname
123---
124apiVersion: v1
125kind: Service
126metadata:
127 name: wordpress
128 namespace: wordpress
129 labels:
130 app: wordpress
131spec:
132 type: LoadBalancer
133 selector:
134 app: wordpress
135 ports:
136 - name: http
137 port: 80
138 targetPort: http
139 protocol: TCP
140 sessionAffinity: None

Technical Details and Configuration

Database Connection Parameters

The key configuration variable is WORDPRESS_DB_HOST. In Kubernetes, this should reference the InnoDB Cluster service:

ParameterValueDescription
WORDPRESS_DB_HOSTmysql-innodbcluster.default.svc.cluster.local:3306DNS service name of InnoDB Cluster with port
WORDPRESS_DB_NAMEdb2Database name created in InnoDB Cluster
WORDPRESS_DB_USERrootDatabase user (should be limited in production)
WORDPRESS_DB_PASSWORD(secret)Encrypted password via Kubernetes Secret

InnoDB Cluster Service Discovery

K3s automatically creates DNS records for Kubernetes Services. The format is:

<service-name>.<namespace>.svc.<cluster-domain>

For example:

  • mysql-innodbcluster.default.svc.cluster.local (in default namespace)
  • mysql-innodbcluster.wordpress.svc.cluster.local (in wordpress namespace)

Persistent Storage Configuration

K3s includes the local-path storage provisioner by default, which:

  • Stores data in /var/lib/rancher/k3s/storage/ on the host
  • Supports ReadWriteOnce access mode
  • Suitable for single-node or small clusters

For multi-node production clusters, consider:

  • Longhorn: Distributed block storage
  • NFS: Shared storage for disaster recovery
  • CSI Drivers: Cloud-native storage solutions
yaml
1# Example Longhorn PVC configuration
2apiVersion: v1
3kind: PersistentVolumeClaim
4metadata:
5 name: wordpress-pvc
6spec:
7 accessModes:
8 - ReadWriteOnce
9 storageClassName: longhorn
10 resources:
11 requests:
12 storage: 20Gi

LoadBalancer Service Type

The LoadBalancer service type exposes WordPress externally:

  • K3s Default: Uses klipper-lb (built-in load balancer)
  • Alternative: Use NodePort service if LoadBalancer is unavailable
  • Cloud Integration: Supports cloud provider load balancers (AWS ELB, GCP Load Balancer, etc.)

Deployment and Verification

1. Create the WordPress Namespace

bash
1kubectl create namespace wordpress

2. Create Database Secrets

bash
1kubectl create secret generic wordpress-db-secret \
2 --namespace wordpress \
3 --from-literal=db_name=db2 \
4 --from-literal=db_user=root \
5 --from-literal=db_password='YourSecurePassword123!'

3. Apply Deployment

bash
1kubectl apply -f wordpress-deployment.yaml

4. Verify Deployment Status

bash
1# Check deployment status
2kubectl get deployment -n wordpress
3kubectl describe deployment wordpress -n wordpress
4
5# Monitor pod creation
6kubectl get pods -n wordpress -w
7
8# Check service status
9kubectl get service -n wordpress
10kubectl get service wordpress -n wordpress -o wide

5. Verify Database Connectivity

bash
1# Execute shell in WordPress pod
2kubectl exec -it deployment/wordpress -n wordpress -- /bin/bash
3
4# Inside the pod, test MySQL connectivity
5apt-get update && apt-get install -y mysql-client
6mysql -h mysql-innodbcluster.default.svc.cluster.local -u root -p'123' -e "SHOW DATABASES;"

Troubleshooting

Issue: WordPress Pod Cannot Connect to Database

Symptoms: Pod restarts continuously, logs show "Can't connect to MySQL server"

Solutions:

  1. Verify InnoDB Cluster is running and accessible:

    bash
    1kubectl get pods -n default -l app=mysql
    2kubectl get svc mysql-innodbcluster -n default
  2. Check network connectivity from WordPress pod:

    bash
    1kubectl exec -it deployment/wordpress -n wordpress -- \
    2 nc -zv mysql-innodbcluster.default.svc.cluster.local 3306
  3. Verify secrets are created:

    bash
    1kubectl get secrets -n wordpress
    2kubectl describe secret wordpress-db-secret -n wordpress
  4. Check logs for detailed error messages:

    bash
    1kubectl logs -f deployment/wordpress -n wordpress

Issue: LoadBalancer Service Shows PENDING

Symptoms: kubectl get svc shows <pending> for EXTERNAL-IP

Solutions:

  1. K3s LoadBalancer needs servicelb enabled (default):

    bash
    1kubectl get pods -n kube-system | grep servicelb
  2. For local testing, use port-forward instead:

    bash
    1kubectl port-forward -n wordpress svc/wordpress 8080:80
    2# Access WordPress at http://localhost:8080
  3. Check service endpoint configuration:

    bash
    1kubectl get endpoints wordpress -n wordpress

Performance Optimization

1. Resource Requests and Limits

The provided deployment specifies resource constraints:

  • Requests: Minimum guaranteed resources for scheduling
  • Limits: Maximum resources allowed to prevent resource starvation

Adjust based on expected WordPress traffic:

yaml
1resources:
2 requests:
3 memory: "256Mi" # For high-traffic sites
4 cpu: "250m"
5 limits:
6 memory: "1Gi"
7 cpu: "1000m"

2. Horizontal Pod Autoscaling

Enable automatic scaling based on CPU utilization:

yaml
1apiVersion: autoscaling/v2
2kind: HorizontalPodAutoscaler
3metadata:
4 name: wordpress-hpa
5 namespace: wordpress
6spec:
7 scaleTargetRef:
8 apiVersion: apps/v1
9 kind: Deployment
10 name: wordpress
11 minReplicas: 2
12 maxReplicas: 5
13 metrics:
14 - type: Resource
15 resource:
16 name: cpu
17 target:
18 type: Utilization
19 averageUtilization: 70
20 - type: Resource
21 resource:
22 name: memory
23 target:
24 type: Utilization
25 averageUtilization: 80

3. Pod Disruption Budget

Ensure high availability during cluster upgrades:

yaml
1apiVersion: policy/v1
2kind: PodDisruptionBudget
3metadata:
4 name: wordpress-pdb
5 namespace: wordpress
6spec:
7 minAvailable: 1
8 selector:
9 matchLabels:
10 app: wordpress

Security Considerations

1. Credential Management

  • Never commit passwords to version control
  • Use Kubernetes Secrets for sensitive data
  • Rotate passwords regularly
  • Consider using external secret management (Sealed Secrets, External Secrets)

2. Network Policies

Restrict traffic to WordPress pods:

yaml
1apiVersion: networking.k8s.io/v1
2kind: NetworkPolicy
3metadata:
4 name: wordpress-network-policy
5 namespace: wordpress
6spec:
7 podSelector:
8 matchLabels:
9 app: wordpress
10 policyTypes:
11 - Ingress
12 - Egress
13 ingress:
14 - from:
15 - podSelector: {}
16 ports:
17 - protocol: TCP
18 port: 80
19 egress:
20 - to:
21 - namespaceSelector: {}
22 ports:
23 - protocol: TCP
24 port: 3306
25 - to:
26 - podSelector:
27 matchLabels:
28 k8s-app: kube-dns
29 ports:
30 - protocol: UDP
31 port: 53

3. Image Security

  • Use specific version tags instead of latest
  • Scan images for vulnerabilities
  • Consider using distroless or minimal base images

Maintenance and Backup

1. Backing up WordPress Data

bash
1# Backup persistent volume
2kubectl exec -it deployment/wordpress -n wordpress -- \
3 tar czf - /var/www/html | gzip > wordpress-backup-$(date +%Y%m%d).tar.gz

2. Backing up MySQL InnoDB Cluster

MySQL InnoDB Cluster automatically maintains replicas. For additional safety:

bash
1# Execute backup from WordPress pod
2kubectl exec -it deployment/wordpress -n wordpress -- \
3 mysqldump -h mysql-innodbcluster.default.svc.cluster.local \
4 -u root -p'123' --all-databases | gzip > mysql-backup-$(date +%Y%m%d).sql.gz