K8s 部署 MySQL 主从复制集群

本文将详细介绍如何在 Kubernetes(K8s)中部署 MySQL 主从复制集群,包含配置文件编写、部署步骤、主从同步验证等核心内容。

前提条件

  1. 已搭建好 K8s 集群(单节点 / 多节点均可,生产环境建议多节点)
  2. 已安装kubectl并配置好集群访问权限
  3. 集群中有可用的存储类(StorageClass),用于动态创建 PV(若没有,可改用 HostPath,仅测试用)

核心思路

  1. 主从集群架构:1 主(master)+1 从(slave),通过 StatefulSet 部署(保证固定网络标识和存储)
  2. 主从同步:基于 GTID(全局事务标识)的复制方式,可靠性更高
  3. 网络:通过 Headless Service 提供固定 DNS 解析,便于主从通信
  4. 存储:使用 PersistentVolumeClaim(PVC)持久化数据

步骤 1:创建命名空间(可选)

为了隔离资源,创建专门的命名空间:

复制代码
kubectl create namespace mysql-cluster

步骤 2:编写 ConfigMap 配置

创建mysql-configmap.yaml,存放主从通用配置和差异化配置:

复制代码
apiVersion: v1
kind: ConfigMap
metadata:
  name: mysql-config
  namespace: mysql-cluster
data:
  # 主库配置
  master.cnf: |
    [mysqld]
    log-bin=mysql-bin
    server-id=1
    gtid-mode=ON
    enforce-gtid-consistency=1
    binlog-format=ROW
    skip-slave-start
    datadir=/var/lib/mysql
    socket=/var/lib/mysql/mysql.sock
    symbolic-links=0
    log-error=/var/log/mysqld.log
    pid-file=/var/run/mysqld/mysqld.pid
  # 从库配置
  slave.cnf: |
    [mysqld]
    server-id=2
    gtid-mode=ON
    enforce-gtid-consistency=1
    skip-slave-start
    read-only=1
    datadir=/var/lib/mysql
    socket=/var/lib/mysql/mysql.sock
    symbolic-links=0
    log-error=/var/log/mysqld.log
    pid-file=/var/run/mysqld/mysqld.pid

应用配置:

复制代码
kubectl apply -f mysql-configmap.yaml

步骤 3:编写 Secret 存储敏感信息

创建mysql-secret.yaml,存储 root 密码和复制用户密码:

复制代码
apiVersion: v1
kind: Secret
metadata:
  name: mysql-secret
  namespace: mysql-cluster
type: Opaque
data:
  # 密码需用base64编码,例如:echo -n "123456" | base64
  root-password: MTIzNDU2
  repl-password: cmVwbDF2ZXJAMTIz

应用 Secret:

复制代码
kubectl apply -f mysql-secret.yaml

步骤 4:编写 StatefulSet 和 Service 配置

创建mysql-statefulset.yaml,包含 Headless Service 和 StatefulSet:

复制代码
apiVersion: v1
kind: Service
metadata:
  name: mysql
  namespace: mysql-cluster
  labels:
    app: mysql
spec:
  clusterIP: None # Headless Service,无集群IP,通过DNS解析Pod
  ports:
  - port: 3306
    name: mysql
  selector:
    app: mysql
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
  namespace: mysql-cluster
