Orchestrating High-Availability WordPress Headless Deployments with Kubernetes and AWS EKS
Kubernetes Cluster Setup on AWS EKS
The foundation of our high-availability WordPress headless deployment is a robust Kubernetes cluster. AWS Elastic Kubernetes Service (EKS) provides a managed control plane, simplifying operational overhead. We’ll focus on a production-ready setup, emphasizing node group configuration and networking.
First, ensure you have the AWS CLI and kubectl configured and authenticated. We’ll create a dedicated VPC for our EKS cluster to maintain network isolation. This involves setting up subnets (public and private), route tables, an internet gateway, and NAT gateways for outbound traffic from private subnets.
Next, we provision the EKS cluster itself. For high availability, we’ll utilize managed node groups with multiple worker nodes spread across different Availability Zones. This ensures resilience against single AZ failures. We’ll also configure IAM roles for the EKS service and the worker nodes, granting necessary permissions for EKS to manage resources and for nodes to interact with AWS services like EBS and ELB.
EKS Cluster Creation with `eksctl`
The `eksctl` tool simplifies EKS cluster creation. Here’s a sample configuration file for creating a cluster with two managed node groups, each with three `m5.xlarge` instances, spread across three AZs:
Create a file named eks-cluster.yaml:
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
name: wp-headless-prod
region: us-east-1
version: "1.28"
managedNodeGroups:
- name: worker-nodes-general
instanceType: m5.xlarge
desiredCapacity: 3
minSize: 2
maxSize: 5
volumeSize: 100 # GiB
privateNetworking: true
ssh:
allow: true
publicKeyName: my-ssh-key # Replace with your EC2 key pair name
tags:
nodegroup-role: worker
- name: worker-nodes-gpu # Example for potential ML/AI workloads if needed
instanceType: g4dn.xlarge
desiredCapacity: 1
minSize: 0
maxSize: 2
volumeSize: 100
privateNetworking: true
ssh:
allow: true
publicKeyName: my-ssh-key
tags:
nodegroup-role: gpu
gpu: true # Enable GPU support
# Optional: Enable CloudWatch logging for control plane components
cloudWatch:
clusterLogging:
enableTypes: ["api", "audit", "authenticator", "controllerManager", "scheduler"]
Then, apply this configuration:
eksctl create cluster -f eks-cluster.yaml
This command will provision the EKS control plane, create the necessary IAM roles, and launch the specified managed node groups. It will also update your ~/.kube/config file to point to the new cluster.
Deploying WordPress and Database with Persistent Storage
For a headless WordPress setup, we’ll deploy WordPress itself as a stateless application, relying on an external database. High availability for the database is critical. We’ll use AWS RDS for PostgreSQL or MySQL, leveraging its Multi-AZ deployment feature for automatic failover. For WordPress, we’ll deploy it as a set of stateless pods managed by a Deployment, with persistent storage for uploads handled via Persistent Volumes (PVs) and Persistent Volume Claims (PVCs) backed by AWS Elastic Block Store (EBS).
AWS RDS Configuration
When creating your RDS instance, ensure the following:
- Engine: PostgreSQL or MySQL (choose based on preference and existing expertise).
- Multi-AZ Deployment: Enabled for high availability.
- Storage: Provisioned IOPS (gp3 or io1) for performance, with auto-scaling enabled if needed.
- Network & Security: Place the RDS instance in private subnets within your VPC. Configure Security Groups to allow inbound traffic only from your EKS worker nodes on the database port (e.g., 5432 for PostgreSQL, 3306 for MySQL).
- Parameter Groups: Tune parameters for performance and security as required.
Note down the RDS endpoint, port, username, and password. These will be used as Kubernetes Secrets.
WordPress Deployment and Service
We’ll define a Kubernetes Deployment for WordPress. This deployment will manage stateless WordPress pods. Each pod will mount a Persistent Volume Claim for the /var/www/html/wp-content/uploads directory to persist media uploads.
First, create a Kubernetes Secret to store your database credentials:
apiVersion: v1 kind: Secret metadata: name: wordpress-db-credentials namespace: default # Or your preferred namespace type: Opaque data: # Base64 encoded values DB_HOST: <base64_encoded_rds_endpoint> DB_NAME: <base64_encoded_db_name> DB_USER: <base64_encoded_db_user> DB_PASSWORD: <base64_encoded_db_password> # For PostgreSQL, you might also need: # DB_PORT: "5432" # For MySQL, you might also need: # DB_PORT: "3306"
You can encode values using `echo -n ‘your_value’ | base64`.
Next, create a Persistent Volume Claim (PVC) for uploads. We’ll use the `gp2` or `gp3` storage class, which maps to AWS EBS volumes. Ensure your EKS cluster has the EBS CSI driver installed.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wordpress-uploads-pvc
namespace: default
spec:
accessModes:
- ReadWriteOnce # EBS volumes are typically RWO
resources:
requests:
storage: 50Gi # Adjust size as needed
storageClassName: gp3 # Or gp2, depending on your cluster's configuration
Now, the WordPress Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: wordpress
namespace: default
labels:
app: wordpress
spec:
replicas: 3 # Scale as needed
selector:
matchLabels:
app: wordpress
template:
metadata:
labels:
app: wordpress
spec:
containers:
- name: wordpress
image: wordpress:latest # Consider pinning to a specific version
ports:
- containerPort: 80
env:
- name: WORDPRESS_DB_HOST
valueFrom:
secretKeyRef:
name: wordpress-db-credentials
key: DB_HOST
- name: WORDPRESS_DB_NAME
valueFrom:
secretKeyRef:
name: wordpress-db-credentials
key: DB_NAME
- name: WORDPRESS_DB_USER
valueFrom:
secretKeyRef:
name: wordpress-db-credentials
key: DB_USER
- name: WORDPRESS_DB_PASSWORD
valueFrom:
secretKeyRef:
name: wordpress-db-credentials
key: DB_PASSWORD
# Add DB_PORT if not default and specified in secret
# - name: WORDPRESS_DB_PORT
# valueFrom:
# secretKeyRef:
# name: wordpress-db-credentials
# key: DB_PORT
volumeMounts:
- name: wordpress-uploads
mountPath: /var/www/html/wp-content/uploads
volumes:
- name: wordpress-uploads
persistentVolumeClaim:
claimName: wordpress-uploads-pvc
And a Service to expose WordPress internally within the cluster:
apiVersion: v1
kind: Service
metadata:
name: wordpress-service
namespace: default
spec:
selector:
app: wordpress
ports:
- protocol: TCP
port: 80
targetPort: 80
type: ClusterIP # Internal service, will be exposed externally via Ingress
Apply these manifests:
kubectl apply -f secret.yaml kubectl apply -f pvc.yaml kubectl apply -f deployment.yaml kubectl apply -f service.yaml
Exposing WordPress via AWS Load Balancer and Ingress Controller
To make WordPress accessible from the internet, we’ll use an Ingress controller and an AWS Application Load Balancer (ALB). The ALB will handle SSL termination and distribute traffic to our WordPress pods. We’ll deploy the AWS Load Balancer Controller within our EKS cluster.
Deploying the AWS Load Balancer Controller
The AWS Load Balancer Controller manages AWS Application Load Balancers and Network Load Balancers for Kubernetes services. It requires specific IAM permissions. First, create an IAM policy that grants the controller the necessary permissions. You can find the latest policy JSON from the AWS documentation.
Then, create an IAM OIDC provider for your EKS cluster if you haven’t already. Associate the IAM policy with a Kubernetes Service Account that the controller will use.
Deploy the controller using its Helm chart:
# Add the EKS chart repository helm repo add eks https://aws.github.io/eks-charts helm repo update # Install the AWS Load Balancer Controller helm install aws-load-balancer-controller eks/aws-load-balancer-controller \ -n kube-system \ --set clusterName=wp-headless-prod \ --set serviceAccount.create=false \ --set serviceAccount.name=aws-load-balancer-controller \ --set region=us-east-1 \ --set vpcId=vpc-xxxxxxxxxxxxxxxxx # Replace with your VPC ID
Ensure the aws-load-balancer-controller Service Account has the correct IAM role attached. You can verify this by checking the annotations on the Service Account in the kube-system namespace.
Configuring Ingress
Now, create an Ingress resource to route external traffic to the wordpress-service. This Ingress resource will instruct the AWS Load Balancer Controller to provision an ALB.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: wordpress-ingress
namespace: default
annotations:
# Use the alb.ingress.kubernetes.io/scheme: internet-facing annotation
# for a public-facing ALB. For internal-only, use 'internal'.
alb.ingress.kubernetes.io/scheme: internet-facing
# Optional: Specify a specific ALB class if needed
# alb.ingress.kubernetes.io/group.name: "wordpress-group"
# Optional: Configure SSL termination
# alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS": 443}]'
# alb.ingress.kubernetes.io/certificate-type: "wildcard" # or "issuer"
# alb.ingress.kubernetes.io/actions.ssl-redirect: '{"Type": "redirect", "RedirectConfig": { "Protocol": "HTTPS", "Port": "443", "StatusCode": "HTTP_301"}}'
spec:
ingressClassName: alb # This tells the controller to use ALB
rules:
- host: wp-headless.yourdomain.com # Replace with your domain
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: wordpress-service
port:
number: 80
# Optional: TLS configuration for HTTPS
# tls:
# - hosts:
# - wp-headless.yourdomain.com
# secretName: wp-headless-tls-secret # Kubernetes secret containing your TLS certificate
Apply the Ingress manifest:
kubectl apply -f ingress.yaml
After applying, the AWS Load Balancer Controller will provision an ALB. You can check its status with:
kubectl get ingress wordpress-ingress -n default
The output will show the ALB’s hostname in the ADDRESS column. Update your DNS records to point wp-headless.yourdomain.com to this ALB hostname.
Securing WordPress: WAF and CDN Integration
For production deployments, security is paramount. We’ll integrate AWS WAF with the ALB and consider CloudFront for CDN capabilities.
AWS WAF Integration
AWS WAF can protect your WordPress site from common web exploits. You can associate a WAF Web ACL with your ALB. This is typically done via the AWS Management Console or AWS CLI after the ALB has been provisioned by the Ingress controller.
Steps:
- Create a WAF Web ACL in the AWS console, choosing rules that align with your security needs (e.g., SQL injection, XSS protection, rate limiting).
- Navigate to your ALB in the EC2 console.
- Under the “AWS WAF” tab, associate your newly created Web ACL with the ALB.
This configuration ensures that all traffic hitting the ALB is first inspected by WAF.
AWS CloudFront for CDN
To improve performance and reduce load on your WordPress origin, integrate AWS CloudFront. CloudFront can cache static assets (images, CSS, JS) and even dynamic content based on your caching policies.
Steps:
- Create a CloudFront distribution.
- Set the “Origin Domain Name” to the DNS name of your ALB (the one obtained from
kubectl get ingress). - Configure cache behaviors, including TTLs and allowed HTTP methods.
- For dynamic content, you might need to configure CloudFront to forward specific headers, cookies, and query strings to the origin.
- Update your DNS records to point your domain (e.g.,
wp-headless.yourdomain.com) to the CloudFront distribution’s domain name instead of the ALB’s. - If you’re using CloudFront for SSL, you’ll typically use a certificate managed by AWS Certificate Manager (ACM) and associate it with the CloudFront distribution.
This setup provides a highly available, scalable, and secure WordPress headless deployment on AWS EKS.