Last updated on May 17, 2023
In this tutorial we will learn how to deploy a highly available and automatically scalable containerized installation of popular WordPress CMS, reaping all benefits of modern technologies offered by mainstream public cloud providers such as AWS. We will deploy our containerized WordPress installation on their EKS kubernetes infrastructure, and we will store our website files on their S3 storage service, backed by their CDN (content delivery network) service, thus serving website visitors from geographically close locations, minimizing the lag and improving their overall experience. Biggest advantage of using containerized services is the easiness of implementation of high-availability and more importantly – automatic scalability! This tutorial is of course short and generalized, but with a little bit more work you will be able to set your system in such way that at any given moment it will only use the right amount of resources it needs for smooth operation, drastically cutting down hosting costs when visits are low, and be able to automatically deploy more when visits go up, keeping operation smooth at all times.
Deploy MySQL
Creating a highly available containerized MySQL cluster on Kubernetes can be achieved by deploying a StatefulSet with persistent storage using a combination of Kubernetes manifests. In this example, I’ll provide you with YAML files to deploy a MySQL cluster using the official MySQL Docker image.
1. Create a `mysql-configmap.yaml` file for MySQL configurations:
apiVersion: v1 kind: ConfigMap metadata: name: mysql-configmap data: mysql.cnf: | [mysqld] skip-host-cache skip-name-resolve explicit_defaults_for_timestamp # Enable the READ-COMMITTED isolation level transaction-isolation=READ-COMMITTED # Disable binary logging to avoid running out of disk space # Set this to 'ROW' to enable replication binlog-format=ROW
2. Create a `mysql-storage.yaml` file to define the persistent storage class and volume claim template:
apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: mysql-storage provisioner: kubernetes.io/gce-pd parameters: type: pd-ssd --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: mysql-data annotations: volume.beta.kubernetes.io/storage-class: "mysql-storage" spec: accessModes: - ReadWriteOnce resources: requests: storage: 10Gi
3. Create a `mysql-statefulset.yaml` file for the MySQL StatefulSet deployment:
apiVersion: apps/v1 kind: StatefulSet metadata: name: mysql spec: selector: matchLabels: app: mysql serviceName: mysql replicas: 3 template: metadata: labels: app: mysql spec: containers: - name: mysql image: mysql:8.0 env: - name: MYSQL_ALLOW_EMPTY_PASSWORD value: "1" - name: MYSQL_ROOT_PASSWORD valueFrom: secretKeyRef: name: mysql-secrets key: MYSQL_ROOT_PASSWORD ports: - containerPort: 3306 name: mysql volumeMounts: - name: mysql-config mountPath: /etc/mysql/conf.d - name: mysql-data mountPath: /var/lib/mysql subPath: mysql volumes: - name: mysql-config configMap: name: mysql-configmap items: - key: mysql.cnf path: mysql.cnf volumeClaimTemplates: - metadata: name: mysql-data spec: accessModes: ["ReadWriteOnce"] resources: requests: storage: 10Gi
4. Create a `mysql-service.yaml` file for the MySQL headless service:
apiVersion: v1 kind: Service metadata: name: mysql labels: app: mysql spec: ports: - port: 3306 clusterIP: None selector: app: mysql
5. Apply the Kubernetes manifests:
kubectl apply -f mysql-configmap.yaml kubectl apply -f mysql-storage.yaml kubectl apply -f mysql-statefulset.yaml kubectl apply -f mysql-service.yaml
These YAML files will deploy a 3-node MySQL cluster using a StatefulSet with a headless service for stable network identity and persistent storage for each MySQL instance. Be sure to replace the `MYSQL_ROOT_PASSWORD`
Deploy WordPress
To deploy a highly available WordPress site on a Kubernetes cluster with an S3 storage bucket as the WordPress upload folder, you’ll need to use an S3-compatible WordPress plugin, like WP Offload Media Lite, to handle the uploads. This example assumes you have an external MySQL database and will deploy WordPress with NGINX using Kubernetes manifests.
1. Create a `secrets.yaml` file for sensitive data:
apiVersion: v1 kind: Secret metadata: name: wordpress-secrets type: Opaque stringData: DB_NAME: 'your_database_name' DB_USER: 'your_database_user' DB_PASSWORD: 'your_database_password' DB_HOST: 'your_database_host' AWS_ACCESS_KEY_ID: 'your_aws_access_key_id' AWS_SECRET_ACCESS_KEY: 'your_aws_secret_access_key' AWS_REGION: 'your_aws_region' S3_BUCKET: 'your_s3_bucket_name' S3_UPLOADS_CDN_URL: 'your_cdn_url'
Replace the placeholders with your actual values.
2. Create a `wordpress-deployment.yaml` file:
apiVersion: apps/v1 kind: Deployment metadata: name: wordpress spec: replicas: 3 selector: matchLabels: app: wordpress template: metadata: labels: app: wordpress spec: containers: - name: wordpress image: wordpress:latest env: - name: WORDPRESS_DB_NAME valueFrom: secretKeyRef: name: wordpress-secrets key: DB_NAME - name: WORDPRESS_DB_USER valueFrom: secretKeyRef: name: wordpress-secrets key: DB_USER - name: WORDPRESS_DB_PASSWORD valueFrom: secretKeyRef: name: wordpress-secrets key: DB_PASSWORD - name: WORDPRESS_DB_HOST valueFrom: secretKeyRef: name: wordpress-secrets key: DB_HOST - name: AWS_ACCESS_KEY_ID valueFrom: secretKeyRef: name: wordpress-secrets key: AWS_ACCESS_KEY_ID - name: AWS_SECRET_ACCESS_KEY valueFrom: secretKeyRef: name: wordpress-secrets key: AWS_SECRET_ACCESS_KEY - name: AWS_REGION valueFrom: secretKeyRef: name: wordpress-secrets key: AWS_REGION - name: S3_BUCKET valueFrom: secretKeyRef: name: wordpress-secrets key: S3_BUCKET - name: S3_UPLOADS_CDN_URL valueFrom: secretKeyRef: name: wordpress-secrets key: S3_UPLOADS_CDN_URL ports: - containerPort: 80 volumeMounts: - name: wordpress-plugins mountPath: /var/www/html/wp-content/plugins volumes: - name: wordpress-plugins emptyDir: {}
3. Create a `nginx-deployment.yaml` file:
apiVersion: apps/v1 kind: Deployment metadata: name: nginx spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:latest ports: - containerPort: 80 volumeMounts: - name: nginx-conf mountPath: /etc/nginx/conf.d volumes: - name: nginx-conf configMap: name: nginx-conf
4. Create a `nginx-configmap.yaml` file:
apiVersion: v1 kind: ConfigMap metadata: name: nginx-conf data: default.conf: | upstream wordpress { server wordpress:80; } server { listen 80; location / { proxy_pass http://wordpress; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }
5. Create a `wordpress-service.yaml` file:
apiVersion: v1 kind: Service metadata: name: wordpress spec: selector: app: wordpress ports: - protocol: TCP port: 80 targetPort: 80 clusterIP: None
6. Create a `nginx-service.yaml` file:
apiVersion: v1 kind: Service metadata: name: nginx spec: type: LoadBalancer selector: app: nginx ports: - protocol: TCP port: 80 targetPort: 80
7. Apply the Kubernetes manifests:
kubectl apply -f secrets.yaml kubectl apply -f wordpress-deployment.yaml kubectl apply -f nginx-deployment.yaml kubectl apply -f nginx-configmap.yaml kubectl apply -f wordpress-service.yaml kubectl apply -f nginx-service.yaml
These files will deploy a highly available WordPress site with NGINX as a reverse proxy in a Kubernetes cluster. Remember to install the WP Offload Media Lite plugin and configure it to use the provided AWS credentials and S3 bucket information.
Please note that in a production environment, you should consider using a more secure method to store sensitive data, such as Kubernetes Secrets encrypted with Sealed Secrets or using an external secrets manager like AWS Secrets Manager or HashiCorp Vault.
Be First to Comment