Laptop with Kubernetes and WordPress logo on the screen

Let’s get WordPress up and running on Kubernetes

Make sure that you've taken a look at the following tutorial(s) before you continue:

Persistent storage for WordPress should be created by now. Persistent disk should be formatted and detached from the instance. GitLab account should be created and you should have an existing GitLab project.

In this tutorial will be used for project's domain name and example for project name and some prefixes. Commands presented in this tutorial should be executed from the root of tutorial's git repository.

You are going to learn

  • how to build your own docker image
  • how to push this image to a remote container registry
  • how to use remote container registry with Kubernetes
  • how to create a Config Map

WordPress on Kubernetes

With this tutorial you are going to dive a little deeper into containers. You are going to build a custom docker image and then run it on Kubernetes. You could potentially use official WordPress image just like you did with MySQL in the previous part of the tutorial. What would be the fun in that ? Am I right ?
By building, pushing and then pulling a custom container image you gain the knowledge needed to do the same for any other type of the project. Let’s not waste anymore of your time for theory.

Build a custom WordPress docker image

Reliability: HighUpdated: 1 October 2017CopiedBookmarkedBookmark removed

Edit docker/wordpress/Dockerfile file. Change VOLUME and WORKDIR to reflect your project's domain name.

VOLUME /var/www/
WORKDIR /var/www/

Build WordPress docker image based on the Dockerfile you've just modified. Modify USERNAME and PROJECT_NAME to fit your GitLab data. Image will be named wordpress and it will be tagged with version number v1.0.0

docker build -t docker/wordpress

Configure GitLab container registry and push WordPress docker image

Reliability: HighUpdated: 1 October 2017CopiedBookmarkedBookmark removed

Container registry is where you place the docker images that have been built successfully. The most known container registry is Docker Hub. Both Google Cloud Platform and GitLab have their own container registries as well. The reasons why in this tutorial we use GitLab container registry is because it is more cloud platform supplier independent approach. GitLab has much more to offer than just that. You will learn all about it in next part of this tutorial series.

Login to your container registry

docker login

Push docker image to GitLab container registry

docker push

Create GitLab container registry Secret

Reliability: HighUpdated: 1 October 2017CopiedBookmarkedBookmark removed

Kubernetes needs to login to our container registry of choice. For that it requires .docker/config.json file. All you need to know for now is that files and other data are passed to Kubernetes using resources such as Config Maps or Secrets. .docker/config.json file needs to be wrapped in a Secret.

First you need to encode this file with base64 and then put the resulting string inside the k8s/other/registry-gitlab-secret.yaml file. Newlines are not valid within base64 string. Kubernetes will use created Secret to authorize itself when pulling an image is required. The name of this Secret is used later inside a Deployment declaration in imagePullSecret property.

cat ~/.docker/config.json | base64 -w 0
cat ~/.docker/config.json | base64 | tr -d '\n'

Create Secret that will hold the file

kubectl apply -f k8s/other/registry-gitlab-secret.yaml

The Secret should be created inside your namespace. Verify it by checking the metadata.creationTimestamp section of the Secret

kubectl get secret -o yaml

Create Secrets and Config Maps

Reliability: HighUpdated: 1 October 2017CopiedBookmarkedBookmark removed

Config Maps are just like Secrets but they are not ... secret. Edit k8s/wordpress/configmap.yaml and adjust it to fit your needs before creating it. First find and replace with your domain name and then find and replace example with your project name. Set all the values just as if you were defining WordPress constants in wp-config.php file.

kubectl apply -f k8s/wordpress/configmap.yaml

Choose username and password for MySQL database user that will be used by WordPress. Do not use the same name for user and database (WP CLI has problems if you do). Encode both values using base64.

echo -n "username_or_pass" | base64 -w 0
echo -n "username_or_pass" | base64 | tr -d '\n'

Paste data part of the resulting strings inside the k8s/wordpress/db-secret.yaml and create the Secret afterwards. Modify the name of the Secret. Prefix it with your project name instead of example

