by Dmitry Krasnov

There are hardly many people who have not heard of WordPress — arguably the most popular CMS for websites and blogs. It is available as a docker image (over 10 million pulls on DockerHub), and by running it on Kubernetes you can build a reliable and scalable website platform.

In this tutorial I will show you how to deploy WordPress and a MySQL database using K8s. Both applications use PersistentVolumes and PersistentVolumeClaims to store data.

What are they? PersistentVolume (PV) resources are used to manage durable storage in a cluster. PersistentVolumes can also be used with other storage types like NFS. A PersistentVolumeClaim (PVC) is a request for storage by a user. Refer to the Kubernetes documentation for an exhaustive overview of supported PersistentVolumes.

Steps

In this manual we will go through several steps:

Create PersistentVolumeClaims and PersistentVolumes Create a Secret for MySQL Deploy MySQL Deploy WordPress Troubleshooting

Before we begin

We will use NFS server to store WordPress and MySQL data. Let’s prepare it first.

Open ports TCP:111, UDP: 111, TCP:2049, UDP:2049

$ sudo yum install nfs-utils -y

Now we will share the NFS directory over the private subnet of Kubernetes:

$ sudo vi /etc/exports

/ 172.31.32.0/24(rw,sync,no_root_squash)

You need to change “172.31.32.0/24” to your private cluster subnet.

Create a backup directory for mysql & wordpress files for volumes:

$ sudo mkdir /{mysql,html}

$ sudo chmod -R 755 /{mysql,html}

$ sudo chown nfsnobody:nfsnobody /{mysql,html}

$ sudo systemctl enable rpcbind ; $ sudo systemctl enable nfs-server

$ sudo systemctl enable nfs-lock ; $ sudo systemctl enable nfs-idmap

$ sudo systemctl start rpcbind ; $ sudo systemctl start nfs-server

$ sudo systemctl start nfs-lock ; $ sudo systemctl start nfs-idmap

Create a Secret for MySQL Password

A Secret is an object that stores a piece of sensitive data like a password or key. Each item in a secret must be base64 encoded. Let’s create a secret for admin use. Encode the password (in our case — admin ):

$ echo -n 'admin' | base64

You will get the encoded password: YWRtaW4=

Create a secret.yml file for MySQL and Wordpress that will be mapped as an Environment Variable as follows:

apiVersion: v1

kind: Secret

metadata:

name: mysql-pass

type: Opaque

data:

password: YWRtaW4=

Don’t forget to add your password to the secret.yml above.

Then run:

$ kubectl create -f secret.yml

Deploy Persistent Volume for WordPress & MySQL

Create PV files and change the IP address of the NFS server you are using.

# Create PersistentVolume

# change the ip of NFS server

apiVersion: v1

kind: PersistentVolume

metadata:

name: wordpress-persistent-storage

labels:

app: wordpress

tier: frontend

spec:

capacity:

storage: 10Gi

accessModes:

- ReadWriteMany

nfs:

server: 172.31.39.63

# Exported path of your NFS server

path: "/html"



---

apiVersion: v1

kind: PersistentVolume

metadata:

name: mysql-persistent-storage

labels:

app: wordpress

tier: mysql

spec:

capacity:

storage: 10Gi

accessModes:

- ReadWriteMany

nfs:

server: 172.31.39.63

# Exported path of your NFS server

path: "/mysql"

Now run the following command to create a PV:

$ kubectl create -f pv-wordpress-mysql.yml

Deploy PersistentVolumeClaim(PVC)

PVC is a request for storage that can at some point become available, bound to some actual PV.

Create a PVC for wordpress with the following content:

apiVersion: v1

kind: PersistentVolumeClaim

metadata:

name: wordpress-persistent-storage

labels:

app: wordpress

spec:

accessModes:

- ReadWriteMany

resources:

requests:

storage: 6Gi

And then run:

$ kubectl create -f pvc-wordpress.yml

Do the same for MySQL:

apiVersion: v1

kind: PersistentVolumeClaim

metadata:

name: mysql-persistent-storage

labels:

app: wordpress

spec:

accessModes:

- ReadWriteMany

resources:

requests:

storage: 6Gi

Run:

$ kubectl create -f pvc-mysql.yml

Great, now let’s deploy the apps!

Deploy MySQL

Here’s the mysql-deploy.yml for MySQL service and deployment:

apiVersion: v1

kind: Service

metadata:

name: wordpress-mysql # will be used as a value in

labels: # WORDPRESS_DB_HOST in wordpress-deploy.yml

app: wordpress

spec:

