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:
- K3s cluster deployed and running (1.24+)
- MySQL InnoDB Cluster already deployed. Follow the setup guide: MySQL InnoDB Cluster on K3s and expose it to LAN by LoadBalancer
kubectlconfigured to access your cluster- Persistent storage configured (K3s default: local-path provisioner)
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
1nano wordpress.yaml1apiVersion: apps/v12kind: Deployment3metadata:4 name: wordpress5 namespace: default6spec:7 replicas: 18 selector:9 matchLabels:10 app: wordpress11 template:12 metadata:13 labels:14 app: wordpress15 spec:16 containers:17 - name: wordpress18 image: wordpress:latest19 env:20 - name: WORDPRESS_DB_HOST21 value: "10.11.0.102:3306"22 - name: WORDPRESS_DB_NAME23 value: "db2"24 - name: WORDPRESS_DB_USER25 value: "root"26 - name: WORDPRESS_DB_PASSWORD27 value: "123"28 ports:29 - containerPort: 8030---31apiVersion: v132kind: Service33metadata:34 name: wordpress35 namespace: default36spec:37 ports:38 - port: 8039 selector:40 app: wordpress41 type: LoadBalancerEnhanced 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:
1# Create a secret for database credentials2kubectl 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-password2. Production-Ready Deployment Manifest
1apiVersion: v12kind: Namespace3metadata:4 name: wordpress5---6apiVersion: v17kind: PersistentVolumeClaim8metadata:9 name: wordpress-pvc10 namespace: wordpress11spec:12 accessModes:13 - ReadWriteOnce14 storageClassName: local-path15 resources:16 requests:17 storage: 20Gi18---19apiVersion: apps/v120kind: Deployment21metadata:22 name: wordpress23 namespace: wordpress24 labels:25 app: wordpress26 version: v127spec:28 replicas: 229 strategy:30 type: RollingUpdate31 rollingUpdate:32 maxSurge: 133 maxUnavailable: 034 selector:35 matchLabels:36 app: wordpress37 template:38 metadata:39 labels:40 app: wordpress41 version: v142 spec:43 securityContext:44 fsGroup: 3345 runAsNonRoot: false46 containers:47 - name: wordpress48 image: wordpress:6.4.2-apache49 imagePullPolicy: IfNotPresent50 ports:51 - name: http52 containerPort: 8053 protocol: TCP54 env:55 - name: WORDPRESS_DB_HOST56 # Use the InnoDB Cluster service endpoint57 value: "mysql-innodbcluster.default.svc.cluster.local:3306"58 - name: WORDPRESS_DB_NAME59 valueFrom:60 secretKeyRef:61 name: wordpress-db-secret62 key: db_name63 - name: WORDPRESS_DB_USER64 valueFrom:65 secretKeyRef:66 name: wordpress-db-secret67 key: db_user68 - name: WORDPRESS_DB_PASSWORD69 valueFrom:70 secretKeyRef:71 name: wordpress-db-secret72 key: db_password73 - name: WORDPRESS_TABLE_PREFIX74 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: http86 initialDelaySeconds: 3087 periodSeconds: 1088 timeoutSeconds: 589 failureThreshold: 390 readinessProbe:91 httpGet:92 path: /93 port: http94 initialDelaySeconds: 1095 periodSeconds: 596 timeoutSeconds: 397 failureThreshold: 298 volumeMounts:99 - name: wordpress-storage100 mountPath: /var/www/html101 securityContext:102 allowPrivilegeEscalation: false103 readOnlyRootFilesystem: false104 capabilities:105 drop:106 - ALL107 volumes:108 - name: wordpress-storage109 persistentVolumeClaim:110 claimName: wordpress-pvc111 affinity:112 podAntiAffinity:113 preferredDuringSchedulingIgnoredDuringExecution:114 - weight: 100115 podAffinityTerm:116 labelSelector:117 matchExpressions:118 - key: app119 operator: In120 values:121 - wordpress122 topologyKey: kubernetes.io/hostname123---124apiVersion: v1125kind: Service126metadata:127 name: wordpress128 namespace: wordpress129 labels:130 app: wordpress131spec:132 type: LoadBalancer133 selector:134 app: wordpress135 ports:136 - name: http137 port: 80138 targetPort: http139 protocol: TCP140 sessionAffinity: NoneTechnical Details and Configuration
Database Connection Parameters
The key configuration variable is WORDPRESS_DB_HOST. In Kubernetes, this should reference the InnoDB Cluster service:
| Parameter | Value | Description |
|---|---|---|
WORDPRESS_DB_HOST | mysql-innodbcluster.default.svc.cluster.local:3306 | DNS service name of InnoDB Cluster with port |
WORDPRESS_DB_NAME | db2 | Database name created in InnoDB Cluster |
WORDPRESS_DB_USER | root | Database 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(indefaultnamespace)mysql-innodbcluster.wordpress.svc.cluster.local(inwordpressnamespace)
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
ReadWriteOnceaccess 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
1# Example Longhorn PVC configuration2apiVersion: v13kind: PersistentVolumeClaim4metadata:5 name: wordpress-pvc6spec:7 accessModes:8 - ReadWriteOnce9 storageClassName: longhorn10 resources:11 requests:12 storage: 20GiLoadBalancer Service Type
The LoadBalancer service type exposes WordPress externally:
- K3s Default: Uses
klipper-lb(built-in load balancer) - Alternative: Use
NodePortservice 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
1kubectl create namespace wordpress2. Create Database Secrets
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
1kubectl apply -f wordpress-deployment.yaml4. Verify Deployment Status
1# Check deployment status2kubectl get deployment -n wordpress3kubectl describe deployment wordpress -n wordpress4
5# Monitor pod creation6kubectl get pods -n wordpress -w7
8# Check service status9kubectl get service -n wordpress10kubectl get service wordpress -n wordpress -o wide5. Verify Database Connectivity
1# Execute shell in WordPress pod2kubectl exec -it deployment/wordpress -n wordpress -- /bin/bash3
4# Inside the pod, test MySQL connectivity5apt-get update && apt-get install -y mysql-client6mysql -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:
-
Verify InnoDB Cluster is running and accessible:
bash1kubectl get pods -n default -l app=mysql2kubectl get svc mysql-innodbcluster -n default -
Check network connectivity from WordPress pod:
bash1kubectl exec -it deployment/wordpress -n wordpress -- \2 nc -zv mysql-innodbcluster.default.svc.cluster.local 3306 -
Verify secrets are created:
bash1kubectl get secrets -n wordpress2kubectl describe secret wordpress-db-secret -n wordpress -
Check logs for detailed error messages:
bash1kubectl logs -f deployment/wordpress -n wordpress
Issue: LoadBalancer Service Shows PENDING
Symptoms: kubectl get svc shows <pending> for EXTERNAL-IP
Solutions:
-
K3s LoadBalancer needs servicelb enabled (default):
bash1kubectl get pods -n kube-system | grep servicelb -
For local testing, use port-forward instead:
bash1kubectl port-forward -n wordpress svc/wordpress 8080:802# Access WordPress at http://localhost:8080 -
Check service endpoint configuration:
bash1kubectl 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:
1resources:2 requests:3 memory: "256Mi" # For high-traffic sites4 cpu: "250m"5 limits:6 memory: "1Gi"7 cpu: "1000m"2. Horizontal Pod Autoscaling
Enable automatic scaling based on CPU utilization:
1apiVersion: autoscaling/v22kind: HorizontalPodAutoscaler3metadata:4 name: wordpress-hpa5 namespace: wordpress6spec:7 scaleTargetRef:8 apiVersion: apps/v19 kind: Deployment10 name: wordpress11 minReplicas: 212 maxReplicas: 513 metrics:14 - type: Resource15 resource:16 name: cpu17 target:18 type: Utilization19 averageUtilization: 7020 - type: Resource21 resource:22 name: memory23 target:24 type: Utilization25 averageUtilization: 803. Pod Disruption Budget
Ensure high availability during cluster upgrades:
1apiVersion: policy/v12kind: PodDisruptionBudget3metadata:4 name: wordpress-pdb5 namespace: wordpress6spec:7 minAvailable: 18 selector:9 matchLabels:10 app: wordpressSecurity 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:
1apiVersion: networking.k8s.io/v12kind: NetworkPolicy3metadata:4 name: wordpress-network-policy5 namespace: wordpress6spec:7 podSelector:8 matchLabels:9 app: wordpress10 policyTypes:11 - Ingress12 - Egress13 ingress:14 - from:15 - podSelector: {}16 ports:17 - protocol: TCP18 port: 8019 egress:20 - to:21 - namespaceSelector: {}22 ports:23 - protocol: TCP24 port: 330625 - to:26 - podSelector:27 matchLabels:28 k8s-app: kube-dns29 ports:30 - protocol: UDP31 port: 533. 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
1# Backup persistent volume2kubectl exec -it deployment/wordpress -n wordpress -- \3 tar czf - /var/www/html | gzip > wordpress-backup-$(date +%Y%m%d).tar.gz2. Backing up MySQL InnoDB Cluster
MySQL InnoDB Cluster automatically maintains replicas. For additional safety:
1# Execute backup from WordPress pod2kubectl 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