kubectl apply -f k8s/wordpress/db-secret.yaml

WordPress requires randomized key and salt values retrieved from the API. Use bash script I've created to generate a file containing Secret with those values in base64 encoded form. Script needs curl, sed and base64 to work.

chmod +x

Paste contents of the resulting file inside k8s/wordpress/secret.yaml. Don't forget to modify the name of the Secret to match the pattern PROJECT_NAME-wp-secret. Deployment assumes that the name of this Secret will match this pattern. Finally create the Secret.

kubectl apply -f k8s/wordpress/secret.yaml

Create Persistent Volume and Claim

Reliability: HighUpdated: 1 October 2017CopiedBookmarkedBookmark removed

Following steps are somewhat similar to what was done for MySQL in the previous part of this tutorial series. You need to let your Persistent Volume know which persistent disk should be used. Modify k8s/wordpress/volume.yaml by replacing example with your project's name and paste your WordPress disk's name into pdName. If you've used another name than main for your namespace then do not forget to modify it accordingly inside k8s/wordpress/volume.yaml as well.

 fsType: ext4

Create Persistent Volume and have a look at it.

kubectl apply -f k8s/wordpress/volume.yaml
kubectl get pv
kubectl get pv --namespace=main
kubectl describe pv PV_NAME

Create a Persistent Volume Claim that was referenced inside our Persistent Volume declaration

kubectl apply -f k8s/wordpress/volume-claim.yaml
kubectl get pvc
kubectl get pvc --namespace=main
kubectl describe pvc PVC_NAME

Create Deployment with GitLab hosted docker image

Reliability: HighUpdated: 1 October 2017CopiedBookmarkedBookmark removed

Find and replace first and example next inside k8s/wordpress/deployment.yaml just as you did in previous steps.

Take a look inside k8s/wordpress/deployment.yaml.

At the top you'll find metadata.labels section. Notice the three labels. They will be used by the Service to select this Deployment.

The Deployment declaration has a single container ( in spec.containers ) which is using an image pulled from your GitLab container registry. Change the image property to reflect your docker image URL.


There are a lot of environmet variables defined there. Some of them are based on the Config Map others on those two Secrets that have just been created.

Modified wp-config.php file processes only those environment variables that are prefixed with WPVAR_. wp-config.php declares WordPress constants using the name of the environment variable without the prefix.

There is also an example of passing literal value to an environment variable - VOLUME_MOUNT_PATH. This variable is used by the docker image's docker/wordpress/ script.

At the bottom of the file you will find volumes section. This is where you tell the Deployment which Persistent Volume Claim should be mounted. Now find a related section volumeMounts. This is where you tell the Deployment where to mount that volume. All important WordPress data ( wp-content/ directory) is going to be kept on this volume which in fact is a persistent disk.

Create WordPress Deployment. It might take some time. After the image is pulled, Kubernetes will try to run it. If everything is fine then the Deployment becomes available.

kubectl apply -f k8s/wordpress/deployment.yaml
kubectl get pod
kubectl get deployment
kubectl describe pod POD_NAME
kubectl describe deployment DEPLOYMENT_NAME

Examine the Pod

Reliability: HighUpdated: 1 October 2017CopiedBookmarkedBookmark removed

First learn how to run a command on your Pod by printing it's environment variables.

kubectl exec POD_NAME printenv

Let's connect to the Pod that the Deployment has created and run bash. Notice that we use options -it which I read as interactive to remember it easily but it really means:

-i - pass stdin to the container
-t - stdin is a TTY

kubectl exec -it POD_NAME -- bash -l

bash login shell is started ( -l option). Starting a login shell will cause /etc/profile to be executed which then runs /etc/bash.bashrc. Those files are copied to your docker image when you build it.

When using login shell you will get customized command prompt and some file listing command aliases you might already know and cherish like: l, ll and la.

