How to Run WordPress in Kuberbetes

Overview

In this tutorial, you will be guided through the process of running WordPress on Kubernetes.

You will learn how to deploy a WordPress Docker container as a Kubernetes Pod, attach persistent storage for storing themes, plugins, and uploaded content, and connecting it to MySQL database.

This lab will walk you through the process on Google’s GKE.

Secrets

Secrets are what we label any sensitive information that shouldn’t be available in clear text. This includes credentials to backend services, such as databases.

Since WordPress must connect to a database, MySQL in our case, we must create a Kubernetes Secret to store our connection password.

Kubernetes stores secrets as Base64 encodes, so the first thing you need to do is encode your MySQL user password to Base64

echo “my-super-secret-password | base64
c3VwZXItc2VjcmV0LXBhc3N3b3JkCg==

The output of the command will look something similar to the example above. Keep note of this output, as we will need to add it to a secrets YAML file.

Now create a new file named secrets.yml.

touch secrets.yml

Add the following contents to secrets.yml, replacing the value for wordpress_db_password with your base64 encoded string.

---
apiVersion: v1
kind: Secret
metadata:
  name: myblog-secrets
type: Opaque
data:
  wordpress_db_password: c3VwZXItc2VjcmV0LXBhc3N3b3JkCg==

Using the kubectl apply command we will store our database password in Kubernetes as a secret.

Kubectl apply -f secrets.yml

Creating a Pod Deployment

A pod is the smallest atomic part of Kuberbetes, and each pod has at least one container inside of it. Pods have their own isolated namespace, filesystem, and network stack shared by all containers encapsulated inside of it.

A deployment is a declares how a pod should be deployed, including the number of replicas that should be created. While a pod resource can be deployed on its own, deployments are used to increase availability.

Create a new YAML file for your WordPress site.

touch myblog-deployment.yml

Add the following to the file.

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: site1-deployment
  labels:
    app: site1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: site1
  template:
    metadata:
      labels:
        app: site1
    spec:
      containers:
        - name: app1
          image: wordpress:5.2
          ports:
            - containerPort: 80
          env:
            - name: WORDPRESS_DB_HOST
              value: 10.0.0.25:3306
            - name: WORDPRESS_DB_USER
              value: root
            - name: WORDPRESS_DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: myblog-secrets
                  key: wordpress_db_password

Run the kubectl create command to create your WordPress pod and its deployment resource.

kubectl apply -f myblog-deployment.yml

The kubectl get deployments command can be used to list all declared deployments, as well as each ones status.

kubectl get deployments

You will also want to check the status of the pods, as a pod’s health is not tied to the health of a deployment. To check your pod’s status, run the kubectl get pods command.

kubectl get pods

Creating a Service Resource

Pods are ephemeral and that means the so is the IP addresses assigned to them. A pod’s IP address is also not routable outside of a Kubernetes cluster, which is my service resources are created.

The service resource is static and is loosely coupled to the pods it services via labels. Service resources are exposed outside of the cluster through a variety of types, with each having it’s own characteristics.

The following service types are avaialable:

  • ClusterIP
  • NodePort
  • LoadBalancer

A ClusterIP is only routable within the Kubernetes cluster and is ideal for services that should remain internal, such as backend services for applications used by web applications.

The NodePort type ties a service to a specific port shared by all nodes in the cluster.

Finally, there is the LoadBalancer types, which will assign your service its own publicly routable IP address. The LoadBalancer type is a cloud feature, and its availability is dependent on cloud networking, with the exception of MetalLB.

When you run a hosted Kubernetes cluster, such as GKE, AKS, or DigitalOceans Kubernetes, the LoadBalancer type will trigger the provisioning of a compute load balancer that will be tied to your service.

We are going to create a LoadBalancer type service, as this is the most common for exposing services to the public Internet.

Create a new file to configure your WordPress site’s service resource.

touch myblog-service.yml

Add the following contents to it.

---
apiVersion: v1
kind: Service
metadata:
  name: site1-service
spec:
  type: LoadBalancer
  selector:
    app: site1
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80

Create the service for the myblog website by using the kubectl apply command.

kubectl apply -f myblog-service.yml

You can monitor the service’s status by using the kubectl get svc command. When the LoadBalancer type is used a public IP address is assigned to it, and this process can take a minute or two to complete.

kubectl get svc

Attaching Persistence Storage

Containers are ephemeral by default, meaning any plugins, themes, or uploads add to your blog will disappear when a Pod stops. In order to persist data between pod life cycles, you will need to attach storage volumes to it.

Attaching storage is a two-step process in Kubernetes. You must first create a storage claim, where you define the storage itself, and then configure your Pod to mount the storage claim as a volume.

Creating a Persistent Storage Claim

When operating in cloud environment you can create a storage claim alone. The claim will typically create a compute disk volume matching the state declared in the Kubernetes storage claim resource.

We are going to create a persistent volume for all of our blog content, including themes, plugins, and uploads. This is required to ensure these items persist when our pods are recreated.

Create a new file named myblog-wpcontent-storage.yml.

touch mybog-wpcontent-storage.yml

And add the following contents to it to create a 1 GiB storage volume.

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: site1-disk
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi

Mounting a Storage Claim in Your Pod

To mount the storage volume generated by the claim you will need to add to parts to the deployment YAML file. The first instructs the container where to mount a volume, and the second ties the storage claim to the container.

Add the following lines under the container section of the deployment template section.

 volumeMounts:
   - mountPath: "/var/www/wp-content"
     name: wpcontent

Now, under the spec key, add the following to assign the storage claim to the deployment.

volumes:
  - name: wpcontent
    persistentVolumeClaim:
      claimName: site1-disk

Your updated myblog-deployment.yml file should now look similar to the follow example.

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: site1-deployment
  labels:
    app: site1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: site1
  template:
    metadata:
      labels:
        app: site1
    spec:
      containers:
        - name: app1
          image: wordpress:5.2
          ports:
            - containerPort: 80
          volumeMounts:
            - mountPath: "/var/www/wp-content"
              name: wpcontent
          env:
            - name: WORDPRESS_DB_HOST
              value: 10.0.0.25:3306
            - name: WORDPRESS_DB_USER
              value: root
            - name: WORDPRESS_DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: myblog-secrets
                  key: wordpress_db_password
      volumes:
        - name: wpcontent
          persistentVolumeClaim:
            claimName: site1-disk

Apply your changes to the deployment by using the kubectl apply command.

kubectl apply -f myblog-deployment.yml

Kubernetes will automatically detect the changes and update the deployment’s declared state.