Top Tags

MariaDB Cluster with WordPress on MicroK8s with Helm

How to deploy MariaDB Cluster write primary and secondary replicas with WordPress on MicroK8s wit Helm

Overview

This guide walks you through deploying a MariaDB cluster with primary-secondary replication architecture alongside WordPress on MicroK8s using Helm charts. The setup demonstrates how to leverage Kubernetes for database high availability and horizontal read scaling.

Architecture Explained

The replication architecture consists of:

  • Primary Node: Handles all write operations (INSERT, UPDATE, DELETE) and propagates changes to secondaries
  • Secondary Nodes (Replicas): Receive replicated data from the primary and can serve read queries, improving read performance
  • WordPress Frontend: Connects to the primary MariaDB node for database operations

This topology is ideal for read-heavy workloads where you can distribute SELECT queries across multiple replicas while maintaining a single source of truth for writes.

Prerequisites

Before starting, ensure you have the following components installed and configured:

ComponentMinimum VersionPurpose
MicroK8s1.28+Kubernetes distribution
Helm3.12+Package manager for Kubernetes
kubectl1.28+Kubernetes CLI tool
Storage Class-For persistent volume provisioning

Enable required MicroK8s addons:

bash
1microk8s enable dns storage helm3 metallb

Understanding MariaDB Replication

MariaDB replication uses a binary log (binlog) based mechanism where:

  1. The primary server records all data modifications in its binary log
  2. Secondary servers connect to the primary and read the binary log
  3. Each secondary applies the logged events to maintain data consistency

Key replication parameters in the configuration:

  • replicationPassword: Credentials for the replication user that secondaries use to connect to the primary
  • replicaCount: Number of secondary read replicas to deploy

MariaDB Cluster Configuration

Values for MariaDB Cluster

Create the MariaDB Helm values file that defines the replication topology:

bash
1nano mariadb.yaml
yaml
1architecture: "replication"
2auth:
3 user: "user"
4 rootPassword: "123"
5 password: "123"
6 replicationPassword: "123"
7mariadb:
8secondary:
9 replicaCount: 4
10tls:
11 enabled: true
12networkPolicy:
13 enabled: false

Configuration Parameters Breakdown

ParameterValueDescription
architecturereplicationEnables primary-secondary replication mode (alternatives: standalone)
auth.rootPassword-Root user password for administrative access
auth.replicationPassword-Password for the replication user connecting secondaries to primary
secondary.replicaCount4Number of read replicas to create
tls.enabledtrueEnables TLS encryption for client connections
networkPolicy.enabledfalseDisables Kubernetes NetworkPolicy (enable in production)

WordPress Configuration

Values for WordPress

The WordPress deployment uses a Kubernetes manifest that defines the Service, PersistentVolumeClaim, and Deployment resources:

bash
1nano wordpress.yaml
yaml
1apiVersion: v1
2kind: Service
3metadata:
4 name: wordpress
5 labels:
6 app: wordpress
7spec:
8 ports:
9 - port: 80
10 selector:
11 app: wordpress
12 tier: frontend
13 type: LoadBalancer
14---
15apiVersion: v1
16kind: PersistentVolumeClaim
17metadata:
18 name: wp-pv-claim
19 labels:
20 app: wordpress
21spec:
22 accessModes:
23 - ReadWriteOnce
24 resources:
25 requests:
26 storage: 20Gi
27---
28apiVersion: apps/v1
29kind: Deployment
30metadata:
31 name: wordpress
32 labels:
33 app: wordpress
34spec:
35 selector:
36 matchLabels:
37 app: wordpress
38 tier: frontend
39 strategy:
40 type: Recreate
41 template:
42 metadata:
43 labels:
44 app: wordpress
45 tier: frontend
46 spec:
47 containers:
48 - image: wordpress:6.2.1-apache
49 name: wordpress
50 env:
51 - name: WORDPRESS_DB_HOST
52 value: wordpress-mariadb
53 - name: WORDPRESS_DB_PASSWORD
54 valueFrom:
55 secretKeyRef:
56 name: mysql-pass
57 key: password
58 - name: WORDPRESS_DB_USER
59 value: wordpress
60 ports:
61 - containerPort: 80
62 name: wordpress
63 volumeMounts:
64 - name: wordpress-persistent-storage
65 mountPath: /var/www/html
66 volumes:
67 - name: wordpress-persistent-storage
68 persistentVolumeClaim:
69 claimName: wp-pv-claim

