Deploying WordPress on Kubernetes

Share this post on:

Today, with advances in technology and information readily available, it is very feasible to run your own website—whether it’s a blog, multimedia site, online store, etc.—without relying on third parties. This reduces costs for your business, capital that you’ll need to start in an increasingly competitive world.

In this section, as the heading suggests, we will deploy our website (WordPress CMS) on a Kubernetes system. We will assume that your Kubernetes cluster is already set up and has Ingress and a certificate manager configured. Let’s get started.

We will go through each of the elements that make up our manifest. We start by defining the namespace that will encompass each of the elements in our manifest.

apiVersion: v1
kind: Namespace
metadata:
  name: market

Next, we need to define a volume or directory where we will store the files for our WordPress project. For this, we use a PersistentVolume and PersistentVolumeClaim, utilizing the local directory on our server /mnt/r-data/market.aldrass.com and reserving a capacity of 2GB, as shown below:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: market-wordpress-pv
  namespace: market
spec:
  capacity:
    storage: 2Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  storageClassName: standard
  hostPath:
    path: "/mnt/r-data/market.aldrass.com"

---

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: market-wordpress-pvc
  namespace: market
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 2Gi
  storageClassName: standard

Next, we will proceed to create the deployment, specifying the number of pods needed, the port on which these pods will listen, and naming the Docker image we will use to deploy them.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: wordpress
  namespace: market
spec:
  replicas: 1
  selector:
    matchLabels:
      app: wordpress
  template:
    metadata:
      labels:
        app: wordpress
    spec:
      containers:
        - name: wordpress
          image: wordpress:latest
          ports:
            - containerPort: 80
          volumeMounts:
            - name: wordpress-persistent-storage
              mountPath: /var/www/html
      volumes:
        - name: wordpress-persistent-storage
          persistentVolumeClaim:
            claimName: market-wordpress-pvc

We will define a LoadBalancer service to expose port 80 of the deployment.

apiVersion: v1
kind: Service
metadata:
  name: wordpress
  namespace: market
spec:
  selector:
    app: wordpress
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
  type: LoadBalancer

The last remaining element in our manifest is the Ingress. By defining this, we will provide external network access to our project using a URL for access. In my case, we will use https://market.aldrass.com. It is important to note that using an automatic certificate generator is key here; I am using Let’s Encrypt. This ensures that my project has a self-signed and valid certificate on the internet.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-aldrass-com
  namespace: market
  annotations:
    nginx.ingress.kubernetes.io/force-ssl-redirect: "false"
    nginx.ingress.kubernetes.io/ssl-redirect: "false"
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  ingressClassName: "nginx"
  tls:
  - hosts:
    - market.aldrass.com
    secretName: market.aldrass-cert
  rules:
  - host: "market.aldrass.com"
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: wordpress
            port:
              number: 80

Once we have reviewed each element that makes up our manifest, it will look like this in its entirety:

apiVersion: v1
kind: Namespace
metadata:
  name: market

---

apiVersion: v1
kind: PersistentVolume
metadata:
  name: market-wordpress-pv
  namespace: market
spec:
  capacity:
    storage: 2Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  storageClassName: standard
  hostPath:
    path: "/mnt/r-data/market.aldrass.com"

---

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: market-wordpress-pvc
  namespace: market
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 2Gi
  storageClassName: standard

---

apiVersion: apps/v1
kind: Deployment
metadata:
  name: wordpress
  namespace: market
spec:
  replicas: 1
  selector:
    matchLabels:
      app: wordpress
  template:
    metadata:
      labels:
        app: wordpress
    spec:
      containers:
        - name: wordpress
          image: wordpress:latest
          ports:
            - containerPort: 80
          volumeMounts:
            - name: wordpress-persistent-storage
              mountPath: /var/www/html
      volumes:
        - name: wordpress-persistent-storage
          persistentVolumeClaim:
            claimName: market-wordpress-pvc

---

apiVersion: v1
kind: Service
metadata:
  name: wordpress
  namespace: market
spec:
  selector:
    app: wordpress
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
  type: LoadBalancer

---

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-aldrass-com
  namespace: market
  annotations:
    nginx.ingress.kubernetes.io/force-ssl-redirect: "false"
    nginx.ingress.kubernetes.io/ssl-redirect: "false"
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  ingressClassName: "nginx"
  tls:
  - hosts:
    - market.aldrass.com
    secretName: market.aldrass-cert
  rules:
  - host: "market.aldrass.com"
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: wordpress
            port:
              number: 80

All that’s left is to deploy it to our cluster. To do this, save the manifest in a file with a .yaml extension and deploy it using the following command:

kubectl apply -f market.yaml

If our manifest was deployed without errors, we should see a result like this once we verify our deployment:

Dhampir:market.aldrass.com yuniel$ kubectl -n market get all,ingress
NAME                             READY   STATUS    RESTARTS   AGE
pod/wordpress-7976864d7c-jvjwh   1/1     Running   0          38s

NAME                TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
service/wordpress   LoadBalancer   10.108.240.111   <pending>     80:30436/TCP   38s

NAME                        READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/wordpress   1/1     1            1           38s

NAME                                   DESIRED   CURRENT   READY   AGE
replicaset.apps/wordpress-7976864d7c   1         1         1       38s

NAME                                            CLASS   HOSTS                ADDRESS          PORTS     AGE
ingress.networking.k8s.io/ingress-aldrass-com   nginx   market.aldrass.com   192.168.128.50   80, 443   38s

So far, everything is good; we have deployed our web server with the WordPress base code. However, if we access our URL https://market.aldrass.com, we get the error message: “Error establishing a database connection.” What went wrong? At the moment, our site is unable to access our newly created database, which makes sense. Let’s proceed to provide WordPress with the database access details as follows:

Create a schema on our database server with the name of your preference; in my case, I will call it market. Now we need to identify the name of the pod we just deployed in Kubernetes. To do this, we can list the pods as follows:

kubectl -n market get pod

Now, access the pod using the following command:

kubectl -n market exec -it wordpress-7976864d7c-jvjwh bash

Once inside the pod, open the following file with your preferred editor: /var/www/html/wp-config.php and edit the following parameters with your database access details, then save the changes:

If you refresh your page now, you should be able to see the WordPress installation wizard. The rest is straightforward: complete the setup and choose the required values, and you will have your web application deployed on Kubernetes and ready to use.

If you’ve reached this point and completed the tutorial, congratulations! If not, feel free to leave your comments here, and I’ll do my best to assist you.

Good luck with your projects.

Yuniel Alvarez