Cristian Marius Tiutiu, Bikram Gupta, and Anish Singh Walia
WordPress is open-source software designed for everyone, emphasizing accessibility, performance, security, and ease of use to create a website, blog, or app. WordPress is a content management system (CMS) built on PHP and using MySQL as a data store, powering over 30% of internet sites today.
In this tutorial, you will use Helm for setting up WordPress on top of a Kubernetes cluster, to create a highly-available website. In addition to leveraging Kubernetes’s intrinsic scalability and high availability aspects, this setup will help keep WordPress secure by providing simplified upgrade and rollback workflows via Helm.
You will be configuring NitroPack, a plugin used for code minification, caching, CDN and lazy loading.
You will use an external MySQL server to abstract the database component since it can be part of a separate cluster or managed service for extended availability. After completing the steps described in this tutorial, you will have a fully functional WordPress installation within a containerized cluster environment managed by Kubernetes.
To complete this tutorial, you will need:
DigitalOcean
API interaction.Kubernetes
interaction.DOKS
clusters. You can learn more here.DNS
within your DigitalOcean
accountBefore proceeding with the tutorial steps, you need a DigitalOcean Managed Kubernetes Cluster (DOKS) available and ready to use. If you already have one configured, you can skip to the next section - Configuring the WordPress MySQL DO Managed Database.
You can use below command to create a new DOKS cluster:
doctl k8s cluster create <YOUR_CLUSTER_NAME> \
--auto-upgrade=false \
--maintenance-window "saturday=21:00" \
--node-pool "name=basicnp;size=s-4vcpu-8gb-amd;count=3;tag=cluster2;label=type=basic;auto-scale=true;min-nodes=2;max-nodes=4" \
--region nyc1
Note: We recommend using a DOKS cluster with a minimum of 2 worker nodes to reduce application impact in case of node failures. The example from this tutorial is using 3 worker nodes, 4cpu/8gb ($48/month
) each, and the autoscaler configured between 2 and 4 nodes max. So, your cluster cost is between $96-$192/month
with hourly
billing. To choose a different node type, you can pick another slug from doctl compute size list
.
This section explains how to install the NFS provisioner using Helm. If you want to use Digitalocean Kubernetes 1-click instead, skip this section and use this to install NFS provisioner on your cluster.
A new DigitalOcean Block Storage volume is provisioned each time you use a PersistentVolume as part of your Kubernetes stateful application. The StorageClass resource tells Kubernetes about the underlying storage type available. DigitalOcean is using do-block-storage by default.
The below command lists the available storage classes for your Kubernetes cluster:
kubectl get sc
The output looks similar to:
OutputNAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
do-block-storage (default) dobs.csi.digitalocean.com Delete Immediate true 24h
DigitalOcean Block Storage Volumes are mounted as read-write by a single node (RWO). Additional nodes cannot mount the same volume. The data content of a PersistentVolume can not be accessed by multiple Pods simultaneously.
Horizontal pod autoscaling (HPA) is used to scale the WordPress Pods in a dynamic StatefulSet hence WordPress requires a volume mounted as read-write by many nodes (RWX).
NFS (Network File System) is a commonly used solution to provide RWX volumes on block storage. This server offers a PersistentVolumeClaim (PVC) in RWX mode so that multiple web applications can access the data in a shared fashion.
OpenEBS Dynamic NFS Provisioner allows users to create a NFS PV that sets up a new Kernel NFS instance for each PV on top of the user’s choice of backend storage.
Next, you will install the OpenEBS Dynamic NFS Provisioner on your Kubernetes cluster using the OpenEBS Helm Chart. You will be installing and configuring only the dynamic nfs provisioner since WordPress requires it.
First, clone the container-blueprints
repository. Then, change the directory to your local copy and to the DOKS-wordpress
subfolder:
git clone https://github.com/digitalocean/container-blueprints.git
cd container-blueprints/DOKS-wordpress
Next, add the Helm
repository:
helm repo add openebs-nfs https://openebs.github.io/dynamic-nfs-provisioner
helm repo update
Next, open and inspect the assets/manifests/openEBS-nfs-provisioner-values.yaml
file provided in the repository:
nfsStorageClass:
backendStorageClass: "do-block-storage"
Note:
The override shown above changes the default value for the backendStorageClass
to do-block-storage. Please visit openebs nfs provisioner helm values for the full values.yaml
file and more details.
Finally, install the chart using Helm:
helm install openebs-nfs openebs-nfs/nfs-provisioner --version 0.9.0 \
--namespace openebs \
--create-namespace \
-f "assets/manifests/openEBS-nfs-provisioner-values.yaml"
Note: A specific version for the Helm chart is used. In this case 0.9.0 was picked, which maps to the 0.9.0 version of the application. It’s good practice in general, to lock on a specific version. This helps to have predictable results, and allows versioning control via Git.
You can verify openEBS deployment status via:
helm ls -n openebs
The output looks similar to (notice that the STATUS column value is deployed):
OutputNAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
openebs-nfs openebs 1 2022-05-09 10:58:14.388721 +0300 EEST deployed nfs-provisioner-0.9.0 0.9.0
NFS provisioner requires a block storage device to create the disk capacity required for the NFS server. Next, you will configure the default Kubernetes Storage Class (do-block-storage) provided by DigitalOcean as the backend storage for the NFS provisioner. In that case, whichever application uses the newly created following Storage Class, can consume shared storage (NFS) on a DigitalOcean volume using OpenEBS NFS provisioner.
Next, open and inspect the sc-rwx-values.yaml
file provided in the repository:
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: rwx-storage
annotations:
openebs.io/cas-type: nsfrwx
cas.openebs.io/config: |
- name: NSFServerType
value: "kernel"
- name: BackendStorageClass
value: "do-block-storage"
provisioner: openebs.io/nfsrwx
reclaimPolicy: Delete
Explanations for the above configuration:
provisioner
- defines what storage class is used for provisioning PVs (e.g. openebs.io/nfsrwx)reclaimPolicy
- dynamically provisioned volumes are automatically deleted when a user deletes the corresponding PersistentVolumeClaimFor more information please about openEBS please visit the OpenEBS Documentation.
Apply via kubectl:
kubectl apply -f assets/manifests/sc-rwx-values.yaml
Verify that the StorageClass was created by executing the command below:
kubectl get sc
The ouput looks similar to:
OutputNAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
do-block-storage (default) dobs.csi.digitalocean.com Delete Immediate true 107m
openebs-kernel-nfs openebs.io/nfsrwx Delete Immediate false 84m
rwx-storage openebs.io/nfsrwx Delete Immediate false 84m
Now, you have a new StorageClass named rwx-storage to dynamically provision shared volumes on top of DigitalOcean Block Storage.
In this section, you will create a dedicated MySQL database such as DigitalOcean’s Managed Databases for WordPress. This is necessary because your WordPress installation will live on a separate server inside the Kubernetes cluster.
If you don’t want to use an external database, please skip to the next chapter - Configuring the Redis Database.
First, create the MySQL managed database:
doctl databases create wordpress-mysql --engine mysql --region nyc1 --num-nodes 2 --size db-s-2vcpu-4gb
The example from this tutorial is using one master node and one slave node, 2cpu/4gb ($100 monthly billing
). For a list of available sizes, visit: https://docs.digitalocean.com/reference/api/api-reference/#tag/Databases.
The output looks similar to the following (the STATE
column should display online
):
OutputID Name Engine Version Number of Nodes Region Status Size
2f0d0969-a8e1-4f94-8b73-2d43c68f8e72 wordpress-mysql-test mysql 8 1 nyc1 online db-s-1vcpu-1gb
To finish setting up MySQL, the database ID is required. You can run below command, to print your MySQL database ID:
doctl databases list
Next, create the WordPress database user:
doctl databases user create 2f0d0969-a8e1-4f94-8b73-2d43c68f8e72 wordpress_user
The output looks similar to the following (the password will be generated automatically):
OutputName Role Password
wordpress_user normal *******
By default, new users get full permissions for the entire database. In general, it’s best security practice to restrict new user privileges to the wordpress database only. You can follow the How to Modify User Privileges in MySQL Databases guide provided by DigitalOcean to accomplish this task.
Next, create the main WordPress database:
doctl databases db create 2f0d0969-a8e1-4f94-8b73-2d43c68f8e72 wordpress
The output looks similar to the following (the password will be generated automatically):
OutputName
wordpress
Finally, you need to setup the trusted sources between your MySQL database and your Kubernetes Cluster (DOKS):
doctl kubernetes cluster list
The output looks similar to the following:
OutputID Name Region Version Auto Upgrade Status Node Pools
c278b4a3-19f0-4de6-b1b2-6d90d94faa3b k8s-cluster nyc1 1.21.10-do.0 false running basic
doctl databases firewalls append 2f0d0969-a8e1-4f94-8b73-2d43c68f8e72 --rule k8s:c278b4a3-19f0-4de6-b1b2-6d90d94faa3b
Note:
Please visit How to Secure MySQL Managed Database Clusters for more details.
Remote Dictionary Server (Redis) is an in-memory, persistent, key-value database, also known as a data structure server. Redis’s caching mechanism, when combined with MySQL or MariaDB, speeds up WordPress database queries. Redis allows you to cache and store data in memory for high-performance data retrieval and storage. With Redis, it’s possible to store data processed by a MySQL database query inside a Redis cache instance for fast retrieval.
Installing and configuring a Redis instance can be done in two ways. Using a DigitalOcean’s Managed Databases for Redis or installing it via a helm chart. Both options will be explored below.
In this section, you will create a Redis Database using DigitalOcean. If you do not want to use a managed database, please skip to the next section - Configuring the Redis helm chart
Before deciding on using a managed database vs the helm-installed one, you should consider the following aspects: With a managed database service, you only need to decide on the initial size of the database server, and you are good to go. Another appeal is the automation side. Performing updates, running migrations, and creating backups are done automatically. Please see this article for more information on managed databases. Using a managed database comes at an extra cost. With the Redis Helm chart installation, it is important to note that DB pods (the database application containers) are transient, so they can restart or fail more often. Specific administrative tasks like backups or scaling require more manual work and setup to achieve those goals. Using the Redis install will not create any additional costs.
First, create the Redis Managed database:
doctl databases create wordpress-redis --engine redis --region nyc1 --num-nodes 1 --size db-s-1vcpu-1gb
The example from this tutorial is using one node, 1cpu/1gb ($10 monthly billing). For a list of available sizes, visit this API doc.
The output looks similar to the following (the STATE
column should display online
):
OutputID Name Engine Version Number of Nodes Region Status Size
91180998-7fe2-450c-b353-492d8abcddad wordpress-redis redis 6 1 nyc1 creating db-s-1vcpu-1gb
Next, you need to set the trusted sources between your Redis database and your Kubernetes Cluster (DOKS):
doctl kubernetes cluster list
The output looks similar to the following:
OutputID Name Region Version Auto Upgrade Status Node Pools
c278b4a3-19f0-4de6-b1b2-6d90d94faa3b k8s-cluster nyc1 1.21.10-do.0 false running basic
doctl databases firewalls append 2f0d0969-a8e1-4f94-8b73-2d43c68f8e72 --rule k8s:c278b4a3-19f0-4de6-b1b2-6d90d94faa3b
Please visit How to Secure Redis Managed Database Clusters for more details.
In this section, you will create a Redis database in your Kubernetes cluster using the Bitnami Redis Helm Chart.
First, add the Helm
repo, and list the available charts
:
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update bitnami
Next, open and inspect the assets/manifests/redis-values.yaml
file provided in the repository:
master:
persistence:
enabled: true
storageClass: rwx-storage
accessModes: ["ReadWriteMany"]
size: 5Gi
auth:
enabled: true
password: <YOUR_REDIS_PASSWORD_HERE>
architecture: standalone
Explanations for the above configuration:
master.persistance
block - enables persistence on Redis master node using PVC and sets PV storage class to the one created earlierauth
block - enables and sets password authentication for a password set by the userarchitecture
- Redis architecture. A standalone Redis StatefulSet. The Redis Master service points to the master where read-write operations can be performed.Note: Most of the overrides can be customized. Please visit redis helm values for more details.
Finally, install the chart using Helm:
helm upgrade redis bitnami/redis \
--atomic \
--create-namespace \
--install \
--namespace redis \
--version 17.0.5 \
--values assets/manifests/redis-values.yaml
A specific version of the Redis Helm
chart is used. In this case, 17.0.5
was picked, which maps to the 7.0.4
release of Redis. It’s good practice in general, to lock on a specific version. This helps to have predictable results and allows versioning control via Git.
Check Helm release status:
helm ls -n redis
The output looks similar to (notice the STATUS
column which has the deployed
value):
OutputNAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
redis redis 1 2022-06-02 08:45:38.617726 +0300 EEST deployed redis-17.0.5 7.0.4
Verify if Redis is up and running:
kubectl get all -n redis
The output looks similar to (all redis
pods should be UP and RUNNING):
OutputNAME READY STATUS RESTARTS AGE
pod/redis-master-0 1/1 Running 0 2m24s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/redis-headless ClusterIP None <none> 6379/TCP 2m25s
service/redis-master ClusterIP 10.245.14.50 <none> 6379/TCP 2m25s
NAME READY AGE
statefulset.apps/redis-master 1/1 2m26s
In this section, you will install WordPress in your Kubernetes cluster using the Bitnami WordPress Helm Chart.
The most important Helm chart values are:
externalDatabase
- configures WordPress to use an external database (such as a DO managed MySQL database).mariadb.enabled
- configures WordPress to use an in-cluster database (e.g. MariaDB).First, add the Helm
repo, and list the available charts
:
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update bitnami
Next, open and inspect the assets/manifests/wordpress-values.yaml
file provided in the repository:
# WordPress service type
service:
type: ClusterIP
# Enable persistence using Persistent Volume Claims
persistence:
enabled: true
storageClassName: rwx-storage
accessModes: ["ReadWriteMany"]
size: 5Gi
volumePermissions:
enabled: true
# Prometheus Exporter / Metrics configuration
metrics:
enabled: false
# Level of auto-updates to allow. Allowed values: major, minor or none.
wordpressAutoUpdateLevel: minor
# Scheme to use to generate WordPress URLs
wordpressScheme: https
# WordPress credentials
wordpressUsername: <YOUR_WORDPRESS_USER_NAME_HERE>
wordpressPassword: <YOUR_WORDPRESS_USER_PASSSWORD_HERE>
# External Database details
externalDatabase:
host: <YOUR_WORDPRESS_MYSQL_DB_HOST_HERE>
port: 25060
user: <YOUR_WORDPRESS_MYSQL_DB_USER_NAME_HERE>
password: <YOUR_WORDPRESS_MYSQL_DB_USER_PASSWORD_HERE>
database: <YOUR_WORDPRESS_MYSQL_DB_NAME_HERE>
# Disabling MariaDB
mariadb:
enabled: false
wordpressExtraConfigContent: |
define( 'WP_REDIS_SCHEME', '<REDIS_SCHEME>' );
define( 'WP_REDIS_HOST', '<REDIS_HOST>' );
define( 'WP_REDIS_PORT', <REDIS_PORT> );
define( 'WP_REDIS_PASSWORD', '<REDIS_PASSWORD>');
define( 'WP_REDIS_DATABASE', 0 );
Most of the overrides can be customized. Please visit wordpress helm values for more details.
The WP_REDIS_SCHEME
parameter needs to be set to tls
when using a managed Redis DO database and tcp
when using a helm installed Redis database.
The WP_REDIS_HOST
parameter value for a helm installed Redis database can be obtained with the following command:
kubectl exec -i -t <REDIS_POD> --namespace redis -- hostname -i
Finally, install the chart using Helm:
helm upgrade wordpress bitnami/wordpress \
--atomic \
--create-namespace \
--install \
--namespace wordpress \
--version 15.0.11 \
--values assets/manifests/wordpress-values.yaml
A specific version for the wordpress Helm
chart is used. In this case 15.0.11
was picked, which maps to the 6.0.1
release of WordPress. It’s good practice in general, to lock on a specific version. This helps to have predictable results, and allows versioning control via Git.
Check Helm release status:
helm ls -n wordpress
The output looks similar to (notice the STATUS
column which has the deployed
value):
OutputNAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
wordpress wordpress 1 2022-03-22 14:22:18.146474 +0200 EET deployed wordpress-15.0.11 6.0.1
Verify if WordPress is up and running:
kubectl get all -n wordpress
The output looks similar to (all wordpress
pods should be UP and RUNNING):
OutputNAME READY STATUS RESTARTS AGE
pod/wordpress-6f55c9ffbd-4frrh 1/1 Running 0 23h
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/wordpress ClusterIP 10.245.36.237 <none> 80/TCP,443/TCP 23h
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/wordpress 1/1 1 1 23h
NAME DESIRED CURRENT READY AGE
replicaset.apps/wordpress-6f55c9ffbd 1 1 1 23h
Verify the PVCs created under the wordpress namespace, and associated OpenEBS volume under the openebs namespace:
kubectl get pvc -A
The output looks similar to (notice the RWX
access mode for the wordpress PVC, and the new storage class defined earlier via the openEBS NFS provisioner):
OutputNAMESPACE NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
openebs nfs-pvc-2b898be6-19f4-4e52-ab9b-10e73ce7d82f Bound pvc-b253c0eb-b02b-46a6-ae88-9a7dd2b71377 5Gi RWO do-block-storage 10m
openebs nfs-pvc-4ce1c2a8-ee65-420f-a722-50f4e50c60a7 Bound pvc-2f2c9dd8-807d-4919-aac1-ab1af69e24c7 5Gi RWO do-block-storage 3m22s
redis redis-data-redis-master-0 Bound pvc-2b898be6-19f4-4e52-ab9b-10e73ce7d82f 5Gi RWX rwx-storage 10m
wordpress wordpress Bound pvc-4ce1c2a8-ee65-420f-a722-50f4e50c60a7 5Gi RWX rwx-storage 3m22s
Verify the associated PVs created in the cluster:
kubectl get pv
The output looks similar to:
OutputNAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pvc-2b898be6-19f4-4e52-ab9b-10e73ce7d82f 5Gi RWX Delete Bound redis/redis-data-redis-master-0 rwx-storage 12m
pvc-2f2c9dd8-807d-4919-aac1-ab1af69e24c7 5Gi RWO Delete Bound openebs/nfs-pvc-4ce1c2a8-ee65-420f-a722-50f4e50c60a7 do-block-storage 4m48s
pvc-4ce1c2a8-ee65-420f-a722-50f4e50c60a7 5Gi RWX Delete Bound wordpress/wordpress rwx-storage 4m48s
pvc-b253c0eb-b02b-46a6-ae88-9a7dd2b71377 5Gi RWO Delete Bound openebs/nfs-pvc-2b898be6-19f4-4e52-ab9b-10e73ce7d82f do-block-storage 12m
You can also create additional pods to demonstrate the capabilities of the NFS provisioner by opening the (WordPress-values.yaml)
file and adding the replicant
line set to the number of desired replicas.
...
replicaCount: 3
...
Apply changes using the helm upgrade command:
helm upgrade wordpress bitnami/wordpress \
--atomic \
--create-namespace \
--install \
--namespace wordpress \
--version 15.0.11 \
--values assets/manifests/wordpress-values.yaml
Verify that the changes are applied. Notice the increased replica count and number of pods:
kubectl get all -n wordpress
The output looks similar to:
OutputNAME READY STATUS RESTARTS AGE
pod/wordpress-5f5f4cf94c-d7mqb 1/1 Running 0 2m58s
pod/wordpress-5f5f4cf94c-qkxdq 1/1 Running 0 3m38s
pod/wordpress-5f5f4cf94c-zf46h 1/1 Running 0 87s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/wordpress ClusterIP 10.245.151.58 <none> 80/TCP,443/TCP 35m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/wordpress 3/3 3 3 35m
NAME DESIRED CURRENT READY AGE
replicaset.apps/wordpress-5f5f4cf94c 3 3 3 35m
replicaset.apps/wordpress-798789f994 0 0 0 19m
We can also check where the pods are deployed:
kubectl get all -n wordpress -o wide
The output looks similar to (notice that the pods are deployed on different nodes):
OutputNAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod/wordpress-5f5f4cf94c-d7mqb 1/1 Running 0 4m7s 10.244.0.206 basicnp-cwxop <none> <none>
pod/wordpress-5f5f4cf94c-qkxdq 1/1 Running 0 4m47s 10.244.1.84 basicnp-cwxol <none> <none>
pod/wordpress-5f5f4cf94c-zf46h 1/1 Running 0 2m36s 10.244.0.194 basicnp-cwxop <none> <none>
The Bitnami WordPress Helm chart comes with built-in support for Ingress routes and certificate management through cert-manager. This makes it easy to configure TLS support using certificates from a variety of certificate providers, including Let’s Encrypt.
First, add the Helm repo, and list the available charts:
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update ingress-nginx
Next, install the Nginx Ingress Controller using Helm:
helm install ingress-nginx ingress-nginx/ingress-nginx --version 4.1.3 \
--namespace ingress-nginx \
--create-namespace
Next, check if the Helm installation was successful by running the command below:
helm ls -n ingress-nginx
The output looks similar to the following (notice the STATUS
column which has the deployed
value):
OutputNAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
ingress-nginx ingress-nginx 1 2022-02-14 12:04:06.670028 +0200 EET deployed ingress-nginx-4.1.3 1.2.1
Finally, list all load balancer resources from your DigitalOcean
account, and print the IP
, ID
, Name
and Status
:
Outputdoctl compute load-balancer list --format IP,ID,Name,Status
The output looks similar to (should contain the new load balancer
resource created for Nginx Ingress Controller
in a healthy state):
OutputIP ID Name Status
45.55.107.209 0471a318-a98d-49e3-aaa1-ccd855831447 acdc25c5cfd404fd68cd103be95af8ae active
In this step, you will configure DNS
within your DigitalOcean
account using a domain
that you own. Then, you will create the domain A
record for WordPress.
First, please issue the below command to create a new domain
(bond-0.co
, in this example):
doctl compute domain create bond-0.co
Note: YOU NEED TO ENSURE THAT YOUR DOMAIN REGISTRAR IS CONFIGURED TO POINT TO DIGITALOCEAN NAME SERVERS. More information on how to do that is available here.
Next, you will add the required A
record for the WordPress application. First, you need to identify the load balancer external IP
created by the nginx
deployment:
Next, you will add the required A
records for the hosts
you created earlier. First, you need to identify the load balancer external IP
created by the nginx
deployment:
kubectl get svc -n ingress-nginx
The output looks similar to (notice the EXTERNAL-IP
column value for the ingress-nginx-controller
service):
OutputNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller LoadBalancer 10.245.109.87 45.55.107.209 80:32667/TCP,443:31663/TCP 25h
ingress-nginx-controller-admission ClusterIP 10.245.90.207 <none> 443/TCP 25h
Then, add the records (please replace the <>
placeholders accordingly). You can change the TTL
value as per your requirement:
doctl compute domain records create bond-0.co --record-type "A" --record-name "wordpress" --record-data "<YOUR_LB_IP_ADDRESS>" --record-ttl "30"
Hint:
If you have only one load balancer
in your account, then please use the following snippet:
LOAD_BALANCER_IP=$(doctl compute load-balancer list --format IP --no-header)
doctl compute domain records create bond-0.co --record-type "A" --record-name "wordpress" --record-data "$LOAD_BALANCER_IP" --record-ttl "30"
Observation and results:
List the available records for the bond-0.co
domain:
doctl compute domain records list bond-0.co
The output looks similar to the following:
OutputID Type Name Data Priority Port TTL Weight
311452740 SOA @ 1800 0 0 1800 0
311452742 NS @ ns1.digitalocean.com 0 0 1800 0
311452743 NS @ ns2.digitalocean.com 0 0 1800 0
311452744 NS @ ns3.digitalocean.com 0 0 1800 0
311453305 A wordpress 45.55.107.209 0 0 30 0
First, add the jetstack
Helm repo and list the available charts:
helm repo add jetstack https://charts.jetstack.io
helm repo update jetstack
Next, install Cert-Manager using Helm:
helm install cert-manager jetstack/cert-manager --version 1.8.0 \
--namespace cert-manager \
--create-namespace \
--set installCRDs=true
Finally, check if Cert-Manager installation was successful by running below command:
helm ls -n cert-manager
The output looks similar to (STATUS
column should print deployed
):
OutputNAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
cert-manager cert-manager 1 2021-10-20 12:13:05.124264 +0300 EEST deployed cert-manager-v1.8.0 v1.8.0
Notes:
A cluster issuer is required first in order to obtain the final TLS certificate. Open and inspect the assets/manifests/letsencrypt-issuer-values-values.yaml
file provided in the repository:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
namespace: wordpress
spec:
acme:
# You must replace this email address with your own.
# Let's Encrypt will use this to contact you about expiring
# certificates, and issues related to your account.
email: <YOUR-EMAIL-HERE>
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
# Secret resource used to store the account's private key.
name: prod-issuer-account-key
# Add a single challenge solver, HTTP01 using nginx
solvers:
- http01:
ingress:
class: nginx
Apply via kubectl
:
kubectl apply -f assets/manifests/letsencrypt-issuer-values.yaml
To secure WordPress traffic, open the helm (wordpress-values.yaml)
file created earlier and add the following settings at the end:
# Enable ingress record generation for WordPress
ingress:
enabled: true
certManager: true
tls: false
hostname: <YOUR_WORDPRESS_DOMAIN_HERE>
annotations:
kubernetes.io/ingress.class: "nginx"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
extraTls:
- hosts:
- <YOUR_WORDPRESS_DOMAIN_HERE>
secretName: wordpress.local-tls
Upgrade via helm
:
helm upgrade wordpress bitnami/wordpress \
--create-namespace \
--namespace wordpress \
--version 15.0.11 \
--timeout 10m0s \
--values assets/manifests/wordpress-values.yaml
This automatically creates a certificate through cert-manager. You can then verify that you’ve successfully obtained the certificate by running the following command:
kubectl get certificate -n wordpress wordpress.local-tls
If successful, the output’s READY column reads True:
OutputNAME READY SECRET AGE
wordpress.local-tls True wordpress.local-tls 24h
Now, you can access WordPress using the domain configured earlier. You will be guided through the installation
process.
In this section, you will learn how to enable metrics for monitoring your WordPress instance.
First, open the wordpress-values.yaml
created earlier in this tutorial, and set metrics.enabled
field to true
:
# Prometheus Exporter / Metrics configuration
metrics:
enabled: true
Apply changes using Helm:
helm upgrade wordpress bitnami/wordpress \
--create-namespace \
--namespace wordpress \
--version 15.0.11 \
--timeout 10m0s \
--values assets/manifests/wordpress-values.yaml
Next, port-forward the wordpress service to inspect the available metrics:
kubectl port-forward --namespace wordpress svc/wordpress-metrics 9150:9150
Now, open a web browser and navigate to localhost:9150/metrics, to see all WordPress metrics.
Finally, you need to configure Grafana and Prometheus to visualize metrics exposed by your new WordPress instance. Please visit How to Install the Prometheus Monitoring Stack to learn more about how to install and configure Grafana and Prometheus.
Plugins are the building blocks of your WordPress site. They bring in important functions to your website, whether you need to add contact forms, improve SEO, increase site speed, create an online store, or offer email opt-ins. Whatever you need your website to do can be done with a plugin.
Below, you can find a list of recommended plugins:
Contact Form by WPForms: allows you to create beautiful contact forms, feedback form, subscription forms, payment forms, and other types of forms for your site.
MonsterInsights: is the best Google Analytics plugin for WordPress. It allows you to “properly” connect your website with Google Analytics, so you can see exactly how people find and use your website.
All in One SEO: helps you get more visitors from search engines to your website. While WordPress is SEO friendly out of the box, there is so much more you can do to increase your website traffic using SEO best practices.
SeedProd: is the best drag and drop page builder for WordPress. It allows you to easily customize your website design and create custom page layouts without writing any code.
LiteSpeed Cache: is an all-in-one site acceleration plugin, featuring an exclusive server-level cache and a collection of optimization feature
UpdraftPlus: simplifies backups and restoration. Backup your files and database backups into the cloud and restore with a single click.
Query Monitor: developer tools panel for WordPress. It enables the debugging of database queries, PHP errors, hooks, and actions.
Please visit https://wordpress.org/plugins/ for more plugins.
A CDN or a Content Delivery Network is a simple way to speed up a WordPress website. A CDN is a server setup that enhances the load speed of web pages by optimizing the delivery requests of the media files. Most sites face latency when their visitors are far from the server location. Using a CDN can speed up content delivery by offloading your web server when serving static content such as images, CSS, JavaScript, and video streams. Another benefit of caching static content is minimum latency. CDN is a great and reliable solution to optimize your website and improve the user experience globally.
NitroPack is a plugin for optimizing the speed and performance of your website.
Next, you will configure the Nitropack plugin for your WordPress instance.
Note:
The administrator password configured via the Wordpress Helm chart values file
(wordpress-values.yaml) fails when attempting to log into the Wordpress administrator console. To change the password, you need to connect to the database and reset it. First, if you’re not familiar with DigitalOcean managed databases, please read the How to Connect to a MySQL Database Cluster guide. Then, follow the Resetting your Wordpress User Password article from the Wordpress support website.
Please follow the below steps to configure the NitroPack plugin for your WordPress instance:
https://<YOUR_WORDPRESS_DOMAIN_HERE>/wp-admin
When asked, please log in with your WordPress admin credentials.
Plugins
menu item, and then open the Add New
sub-menu.Nitropack
plugin, and from the results page, click on the Install Now
button. After the installation is complete, click on the Activate
button. You should see the plugin added to your list of plugins.Settings
link under the plugin name. On the following page, click on the Connect to NitroPack button
. Next, you will be redirected to log in or create a new account with NitroPack.Next, please follow the below steps to connect your site with NitroPack:
Add new website
menu item, then fill the Website URL
, and Website name
with your data. Now, click on the Free subscription
option and then the Proceed
button.Cloudflare
account with the NitroPack
account.Dashboard
with cache information for your WordPress installation.Note:
There is a known issue where you will see this message after installing Nitropack
: Could not turn on the WP_CACHE constant in wp-config.php.
. This is due to restrictive permissions on the wp-config.php
file. To fix this issue, you will need to SSH into the WordPress container with kubectl using this:
kubectl exec --stdin --tty <your_wordpress_pod> -n wordpress -- /bin/bash
Navigate to /bitnami/wordpress
inside the container and run chmod 0644 wp-config.php
to change the permissions. Restarting the plugin home page should result in that error being fixed.
You can also check this article on how to check if NitroPack is serving optimized pages to visitors.
Cloudflare is a company that provides content delivery network (CDN), DNS, DDoS protection, and security services. Cloudflare is a good solution to speed up and enhance the security of your WordPress site.
Note: A Cloudflare account is required for this configuration. If you don’t have one, please visit the Cloudflare website and sign up for a free account. If the Wordpress instalation was configured with a domain purchased from another registrar (e.g. GoDaddy) you will need to change it’s custom nameservers to point to Cloudflare nameservers.
Please follow the below steps to configure Cloudflare to work with your WordPress site:
+ Add Site
.Add Site
button.Get Started
button under the Free
plan.Review DNS records
page, click on the Add record button
and add a A
record.IPv4 address
entered is the address of the DigitalOcean load balancer and on the Continue
button.administrator
account and change the custom nameservers.Done, check nameservers
button.Skip recommandations
link.An email will be sent out when the site is active on Cloudflare.
From your Cloudflare account, you can check out the Analytics
page for more information about the Web traffic on your WordPress site.
Processing nameserver updates can take up to 24 hours to complete.
WordPress does many MySQL query lookups, and Redis Object Cache will optimize WordPress database usage. Redis object can be used to store the cache of request outputs for a particular query sent to the MySQL server.
Next, you will configure the Redis Object Cache plugin for your WordPress instance.
Please follow the below steps to configure Redis Object Cache to work with your WordPress site:
https://<YOUR_WORDPRESS_DOMAIN_HERE>/wp-admin
When asked, please log in with your WordPress admin credentials.
Plugins
menu item, and then open the Add New
sub-menu.Redis Object Cache
plugin, and from the results page, click on the Install Now
button. After the installation is complete, click on the Activate
button. You should see the plugin overview page.Enable Object Cache
button. The plugin should be able to connect to the Redis Cluster and display a status of Connected
.Being so popular, WordPress often becomes a target for malicious exploitation, so it’s important to keep it up to date. You can upgrade WordPress via the helm upgrade
command.
First, update the helm repository:
helm repo update
Next, upgrade WordPress to the new version:
helm upgrade wordpress bitnami/wordpress \
--atomic \
--create-namespace \
--install \
--namespace wordpress \
--version <WORDPRESS_NEW_VERSION> \
--timeout 10m0s \
--values assets/manifests/wordpress-values.yaml
Replace WORDPRESS_NEW_VERSION
with the new version.
In this guide, you learned how to install WordPress the Kubernetes way, using Helm and an external MySQL database. You also learned how to upgrade WordPress to a new version and how to roll back to a previous release in case of errors.
If you want to learn more about Kubernetes and Helm, please check out the DO Kubernetes section of our community page.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
Sign up for Infrastructure as a Newsletter.
Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.
Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.
Hi … I am getting the following (1xwarning+ 1x) error when trying to elm upgrade
W0309 17:02:09.403436 29108 warnings.go:70] annotation “kubernetes.io/ingress.class” is deprecated, please use ‘spec.ingressClassName’ instead Error: UPGRADE FAILED: failed to create resource: Internal error occurred: failed calling webhook “validate.nginx.ingress.kubernetes.io”: failed to call webhook: Post “https://ingress-nginx-controller-admission.ingress-nginx.svc:443/networking/v1/ingresses?timeout=10s”: http: server gave HTTP response to HTTPS client
Not sure how to troubleshoot this.
Can you confirm if in the last line of the wordpress-values.yaml file I was not sure if the secretName should be edited to be the hostane running wp or kept as wordpress.local-tls? ( I tried both and still getting the same error ) secretName: wordpress.