WordPress Manifest Components

Service (LoadBalancer): Exposes WordPress externally on port 80, automatically obtaining an external IP from MetalLB or your cloud provider's load balancer.

PersistentVolumeClaim: Requests 20Gi of persistent storage for WordPress files, themes, and plugins. Uses ReadWriteOnce access mode suitable for single-pod deployments.

Deployment Strategy: Uses Recreate strategy which terminates the old pod before creating a new one during updates. This prevents multiple pods accessing the same PersistentVolume simultaneously.

Environment Variables:

  • WORDPRESS_DB_HOST: Points to the MariaDB primary service
  • WORDPRESS_DB_PASSWORD: Securely retrieved from a Kubernetes Secret
  • WORDPRESS_DB_USER: Database username for WordPress

Deployment

Deploy it

Add the Bitnami Helm repository and deploy both MariaDB and WordPress:

bash
1helm repo add bitnami https://charts.bitnami.com/bitnami
2
3microk8s helm install maria oci://registry-1.docker.io/bitnamicharts/mariadb -f mariadb.yaml
4microk8s helm install wordpress oci://registry-1.docker.io/bitnamicharts/wordpress -f wordpress.yaml

Create the Database Password Secret

Before the WordPress deployment can start, create the Secret containing the database password:

bash
1kubectl create secret generic mysql-pass --from-literal=password='123'

Verify Deployment Status

Check the status of all deployed resources:

bash
1# Check MariaDB pods (should show 1 primary + 4 secondaries)
2kubectl get pods -l app.kubernetes.io/name=mariadb
3
4# Check WordPress pod
5kubectl get pods -l app=wordpress
6
7# View services and their external IPs
8kubectl get svc
9
10# Check persistent volume claims
11kubectl get pvc

Expected output for MariaDB pods:

plaintext
1NAME READY STATUS RESTARTS AGE
2maria-mariadb-primary-0 1/1 Running 0 5m
3maria-mariadb-secondary-0 1/1 Running 0 5m
4maria-mariadb-secondary-1 1/1 Running 0 4m
5maria-mariadb-secondary-2 1/1 Running 0 3m
6maria-mariadb-secondary-3 1/1 Running 0 2m

Verifying Replication

Connect to Primary and Check Replication Status

Connect to the MariaDB primary pod and verify replication is working:

bash
1# Get a shell into the primary pod
2kubectl exec -it maria-mariadb-primary-0 -- bash
3
4# Connect to MariaDB as root
5mysql -u root -p
6
7# Check replication status on primary
8SHOW MASTER STATUS\G
9
10# View connected replicas
11SHOW SLAVE HOSTS;

Test Replication

Create a test database and verify it replicates to secondaries:

bash
1# On the primary
2CREATE DATABASE test_replication;
3USE test_replication;
4CREATE TABLE test_table (id INT PRIMARY KEY, data VARCHAR(100));
5INSERT INTO test_table VALUES (1, 'Hello from primary');

Then connect to a secondary and verify:

bash
1# Connect to a secondary pod
2kubectl exec -it maria-mariadb-secondary-0 -- bash
3
4# Connect and verify data
5mysql -u root -p -e "SELECT * FROM test_replication.test_table;"

Scaling Operations

Scale Read Replicas

Increase or decrease the number of secondary replicas:

bash
1# Scale to 6 replicas
2microk8s helm upgrade maria oci://registry-1.docker.io/bitnamicharts/mariadb \
3 -f mariadb.yaml \
4 --set secondary.replicaCount=6
5
6# Or scale down to 2 replicas
7microk8s helm upgrade maria oci://registry-1.docker.io/bitnamicharts/mariadb \
8 -f mariadb.yaml \
9 --set secondary.replicaCount=2