Have you ever looked in the mirror wondering who you are, where you are and what is your name ? Three simple commands will give you the answers you've been looking for

uname -n

WordPress data on persistent drive

Reliability: HighUpdated: 1 October 2017CopiedBookmarkedBookmark removed

Copy the source WordPress wp-content/ directory to your mounted persistent drive. This is where nginx and WordPress are expecting it to be.

cp -R /usr/src/wordpress/wp-content /var/www/
chown deployer:www-data /var/www/
chmod g+w /var/www/
chown -R deployer:www-data /var/www/
chmod -R g+w /var/www/

Create MySQL database for WordPress

Reliability: HighUpdated: 1 October 2017CopiedBookmarkedBookmark removed

Connect to MySQL database from the Pod.

mysql -uroot -hwordpress-mysql -p

Create database and user for WordPress. Grant all privileges on that database to the user. Substitute username_here, password_here and example with your values.

CREATE USER 'username_here'@'%' IDENTIFIED WITH mysql_native_password BY 'password_here';
GRANT ALL PRIVILEGES ON example.* TO 'username_here'@'%';
SHOW GRANTS FOR 'username_here'@'%';

exit twice

Create Load Balancer Service for WordPress

Reliability: HighUpdated: 1 October 2017CopiedBookmarkedBookmark removed

Create a Load Balancer Service for WordPress deployment. This Service exposes port 80 and points to Pods matching the selector app: example and tier: frontend. When a Load Balancer is created additional charges start applying. Google charges you for something called Network Load Balancing: Forwarding Rule Minimum Service Charge. Monthly cost of this is about the same as the cost of one g1-small instance ( approx. $20). Check Useful links for pricing details link.

In the next part of the tutorial series you will configure your own nginx based Load Balancer so in the end you will only need one forwarding rule to service multiple domains from your cluster.

kubectl apply -f k8s/wordpress/service-load-balancer.yaml

It takes some time for your Service to get an external IP.

kubectl get svc

wordpress-lb <pending>      80:31999/TCP 9s
wordpress-mysql None          <none>         3306/TCP     1d

... and after a while

wordpress-lb 80:31999/TCP 2m
wordpress-mysql None          <none>         3306/TCP     1d

Do not start WordPress installation process just yet. You should see WordPress installation page when you visit http://EXTERNAL-IP

For the data above it would be:

Have a look at the Service you've just created. It has the type set to LoadBalancer. That means that during the creation process Google Compute Engine Load Balancer has been provisioned for this Service.

kubectl get service
kubectl get service --namespace=main
kubectl get service SERVICE_NAME -o yaml
kubectl describe service SERVICE_NAME

Look at the forwarding rule that has been created in the process

gcloud compute forwarding-rules list

You can also use UI to view your Load Balancers. Google Cloud Platform ConsoleNetwork ServicesLoad balancing

This Service is not going to be used in the next part of the tutorial series so go ahead and delete it.

kubectl delete service SERVICE_NAME

Forwarding rules - a word of caution

Please take notice of the following. Deleting your Google Compute Engine Load Balancer Service does not remove the forwarding rule.
You can at the same time have no Services present and be charged for load balancing. Once the Service is deleted you can remove the forwarding rule using

gcloud compute forwarding-rules delete FORWARDING_RULE_NAME

Do not remove forwarding rule if your Service is up and you are still using the corresponding Load Balancer.

Think of forwarding rule as a kind of the connection between your Load Balancer and the outside world (eg. DNS). When you destroy a Load Balancer and re-create it then it will probably get different IP but it will be connected to the same forwarding rule. This way you do not need to change your DNS A record and point your domain to another IP address every time you make changes inside your cluster.

Good job. You deserve a bottle of Grog. Arrrr !
Success ! Let's open a case of Rum. Arrrr !
New skills gained. Beers all around! Arrrr !
Thanks for reading ! Please
  • 1

Leave a comment

Your email address will not be published. Required fields are marked *