รัน MySQL-MariaDB Cluster บน Kubernetes ด้วย StatefulSet

รัน MySQL-MariaDB Cluster บน Kubernetes ด้วย StatefulSet

การเรียกใช้ MySQL-MariaDB Cluster บน Kubernetes โดยใช้ StatefulSet เป็นวิธีที่มีประสิทธิภาพในการจัดการฐานข้อมูลขนาดใหญ่ที่ต้องการความพร้อมใช้งานสูง (High Availability) และการ Replication ที่เสถียร StatefulSet ให้คุณสามารถจัดการ database replication, persistent storage, และ stable network identity สำหรับแต่ละ Pod ได้อย่างสมบูรณ์

ทำไมต้องใช้ MySQL-MariaDB บน Kubernetes

  • Replication Support: StatefulSet จัดการ Master-Slave Replication และ Galera Cluster ได้อย่างมีประสิทธิภาพ
  • Persistent Storage: PersistentVolume เก็บข้อมูลฐานข้อมูลแบบถาวร ไม่สูญหายเมื่อ Pod ถูกลบ
  • Network Stability: StatefulSet ให้ stable hostname สำหรับแต่ละ Pod ทำให้การเชื่อมต่อระหว่าง Pod เสถียรขึ้น
  • Ordered Deployment: Pods ถูกสร้างและลบตามลำดับ 0, 1, 2, …, n ป้องกัน data inconsistency
  • Scalability: ง่ายต่อการเพิ่มหรือลดจำนวน Replicas ตามความต้องการ

1. Master-Slave Replication

เป็นรูปแบบที่ใช้มากที่สุด โดย Master Pod รับการเขียนข้อมูล (write) และ Slave Pods รับการอ่านข้อมูล (read) โดยการทำ replicate จาก Master ทำให้สามารถแยกโหลดการอ่านและการเขียนได้

2. Galera Cluster

Galera Cluster เป็นรูปแบบ Multi-Master Replication ที่ให้ทุก Node สามารถรับการเขียนข้อมูลได้ด้วยกัน มีการ synchronous replication ทำให้ข้อมูลสอดคล้องกันตลอดเวลา

ขั้นตอนที่ 1: สร้าง Namespace

kubectl create namespace mysql-cluster
kubectl config set-context --current --namespace=mysql-cluster

ขั้นตอนที่ 2: สร้าง Secret สำหรับ MySQL Passwords

สร้าง Kubernetes Secret เพื่อเก็บรหัสผ่าน MySQL อย่างปลอดภัยแทนการเขียน hard-coded ลงในไฟล์ YAML:

kubectl create secret generic mysql-secret \
  --from-literal=MYSQL_ROOT_PASSWORD=secure-root-password \
  --from-literal=MYSQL_REPLICATION_USER=repl \
  --from-literal=MYSQL_REPLICATION_PASSWORD=repl-password \
  -n mysql-cluster

ขั้นตอนที่ 3: สร้าง MySQL ConfigMap

สร้าง ConfigMap ที่เก็บไฟล์การตั้งค่า MySQL (my.cnf) สำหรับ replication:

cat > mysql-config.yaml << 'EOF'
apiVersion: v1
kind: ConfigMap
metadata:
  name: mysql-config
  namespace: mysql-cluster
data:
  my.cnf: |
    [mysqld]
    log-bin=mysql-bin
    binlog-format=ROW
    server-id=0
    relay-log=relay-bin
    relay-log-index=relay-bin.index
    master-info-repository=TABLE
    relay-log-info-repository=TABLE
    skip-slave-start
    read-only=off
EOF

kubectl apply -f mysql-config.yaml

ขั้นตอนที่ 4: สร้าง MySQL StatefulSet

สร้าง StatefulSet สำหรับจัดการ MySQL Pods โดยมี Persistent Storage และ Health Checks:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
  namespace: mysql-cluster
spec:
  serviceName: mysql
  replicas: 3
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - name: mysql
        image: mysql:8.0-debian
        ports:
        - containerPort: 3306
          name: mysql
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-secret
              key: MYSQL_ROOT_PASSWORD
        - name: MYSQL_REPLICATION_USER
          valueFrom:
            secretKeyRef:
              name: mysql-secret
              key: MYSQL_REPLICATION_USER
        - name: MYSQL_REPLICATION_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-secret
              key: MYSQL_REPLICATION_PASSWORD
        volumeMounts:
        - name: mysql-data
          mountPath: /var/lib/mysql
        - name: mysql-config
          mountPath: /etc/mysql/conf.d
        livenessProbe:
          exec:
            command:
            - /bin/sh
            - -c
            - "mysqladmin ping -h127.0.0.1 -uroot -p${MYSQL_ROOT_PASSWORD}"
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          exec:
            command:
            - /bin/sh
            - -c
            - "mysql -h127.0.0.1 -uroot -p${MYSQL_ROOT_PASSWORD} -e 'SELECT 1'"
          initialDelaySeconds: 5
          periodSeconds: 5
      volumes:
      - name: mysql-config
        configMap:
          name: mysql-config
  volumeClaimTemplates:
  - metadata:
      name: mysql-data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 30Gi

ขั้นตอนที่ 5: สร้าง MySQL Service