Monitor Resource Usage

Check resource consumption of MariaDB pods:

bash
1kubectl top pods -l app.kubernetes.io/name=mariadb

Troubleshooting

Common Issues

IssuePossible CauseSolution
Secondary pods in CrashLoopBackOffIncorrect replication passwordVerify replicationPassword matches primary configuration
WordPress can't connect to DBWrong WORDPRESS_DB_HOST valueEnsure it matches the MariaDB service name
PVC stuck in PendingNo StorageClass availableEnable storage addon: microk8s enable storage
Replication lag increasingHigh write load or network issuesCheck pod resources and network latency

View MariaDB Logs

bash
1# Primary logs
2kubectl logs maria-mariadb-primary-0
3
4# Secondary logs
5kubectl logs maria-mariadb-secondary-0
6
7# Follow logs in real-time
8kubectl logs -f maria-mariadb-primary-0

Check Replication Lag

Connect to a secondary and check the replication delay:

bash
1kubectl exec -it maria-mariadb-secondary-0 -- \
2 mysql -u root -p -e "SHOW SLAVE STATUS\G" | grep -E "Seconds_Behind_Master|Slave_IO_Running|Slave_SQL_Running"

Cleanup

Remove all deployed resources:

bash
1# Uninstall Helm releases
2microk8s helm uninstall maria
3microk8s helm uninstall wordpress
4
5# Delete PVCs (WARNING: This deletes all data!)
6kubectl delete pvc --all
7
8# Delete the password secret
9kubectl delete secret mysql-pass

Advanced Configuration Examples

Enable Prometheus Metrics

Add metrics monitoring to your MariaDB cluster for observability:

yaml
1# Additional values for mariadb.yaml
2metrics:
3 enabled: true
4 image:
5 registry: docker.io
6 repository: bitnami/mysqld-exporter
7 serviceMonitor:
8 enabled: true
9 namespace: monitoring
10 labels:
11 release: prometheus
12 resources:
13 limits:
14 cpu: 100m
15 memory: 128Mi
16 requests:
17 cpu: 50m
18 memory: 64Mi

Configure Resource Limits

Set resource requests and limits for production workloads:

yaml
1# Resource configuration for primary
2primary:
3 resources:
4 limits:
5 cpu: "2"
6 memory: 4Gi
7 requests:
8 cpu: "500m"
9 memory: 1Gi
10 persistence:
11 size: 50Gi
12 storageClass: "fast-storage"
13
14# Resource configuration for secondaries
15secondary:
16 resources:
17 limits:
18 cpu: "1"
19 memory: 2Gi
20 requests:
21 cpu: "250m"
22 memory: 512Mi
23 persistence:
24 size: 50Gi

Custom MariaDB Configuration

Override MariaDB server configuration with custom parameters:

yaml
1# Primary node configuration
2primary:
3 configuration: |-
4 [mysqld]
5 skip-name-resolve
6 explicit_defaults_for_timestamp
7 basedir=/opt/bitnami/mariadb
8 datadir=/bitnami/mariadb/data
9 port=3306
10 socket=/opt/bitnami/mariadb/tmp/mysql.sock
11 tmpdir=/opt/bitnami/mariadb/tmp
12 max_allowed_packet=64M
13 bind-address=0.0.0.0
14 pid-file=/opt/bitnami/mariadb/tmp/mysqld.pid
15 log-error=/opt/bitnami/mariadb/logs/mysqld.log
16 character-set-server=UTF8
17 collation-server=utf8_general_ci
18 slow_query_log=1
19 long_query_time=10.0
20 max_connections=200
21 innodb_buffer_pool_size=1G
22 innodb_log_file_size=256M
23 innodb_flush_log_at_trx_commit=2
24
25# Secondary nodes configuration
26secondary:
27 configuration: |-
28 [mysqld]
29 skip-name-resolve
30 explicit_defaults_for_timestamp
31 basedir=/opt/bitnami/mariadb
32 datadir=/bitnami/mariadb/data
33 port=3306
34 socket=/opt/bitnami/mariadb/tmp/mysql.sock
35 tmpdir=/opt/bitnami/mariadb/tmp
36 max_allowed_packet=64M
37 bind-address=0.0.0.0
38 pid-file=/opt/bitnami/mariadb/tmp/mysqld.pid
39 log-error=/opt/bitnami/mariadb/logs/mysqld.log
40 character-set-server=UTF8
41 collation-server=utf8_general_ci
42 read_only=1
43 max_connections=200
44 innodb_buffer_pool_size=512M

