Instalasi MariaDB Galera Cluster¶
Status: SUDAH DIIMPLEMENTASIKAN (8 Juli 2026)
Cluster Galera 3-node sudah berjalan sehat di namespace galera-system, backup terjadwal
ke S3 sudah aktif, dan uji failover sudah lulus. Halaman ini didokumentasikan sebagai
catatan implementasi — bukan lagi rencana.
1. Keputusan Teknis Awal¶
| Item | Keputusan |
|---|---|
| Operator | mariadb-operator v26.6.0 (CRD-native: MariaDB, Backup, Restore, User, Database, Grant) |
| Namespace | galera-system |
| Jumlah node | 3 (multi-master, sesuai SEKSI-01) |
| Sizing per node | Request 0,5 vCPU / 1GB — Limit 1 vCPU / 2GB (sesuai rencana resmi) |
| Storage per node | 50Gi, StorageClass khusus longhorn-db-single |
| Image | mariadb:11.8.8 (default operator) |
Kenapa bukan Percona XtraDB Cluster Operator?
Percona XtraDB adalah fork MySQL, bukan MariaDB. Karena keputusan teknologi resmi adalah
MariaDB Galera, dipakai mariadb-operator yang memang dibangun khusus untuk MariaDB.
2. Storage — Hindari Double-Replication¶
Galera sudah melakukan replikasi data sendiri (3 node). Bila memakai StorageClass Longhorn default (replika 3×), total duplikasi menjadi 3×3 = 9× — sangat boros. Dibuat StorageClass khusus dengan replika 1×:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: longhorn-db-single
provisioner: driver.longhorn.io
parameters:
numberOfReplicas: "1"
staleReplicaTimeout: "30"
allowVolumeExpansion: true
3. Install mariadb-operator¶
helm repo add mariadb-operator https://mariadb-operator.github.io/mariadb-operator
helm repo update
helm install mariadb-operator mariadb-operator/mariadb-operator -n mariadb-operator --create-namespace
Gotcha: CRD tidak ikut terpasang otomatis
Chart utama tidak menyertakan CRD secara default (crds.enabled: false) — operator akan
CrashLoopBackOff dengan error "no matches for k8s.mariadb.com/v1alpha1" bila CRD belum
ada. Perlu chart terpisah:
bash
helm install mariadb-operator-crds mariadb-operator/mariadb-operator-crds -n mariadb-operator
kubectl rollout restart deployment mariadb-operator -n mariadb-operator
4. Deploy Cluster Galera (Custom Resource)¶
apiVersion: k8s.mariadb.com/v1alpha1
kind: MariaDB
metadata:
name: galera-cluster
namespace: galera-system
spec:
rootPasswordSecretKeyRef:
name: mariadb-root-credentials
key: password
generate: true # operator generate password otomatis
database: appdb
username: appuser
passwordSecretKeyRef:
name: appuser-credentials
key: password
generate: true
replicas: 3
galera:
enabled: true
affinity:
antiAffinityEnabled: true # shortcut bawaan operator, tak perlu tulis manual podAntiAffinity
storage:
size: 50Gi
storageClassName: longhorn-db-single
resources:
requests:
cpu: 500m
memory: 1Gi
limits:
cpu: 1000m
memory: 2Gi
database dan username di level atas membuat operator otomatis membuat database awal +
user dengan ALL PRIVILEGES di database tersebut — tidak perlu resource Database/User/Grant
terpisah untuk kasus sederhana ini.
5. Verifikasi Cluster Sehat ✅¶
kubectl get pods -n galera-system
# galera-cluster-0 2/2 Running
# galera-cluster-1 2/2 Running
# galera-cluster-2 2/2 Running
kubectl exec -n galera-system galera-cluster-0 -c mariadb -- \
mariadb -uroot -p"$ROOTPASS" -e "SHOW STATUS LIKE 'wsrep_cluster_size';"
Gotcha: mysql bukan lagi nama command
Image mariadb:11.8.8 tidak punya binary mysql — sudah diganti nama jadi mariadb
(dan mariadb-dump menggantikan mysqldump). Sesuaikan semua script/dokumentasi lama yang
masih menyebut mysql.
Hasil verifikasi: wsrep_cluster_size = 3, wsrep_ready = ON,
wsrep_local_state_comment = Synced di ketiga node. Pod anti-affinity terkonfirmasi bekerja —
3 pod tersebar di 3 node fisik berbeda (10.0.0.3, 10.0.0.5, 10.0.0.6).
6. Database & User Aplikasi ✅¶
Dibuat otomatis oleh operator saat cluster pertama kali bootstrap (lihat langkah 4):
| Item | Nilai |
|---|---|
| Database | appdb |
| User | appuser (ALL PRIVILEGES di appdb) |
| Password | Auto-generated, tersimpan di Secret appuser-credentials (key password) |
| Root password | Auto-generated, tersimpan di Secret mariadb-root-credentials (key password) |
7. ProxySQL — Ditunda¶
Sesuai keputusan, ProxySQL belum dipasang di sesi ini — fokus dulu ke Galera inti sampai stabil. Bisa ditambahkan kapan saja mengikuti sizing pada SEKSI-01 (0,5 vCPU/512MB request, 1 vCPU/1GB limit).
8. Backup Terjadwal ke S3 ✅¶
8.1 Arsitektur Endpoint S3¶
Endpoint S3 khusus oneKE (https://oneke-s3.ums.id/, bucket oneke-backups) dibangun di atas
MinIO milik infrastruktur UMS (server bastion, 172.16.64.228:9100) — detail lengkap arsitektur,
alasan DNS-only (bypass Cloudflare JS Challenge), dan pengecualian firewall ada di
Rencana Arsitektur Implementasi.
8.2 Bug: Backup CRD Bawaan Operator Crash¶
Bug ditemukan: nil pointer panic di SDK minio-go
Resource Backup bawaan mariadb-operator (mode S3) konsisten crash saat upload —
panic invalid memory address or nil pointer dereference di dalam
minio-go/v7/pkg/signer.(*StreamingReader).signChunk. Direproduksi 3× berturut-turut,
bukan hiccup jaringan (jalur nginx/firewall yang sama sudah terbukti lancar untuk upload
manual via mc, termasuk file 20MB). Ini bug di SDK yang dipakai operator versi 26.6.0,
bukan masalah di infrastruktur yang kita bangun.
Solusi: CronJob kustom yang menjalankan mariadb-dump dipipe langsung ke mc (client yang
sudah terbukti bekerja normal), bypass mekanisme Backup CRD yang bermasalah:
apiVersion: batch/v1
kind: CronJob
metadata:
name: galera-backup-mc
namespace: galera-system
spec:
schedule: "0 2 * * *"
jobTemplate:
spec:
backoffLimit: 2
template:
spec:
restartPolicy: OnFailure
initContainers:
- name: install-mc
image: curlimages/curl:latest
command: ["sh", "-c", "curl -sL -o /shared/mc https://dl.min.io/client/mc/release/linux-amd64/mc && chmod +x /shared/mc"]
volumeMounts:
- name: shared
mountPath: /shared
containers:
- name: backup
image: mariadb:11.8.8
command:
- sh
- -c
- |
set -e
TS=$(date +%Y%m%d-%H%M%S)
/shared/mc alias set oneke https://oneke-s3.ums.id "$ONEKE_ACCESS_KEY" "$ONEKE_SECRET_KEY"
mariadb-dump -h galera-cluster.galera-system.svc.cluster.local -u root -p"$MARIADB_ROOT_PASSWORD" \
--all-databases --single-transaction | gzip > /tmp/backup-$TS.sql.gz
/shared/mc cp /tmp/backup-$TS.sql.gz oneke/oneke-backups/galera-dumps/backup-$TS.sql.gz
env:
- name: MARIADB_ROOT_PASSWORD
valueFrom: { secretKeyRef: { name: mariadb-root-credentials, key: password } }
- name: ONEKE_ACCESS_KEY
valueFrom: { secretKeyRef: { name: oneke-s3-credentials, key: access-key-id } }
- name: ONEKE_SECRET_KEY
valueFrom: { secretKeyRef: { name: oneke-s3-credentials, key: secret-access-key } }
volumeMounts:
- name: shared
mountPath: /shared
volumes:
- name: shared
emptyDir: {}
Gotcha kedua: image minio/mc:latest tidak jalan di CPU virtual ini
Percobaan pertama pakai image resmi minio/mc:latest sebagai initContainer gagal dengan
error Fatal glibc error: CPU does not support x86-64-v2 — image itu di-build dengan
requirement instruksi CPU yang tidak didukung CPU virtual OpenNebula di sini. Solusinya:
download binary mc release langsung via curl (binary generic, bukan image Docker
ter-optimasi) memakai base image curlimages/curl yang ringan.
Hasil uji manual (8 Juli 2026): backup berhasil, file backup-20260708-145101.sql.gz
(517KB) terkonfirmasi ada di oneke-backups/galera-dumps/.
8.3 Fallback: PVC Lokal¶
Bila endpoint S3 eksternal bermasalah, opsi cadangan ke storage lokal cluster:
storage:
persistentVolumeClaim:
storageClassName: longhorn-hdd
resources:
requests:
storage: 20Gi
9. Uji Failover ✅¶
kubectl delete pod galera-cluster-1 -n galera-system --force --grace-period=0
Hasil: pod baru galera-cluster-1 otomatis dibuat StatefulSet, rejoin cluster dalam ~41
detik (IST — Incremental State Transfer), kembali ke 2/2 Running. Setelah recovery:
wsrep_cluster_size = 3 dan Synced di ketiga node, database appdb tetap utuh — tanpa
intervensi manual sama sekali.
10. Ringkasan Kredensial¶
Semua kredensial tersimpan sebagai Kubernetes Secret di namespace galera-system — tidak
dituliskan plaintext di dokumen ini:
| Secret | Key | Isi |
|---|---|---|
mariadb-root-credentials |
password |
Root password MariaDB (auto-generated) |
appuser-credentials |
password |
Password user appuser (auto-generated) |
oneke-s3-credentials |
access-key-id, secret-access-key |
Kredensial S3 oneke-svc |
# Ambil kredensial saat dibutuhkan (jangan disimpan di file/chat terbuka)
kubectl get secret mariadb-root-credentials -n galera-system -o jsonpath='{.data.password}' | base64 -d
11. phpMyAdmin ✅¶
Dipasang untuk kebutuhan penelitian (akses mudah, belum tahap produksi) — akses via
https://oneke.ums.id/phpmyadmin/.
11.1 Deployment¶
apiVersion: apps/v1
kind: Deployment
metadata:
name: phpmyadmin
namespace: galera-system
spec:
replicas: 1
selector:
matchLabels: { app: phpmyadmin }
template:
metadata:
labels: { app: phpmyadmin }
spec:
containers:
- name: phpmyadmin
image: phpmyadmin/phpmyadmin:latest
env:
- name: PMA_HOST
value: galera-cluster.galera-system.svc.cluster.local
- name: PMA_PORT
value: "3306"
- name: PMA_ABSOLUTE_URI # wajib diisi karena diakses lewat subpath, bukan root domain
value: https://oneke.ums.id/phpmyadmin/
ports:
- containerPort: 80
resources:
requests: { cpu: 100m, memory: 256Mi }
limits: { cpu: 500m, memory: 512Mi }
Terhubung ke galera-cluster.galera-system.svc.cluster.local:3306 — koneksi bisa ke node mana
saja karena Galera multi-master, operator/Service yang mengatur routing.
11.2 Routing (subpath, bukan subdomain baru)¶
Sama seperti dokumentasi /plan/, dipakai path /phpmyadmin/ di domain oneke.ums.id yang
sudah ada (bukan subdomain baru) — via Middleware stripPrefix + redirect trailing-slash,
reuse sertifikat TLS yang sudah ada (oneke-root-tls).
11.3 Status Keamanan — Sengaja Longgar Sementara¶
Keputusan sadar: belum ada IP whitelist/Basic Auth
Atas keputusan eksplisit untuk memudahkan akses selama fase penelitian, /phpmyadmin/
belum diberi lapis proteksi tambahan (IP whitelist + Basic Auth) seperti pada Longhorn
UI. Satu-satunya gerbang saat ini adalah login form phpMyAdmin sendiri (masih butuh
kredensial database asli — root atau appuser — bukan benar-benar terbuka tanpa syarat).
Wajib ditambahkan sebelum dipakai untuk data produksi sungguhan — pola sudah tersedia, tinggal replikasi dari Web Security Longhorn (IP whitelist di HAProxy VNF + Basic Auth via Traefik Middleware).
Login yang direkomendasikan untuk penelitian sehari-hari: user appuser (akses terbatas
hanya ke appdb, bukan root) — password di Secret appuser-credentials.
11.4 Gotcha yang Dicek (tidak terjadi)¶
Mengingat image minio/mc:latest sebelumnya gagal karena requirement CPU x86-64-v2 (lihat
§8.2), image phpmyadmin/phpmyadmin:latest sempat dicurigai berisiko sama — namun terbukti
berjalan normal di CPU virtual OpenNebula ini tanpa masalah kompatibilitas.
Ringkasan Eksekusi¶
| Langkah | Status |
|---|---|
StorageClass longhorn-db-single |
✅ |
Namespace galera-system |
✅ |
mariadb-operator + CRDs |
✅ |
| Cluster Galera 3 node | ✅ Healthy, Synced |
Database appdb + user appuser |
✅ Auto-created |
| ProxySQL | ⏸️ Ditunda (sesuai keputusan) |
| Backup terjadwal ke S3 | ✅ (via CronJob kustom, bukan Backup CRD bawaan — lihat 8.2) |
| Uji failover | ✅ Lulus, recovery ~41 detik |
phpMyAdmin (/phpmyadmin/) |
✅ Live, proteksi tambahan tertunda (sengaja, fase penelitian) |
Diimplementasikan 8 Juli 2026. Lihat Errata & Rekonsiliasi untuk temuan lain di
sesi yang sama (disk VNF/master penuh, proses .vscode-server macet).