สร้าง Kubernetes Service สองแบบ: Headless Service สำหรับ StatefulSet และ ClusterIP Service สำหรับการเชื่อมต่อแบบ load-balanced:

apiVersion: v1
kind: Service
metadata:
  name: mysql
  namespace: mysql-cluster
spec:
  clusterIP: None
  selector:
    app: mysql
  ports:
  - port: 3306
    targetPort: 3306
---
apiVersion: v1
kind: Service
metadata:
  name: mysql-read
  namespace: mysql-cluster
spec:
  type: ClusterIP
  selector:
    app: mysql
  ports:
  - port: 3306
    targetPort: 3306

ขั้นตอนที่ 6: ตั้งค่า Replication

สร้าง Job เพื่อตั้งค่า Replication จากอปลายน์ Master (mysql-0) ไปยัง Slave Pods:

apiVersion: batch/v1
kind: Job
metadata:
  name: mysql-replication-init
  namespace: mysql-cluster
spec:
  template:
    spec:
      containers:
      - name: mysql-init
        image: mysql:8.0-debian
        command: ["/bin/bash"]
        args:
          - -c
          - |
            for i in {1..2}; do
              mysql -h mysql-$i.mysql -u root -p${MYSQL_ROOT_PASSWORD} -e \
                "CHANGE MASTER TO MASTER_HOST='mysql-0.mysql', \
                MASTER_USER='repl', MASTER_PASSWORD='${MYSQL_REPLICATION_PASSWORD}', \
                MASTER_LOG_FILE='mysql-bin.000001', MASTER_LOG_POS=154; \
                START SLAVE;"
            done
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-secret
              key: MYSQL_ROOT_PASSWORD
        - name: MYSQL_REPLICATION_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-secret
              key: MYSQL_REPLICATION_PASSWORD
      restartPolicy: Never

การตรวจสอบ Cluster Status

# ตรวจสอบสถานะของ Pods
kubectl get pods -n mysql-cluster

# เข้าไป Master Pod และดูสถานะ Master
kubectl exec -it mysql-0 -n mysql-cluster -- \
  mysql -uroot -p${MYSQL_ROOT_PASSWORD} -e "SHOW MASTER STATUS\G"

# ตรวจสอบสถานะ Slave Replication
kubectl exec -it mysql-1 -n mysql-cluster -- \
  mysql -uroot -p${MYSQL_ROOT_PASSWORD} -e "SHOW SLAVE STATUS\G"

# ดูรายชื่อกระบวนการที่กำลังทำงาน
kubectl exec -it mysql-0 -n mysql-cluster -- \
  mysql -uroot -p${MYSQL_ROOT_PASSWORD} -e "SELECT * FROM information_schema.processlist\G"

Full Backup จาก Master Pod

kubectl exec -it mysql-0 -n mysql-cluster -- \
  mysqldump -uroot -p${MYSQL_ROOT_PASSWORD} \
  --all-databases --single-transaction > mysql-full-backup.sql

Incremental Backup โดยใช้ Binary Logs

kubectl exec -it mysql-0 -n mysql-cluster -- \
  mysqlbinlog mysql-bin.000001 > binlog-000001.sql

การขยายขนาด (Scaling) MySQL Cluster

เพื่อเพิ่ม Replicas สำหรับการรับน้ำหนักการอ่าน (read load) หรือสำหรับความพร้อมใช้งานที่สูงขึ้น:

# เพิ่มจาก 3 เป็น 5 replicas
kubectl patch statefulset mysql -n mysql-cluster -p '{"spec":{"replicas":5}}'

Best Practices สำหรับ MySQL on Kubernetes

  • ใช้ Kubernetes Secret เพื่อเก็บ Passwords แทนการ hard-code ในไฟล์ YAML
  • เลือก PersistentVolume storage class ที่เหมาะกับ infrastructure ของคุณ (SSD, NAS, Cloud Storage)
  • ตั้งค่า Liveness Probe และ Readiness Probe เพื่อตรวจสอบสุขภาพ Cluster
  • จัดการ Backup strategy อย่างเหมาะสมและทดสอบ Recovery process เป็นประจำ
  • ใช้ Binary Logs สำหรับ Incremental Backup และ Point-in-time Recovery
  • ตั้งค่า Monitoring และ Alerting สำหรับ Replication lag และการลบต่าง ๆ
  • ปิด skip-slave-start เพื่อให้ Slave Replication เริ่มต้นใหม่โดยอัตโนมัติหลังจาก Pod restart
  • ใช้ row-based binary logging (binlog-format=ROW) สำหรับความปลอดภัยของ Replication

บทสรุป

การ Deploy MySQL-MariaDB Cluster บน Kubernetes ด้วย StatefulSet ต้องการความเข้าใจลึก ๆ เกี่ยวกับ Kubernetes primitives และ database replication concepts StatefulSet ให้ข้อมูลเหล่านี้: stable network identity, ordered deployment, persistent storage, ทำให้เป็นตัวเลือกที่ดีสำหรับ database workloads ด้วยการตั้งค่าที่ถูกต้องและ monitoring ที่เพียงพอ คุณสามารถสร้าง MySQL Cluster ที่มีความพร้อมใช้งานสูงและ scalable ได้