Migrating my WordPress blog site to Azure Kubernetes Services (AKS)

As Kubernetes becomes more popular, I decided to migrate my blog site from Azure App Services and Azure Database for MySQL to AKS and utilize Azure regions Australia East, West Europe and East US and providing security using Azure Front Door with WAF.

As I used several sites as reference (sites listed below), I decided to document the commands I used to install the site.

Firstly, I install AKS in the desired region via the Azure Portal with mostly default settings except for enabling of Virtual nodes and using a System-assigned managed identity.

After AKS is provisioned, select the Azure Cloud Shell from the Azure Portal and install Helm v3.x.

curl https://raw.githubusercontent.com/kubernetes/helm/master/scripts/get-helm-3 > get_helm.sh
chmod 700 get_helm.sh
./get_helm.sh

Configure kubectl to use the credentials of the new AKS cluster.

az aks get-credentials --name aks-cluster --resource-group aks-resource-group

Here is where we need to deviate from the documentation as the Bitnami instructions will install WordPress without Ingress and a self-signed certificate. This is fine if you want to run WordPress in AKS using an Application Gateway in one region as the App Gateway can connect to the backend using HTTP whilst utilizing HTTPS on the frontend.

As I will be using Azure Front Door to utilize its WAF capabilities and load balance to multiple AKS cluster in different regions, one of the limitations of Azure Front Door is that it does not support self-signed certificates on the backend. It is also a requirement of Front Door to match the HTTP or HTTPS request to the backend. In other words, it is not possible to connect to HTTPS to Front Door and use Front Door to connect via HTTP to the backend.

Therefore, we need to deploy an Ingress nginx controller and Cert-Manager utilizing free Let’s Encrypt certificates.

First, the Ingress-nginx needs to be deployed in AKS.

# Add the ingress-nginx repository
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
# Use Helm to deploy an NGINX ingress controller
helm install nginx-ingress ingress-nginx/ingress-nginx \
    --namespace default \
    --set controller.nodeSelector."beta\.kubernetes\.io/os"=linux \
    --set defaultBackend.nodeSelector."beta\.kubernetes\.io/os"=linux

Use the following command to get the external public IP of the ingress-nginx controller.

kubectl --namespace default get services -o wide -w nginx-ingress-ingress-nginx-controller
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
nginx-ingress-ingress-nginx-controller LoadBalancer 10.0.74.133 EXTERNAL_IP 80:32486/TCP,443:30953/TCP 44s app.kubernetes.io/component=controller,app.kubernetes.io/instance=nginx-ingress,app.kubernetes.io/name=ingress-nginx

This next step is very important for Cert-Manager to automatically request Let’s Encrypt certificates. Let’s Encrypt uses either http01 or dns01 challenge to verify that the domain name is valid.

Create a public DNS hostname for the AKS public IP. This is used by Let’s Encrypt http01 challenge to verify the host name of the certificate being requested.

Next step is to deploy Cert Manager in AKS. As Cert Manager’s ClusterIssuer works for all namespaces, we create a dedicated namespace for it.

# Create a new Cert-Manager namespace
kubectl create namespace cert-manager

# Label the cert-manager namespace to disable resource validation
kubectl label namespace cert-manager cert-manager.io/disable-validation=true 

# Add the Jetstack Helm repository
helm repo add jetstack https://charts.jetstack.io 

# Update your local Helm chart repository cache 
helm repo update 

# Install the cert-manager Helm chart
# Refer to https://cert-manager.io/docs/installation/ for latest version
helm install \
  cert-manager \
  --namespace cert-manager \
  --version v1.7.1 \
  --set installCRDs=true \
  --set nodeSelector."beta\.kubernetes\.io/os"=linux \
  jetstack/cert-manager

Once Cert Manager is deployed, we then need to create a ClusterIssuer to request certificates. Create the following yaml file using command “nano letsencrypt-prod.yaml” in the Azure Cloud Shell and paste the following content.

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    email: <email address to alert for certificate renewals>
    privateKeySecretRef:
      name: letsencrypt-prod
    server: https://acme-v02.api.letsencrypt.org/directory
    solvers:
    - http01:
      ingress:
        class: nginx

Run the following command to create the ClusterIssuer:

kubectl apply -f letsencrypt-prod.yaml

Finally, we are now ready to deploy WordPress.

# Add the Microsoft Azure Marketplace chart repository
helm repo add azure-marketplace https://marketplace.azurecr.io/helm/v1/repo

helm install wordpress azure-marketplace/wordpress \
  --set service.type=ClusterIP \
  --set ingress.enabled=true \
  --set ingress.certManager=true \
  --set ingress.annotations."kubernetes\.io/ingress\.class"=nginx \
  --set ingress.annotations."cert-manager\.io/cluster-issuer"=letsencrypt-prod \
  --set ingress.hostname=DOMAIN \
  --set ingress.extraTls[0].hosts[0]=DOMAIN \
  --set ingress.extraTls[0].secretName=wordpress.local-tls

Once WordPress is deployed, run the following command to ensure that Cert Manager successfully issues the Let’s Encrypt certificate for the WordPress site.

kubectl describe cert

Note that the Ingress-nginx controller uses host headers and you can only browse to the WordPress site using the hostname and NOT the IP address. This also means that the backend pool in Azure Front Door needs to be specified with the DNS name and not the IP address. I have found that specifying the IP address and using the DNS name for the host header will still throw a 503 error as the Front Door health probe will fail.

Reference sites:

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.