spec:
  serviceName: mysql # 关联Headless Service
  replicas: 2 # 1主1从
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      initContainers:
      # 初始化权限目录
      - name: init-mysql
        image: mysql:8.0
        command:
        - bash
        - -c
        - |
          set -ex
          # 基于Pod序号区分主从(0为主,1为从)
          [[ `hostname` =~ -([0-9]+)$ ]] || exit 1
          ordinal=${BASH_REMATCH[1]}
          echo "server-id=$((100 + $ordinal))" > /mnt/conf.d/server-id.cnf
          # 主库加载master.cnf,从库加载slave.cnf
          if [[ $ordinal -eq 0 ]]; then
            cp /mnt/config-map/master.cnf /mnt/conf.d/
          else
            cp /mnt/config-map/slave.cnf /mnt/conf.d/
          fi
        volumeMounts:
        - name: conf
          mountPath: /mnt/conf.d
        - name: config-map
          mountPath: /mnt/config-map
      # 从库初始化复制(等待主库启动后配置)
      - name: clone-mysql
        image: mysql:8.0
        command:
        - bash
        - -c
        - |
          set -ex
          # 仅从库执行
          [[ `hostname` =~ -([0-9]+)$ ]] || exit 1
          ordinal=${BASH_REMATCH[1]}
          if [[ $ordinal -eq 0 ]]; then
            exit 0
          fi
          # 等待主库就绪
          until mysql -h mysql-0.mysql -uroot -p"${MYSQL_ROOT_PASSWORD}" -e "SELECT 1"; do
            echo "Waiting for master to be ready..."
            sleep 3
          done
          # 克隆主库数据
          mysqldump -h mysql-0.mysql -uroot -p"${MYSQL_ROOT_PASSWORD}" --all-databases --master-data=1 --single-transaction > /tmp/backup.sql
          mysql -uroot -p"${MYSQL_ROOT_PASSWORD}" -e "RESET MASTER; SOURCE /tmp/backup.sql;"
          # 配置主从复制
          mysql -uroot -p"${MYSQL_ROOT_PASSWORD}" -e "
            CHANGE REPLICATION SOURCE TO
            SOURCE_HOST='mysql-0.mysql',
            SOURCE_USER='repl',
            SOURCE_PASSWORD='${MYSQL_REPL_PASSWORD}',
            SOURCE_AUTO_POSITION=1;
            START REPLICA;
          "
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-secret
              key: root-password
        - name: MYSQL_REPL_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-secret
              key: repl-password
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
        - name: conf
          mountPath: /etc/mysql/conf.d
      containers:
      - name: mysql
        image: mysql:8.0
        ports:
        - containerPort: 3306
          name: mysql
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-secret
              key: root-password
        - name: MYSQL_REPL_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-secret
              key: repl-password
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
        - name: conf
          mountPath: /etc/mysql/conf.d
        # 健康检查
        livenessProbe:
          exec:
            command: ["mysqladmin", "ping", "-uroot", "-p${MYSQL_ROOT_PASSWORD}"]
          initialDelaySeconds: 30
          periodSeconds: 10
          timeoutSeconds: 5
        readinessProbe:
          exec:
            command: ["mysql", "-uroot", "-p${MYSQL_ROOT_PASSWORD}", "-e", "SELECT 1"]
          initialDelaySeconds: 5
          periodSeconds: 2
          timeoutSeconds: 1
      volumes:
      - name: conf
        emptyDir: {}
      - name: config-map
        configMap:
          name: mysql-config
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      # 替换为你的StorageClass名称,测试环境可改用hostPath
      storageClassName: "standard" 
      resources:
        requests:
          storage: 10Gi

步骤 5:部署集群

  1. 应用配置文件:

    kubectl apply -f mysql-statefulset.yaml

  2. 查看 Pod 状态(等待所有 Pod 变为 Running):

    kubectl get pods -n mysql-cluster -w

    NAME READY STATUS RESTARTS AGE
    mysql-0 1/1 Running 0 5m
    mysql-1 1/1 Running 0 3m

步骤 6:配置主库复制用户

  1. 进入主库 Pod(mysql-0):

    kubectl exec -it mysql-0 -n mysql-cluster -- bash

  2. 登录 MySQL:

    mysql -uroot -p$MYSQL_ROOT_PASSWORD

  3. 创建复制用户并授权:

    CREATE USER 'repl'@'%' IDENTIFIED BY '${MYSQL_REPL_PASSWORD}';
    GRANT REPLICATION SLAVE, REPLICATION CLIENT ON . TO 'repl'@'%';
    FLUSH PRIVILEGES;

(注:若clone-mysql初始化容器已自动创建,可跳过此步骤)

步骤 7:验证主从复制

1. 查看主库状态

在主库 Pod 中执行:

复制代码
SHOW MASTER STATUS;

输出示例(关键看 File 和 Position):