Initialize Database with Scripts

Pre-populate your database with initial schema and data:

yaml
1initdbScripts:
2 init-wordpress-db.sql: |
3 -- Create WordPress database and user
4 CREATE DATABASE IF NOT EXISTS wordpress CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
5 CREATE USER IF NOT EXISTS 'wordpress'@'%' IDENTIFIED BY 'your_secure_password';
6 GRANT ALL PRIVILEGES ON wordpress.* TO 'wordpress'@'%';
7 FLUSH PRIVILEGES;
8
9 health-check-script.sh: |
10 #!/bin/sh
11 if [[ $(hostname) == *primary* ]]; then
12 echo "Primary node initialized successfully"
13 mysql -P 3306 -uroot -p${MARIADB_ROOT_PASSWORD} -e "SELECT 1";
14 fi

Production Considerations

Security Hardening

For production deployments, implement the following security measures:

  1. Strong Passwords: Use randomly generated passwords with at least 32 characters
  2. Network Policies: Enable and configure Kubernetes NetworkPolicy to restrict pod-to-pod communication
  3. TLS Encryption: Enable TLS for both client connections and replication traffic
  4. Pod Security Standards: Apply restrictive security contexts
  5. Secrets Management: Use external secrets managers like HashiCorp Vault or AWS Secrets Manager

High Availability Considerations

ConsiderationRecommendation
Pod Anti-AffinityDistribute replicas across different nodes
Pod Disruption BudgetEnsure minimum available replicas during maintenance
Backup StrategyImplement regular automated backups using Velero or mysqldump
Monitoring & AlertingSet up Prometheus alerts for replication lag and resource usage
Failover TestingRegularly test failover procedures

Backup Configuration Example

Configure automated backups using a CronJob:

yaml
1apiVersion: batch/v1
2kind: CronJob
3metadata:
4 name: mariadb-backup
5spec:
6 schedule: "0 2 * * *" # Daily at 2 AM
7 jobTemplate:
8 spec:
9 template:
10 spec:
11 containers:
12 - name: backup
13 image: bitnami/mariadb:latest
14 command:
15 - /bin/sh
16 - -c
17 - |
18 mysqldump -h maria-mariadb-primary \
19 -u root -p${MARIADB_ROOT_PASSWORD} \
20 --all-databases --single-transaction \
21 --routines --triggers \
22 | gzip > /backup/backup-$(date +%Y%m%d-%H%M%S).sql.gz
23 env:
24 - name: MARIADB_ROOT_PASSWORD
25 valueFrom:
26 secretKeyRef:
27 name: maria-mariadb
28 key: mariadb-root-password
29 volumeMounts:
30 - name: backup-volume
31 mountPath: /backup
32 restartPolicy: OnFailure
33 volumes:
34 - name: backup-volume
35 persistentVolumeClaim:
36 claimName: mariadb-backup-pvc

Comparison: Replication vs Galera Cluster

Understanding when to use each architecture:

FeaturePrimary-Secondary ReplicationGalera Cluster
Write NodesSingle (primary only)Multi-master (all nodes)
Read ScalingExcellentGood
Write ScalingLimitedBetter distribution
ConsistencyEventual (asynchronous)Synchronous (strong)
FailoverManual or requires automationAutomatic
Network SensitivityTolerantRequires low latency
ComplexityLowerHigher
Use CaseRead-heavy workloadsWrite-heavy, HA requirements

For WordPress specifically, primary-secondary replication is often preferred due to the read-heavy nature of most WordPress sites (displaying content) versus the relatively low write activity (publishing posts, comments).


References