ports:

- port: 3306

selector:

app: wordpress

tier: mysql

clusterIP: None

---

apiVersion: apps/v1beta2 # for versions before 1.9.0 use apps/v1beta2

kind: Deployment

metadata:

name: wordpress-mysql

labels:

app: wordpress

spec:

selector:

matchLabels:

app: wordpress

tier: mysql

strategy:

type: Recreate

template:

metadata:

labels:

app: wordpress

tier: mysql

spec:

containers:

- image: mysql:5.6

name: mysql

env:

- name: MYSQL_ROOT_PASSWORD

valueFrom:

secretKeyRef:

name: mysql-pass # the one generated before in secret.yml

key: password

ports:

- containerPort: 3306

name: mysql

volumeMounts:

- name: mysql-persistent-storage # which data will be stored

mountPath: "/var/lib/mysql"

volumes:

- name: mysql-persistent-storage # PVC

persistentVolumeClaim:

claimName: mysql-persistent-storage

The file consists of 2 separate configs:

The Service part maps MySQL’s port 3306 and makes it available for all containers with the labels app:wordpress & tier:mysql .

The Deployment part declares the creation strategy and specs of our MySQL container:

it’s an image from the Docker Hub: mysql:5.6

it has app:wordpress & tier:frontend labels (used in Service)

& labels (used in Service) it contains an environment variable called MYSQL_ROOT_PASSWORD which holds the value from our secret password

which holds the value from our secret password it has an open port 3306

it has a volume claim mounted in /var/lib/mysql .

Now create the deployment and service:

$ kubectl create -f mysql-deploy.yml

Deploy WordPress

The process above is pretty much the same for WordPress. Here’s the wordpress-deploy.yml:

# create a service for wordpress

apiVersion: v1

kind: Service

metadata:

name: wordpress

labels:

app: wordpress

spec:

ports:

- port: 80

selector:

app: wordpress

tier: frontend

type: ClusterIP---

apiVersion: apps/v1beta2 # for versions before 1.9.0 use apps/v1beta2

kind: Deployment

metadata:

name: wordpress

labels:

app: wordpress

spec:

selector:

matchLabels:

app: wordpress

tier: frontend

strategy:

type: Recreate

template:

metadata:

labels:

app: wordpress

tier: frontend

spec:

containers:

- image: wordpress:4.8-apache

name: wordpress

env:

- name: WORDPRESS_DB_HOST

value: wordpress-mysql

- name: WORDPRESS_DB_PASSWORD

valueFrom:

secretKeyRef:

name: mysql-pass # generated before in secret.yml

key: password

ports:

- containerPort: 80

name: wordpress

volumeMounts:

- name: wordpress-persistent-storage

mountPath: "/var/www/html" # which data will be stored

volumes:

- name: wordpress-persistent-storage

persistentVolumeClaim:

claimName: wordpress-persistent-storage

Again, the file consists of two configs:

Service maps port 80 of the container to the node’s external IP:Port for all containers with the labels app:wordpress & tier:frontend

Deployment declares the creation spec of our WordPress container:

it’s an image from the Docker Hub: wordpress:4.8-apache

it has app:wordpress & tier:frontend labels (used in Service)

& labels (used in Service) it contains environment variables WORDPRESS_DB_HOST , which is the internal host name of the MySQL instance, and WORDPRESS_DB_PASSWORD , which holds the value from our secret password

, which is the internal host name of the MySQL instance, and , which holds the value from our secret password it has an open port 80

it has a volume claim mounted in /var/www/html from which the WP sources are served.

Here’s what we do next:

$ kubectl create -f wordpress-deploy.yml

Launch WordPress

To access WordPress list the services and navigate to the External IP:Port , in our case that would be EXTERNAL_IP:32723.

Done! Now you can create your own blog or website in the WordPress web panel.

Troubleshooting

You can check if everything works fine by using the following commands:

$ kubectl get pv,pvc // list pv,pvc for MySQL & WordPress $ kubectl get deployment // list deployment for MySQL & WordPress $ kubectl get pods $ kubectl describe pods pod-name //pod-name from previous Command

On NFS Server

Check NFS server and you will see your data under /mysql & /html

$ ls /mysql ; ls /html

After installing Wordpress try to delete one pod and it will mount data from /html automatically:

$ kubeclt delete pods pod-name

Conclusion

We have deployed a wordpress with MySQL, Persistent volumes, and NFS on Kubernetes. The main benefit of this stack is flexibility since it allows you to implement practically any type of workflow. This workflow can be extended or complexified depending on your development needs.