复制代码
+------------------+----------+--------------+------------------+-------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000001 |      156 |              |                  |                   |
+------------------+----------+--------------+------------------+-------------------+

2. 查看从库复制状态

  1. 进入从库 Pod(mysql-1):

    kubectl exec -it mysql-1 -n mysql-cluster -- bash
    mysql -uroot -p$MYSQL_ROOT_PASSWORD

  2. 执行:

    SHOW REPLICA STATUS\G;

关键检查项:

  • Slave_IO_Running: Yes
  • Slave_SQL_Running: Yes
  • Last_IO_Error: 空(无错误)
  • Seconds_Behind_Master: 0(延迟为 0)

3. 数据同步测试

  1. 在主库创建测试数据库和表:

    CREATE DATABASE test_db;
    USE test_db;
    CREATE TABLE test_table (id INT PRIMARY KEY, name VARCHAR(20));
    INSERT INTO test_table VALUES (1, 'test');

  2. 在从库验证数据是否同步:

    USE test_db;
    SELECT * FROM test_table;

若能查询到数据,说明主从同步正常。

常见问题排查

  1. 从库 Slave_IO_Running 为 No

    • 检查主库地址是否正确(mysql-0.mysql)
    • 检查复制用户密码是否正确
    • 检查主库 3306 端口是否可访问(telnet mysql-0.mysql 3306
  2. PVC 创建失败

    • 确认集群中有可用的 StorageClass

    • 测试环境可改用 hostPath 替换 volumeClaimTemplates: yaml

      复制代码
      volumes:
      - name: data
        hostPath:
          path: /data/mysql/$(POD_NAME)
          type: DirectoryOrCreate

      (需在 StatefulSet 的 pod 模板中添加$(POD_NAME)环境变量)

  3. 初始化容器执行失败

    • 查看日志:kubectl logs mysql-1 -n mysql-cluster -c clone-mysql
    • 检查主库是否正常启动,root 密码是否正确

生产环境优化建议

  1. 增加从库数量(修改 StatefulSet 的 replicas)
  2. 使用 MySQL Operator(如 Percona XtraDB Operator)简化管理
  3. 配置监控(Prometheus + Grafana)监控主从延迟、同步状态
  4. 启用数据加密(TLS)和密码轮换
  5. 配置备份策略(如定时 mysqldump 或使用 xtrabackup)
  6. 使用 RBAC 限制 Pod 权限,配置网络策略隔离 MySQL 集群

清理资源(可选)

若需删除集群,执行:

复制代码
kubectl delete -f mysql-statefulset.yaml
kubectl delete -f mysql-secret.yaml
kubectl delete -f mysql-configmap.yaml
kubectl delete namespace mysql-cluster
# 删除PVC(谨慎,会删除数据)
kubectl delete pvc -n mysql-cluster --all

以上就是 K8s 中部署 MySQL 主从复制集群的完整流程,核心是通过 StatefulSet 保证主从节点的固定标识,结合 ConfigMap 和 Secret 管理配置,通过初始化容器完成主从复制的自动配置。

相关推荐
翔云1234563 小时前
服务器异常崩溃,GTID 是否会出现在 mysql.gtid_executed 表但不在 binlog 中
服务器·mysql·adb
海上飞猪3 小时前
【Mysql】Mysql的安装部署和使用
android·mysql·adb
翔云1234561 天前
mysql.gtid_executed 表的初始化和更新机制
数据库·mysql·adb
头发那是一根不剩了1 天前
MySQL 启动、连接问题汇总
数据库·mysql·adb
dyxal2 天前
Windows 内网环境离线安装 MySQL 完整指南
windows·mysql·adb
我的offer在哪里2 天前
mysql的底层文件分析
数据库·mysql·adb
我的offer在哪里2 天前
如何查看和修改 MySQL 底层文件(分「查看」「修改」维度,严格区分安全 / 危险操作)
mysql·安全·adb
我的offer在哪里2 天前
如何确认 MySQL 备份权限的最小化(从「权限设计」「权限校验」「权限审计」三维度落地)
adb
ahauedu2 天前
MySQL- 查看表的历史SQL
sql·mysql·adb