Kubernetes 实战:使用 StatefulSet 部署多副本 MySQL 集群

Kubernetes 实战:使用 StatefulSet 部署多副本 MySQL 集群

website Kubernetes website and documentation repo: website 项目地址: https://gitcode.com/gh_mirrors/webs/website

前言

在 Kubernetes 中部署有状态应用一直是个挑战,特别是像 MySQL 这样的数据库系统。本文将带你深入了解如何使用 StatefulSet 控制器在 Kubernetes 中部署一个多副本的 MySQL 集群,并确保其高可用性和数据持久性。

准备工作

在开始之前,请确保你已经具备以下条件:

  1. 一个运行中的 Kubernetes 集群
  2. 已配置默认的 StorageClass
  3. 熟悉 Kubernetes 核心概念(Pod、Service、PersistentVolume 等)
  4. 了解 StatefulSet 的基本工作原理
  5. 使用兼容 AMD64 架构的 CPU

部署架构概述

我们将部署的 MySQL 集群包含以下组件:

  1. 一个 ConfigMap:存储 MySQL 配置
  2. 两个 Service:
    • 无头服务(Headless Service):用于 Pod 间直接通信
    • 读服务(Read Service):负载均衡读请求
  3. 一个 StatefulSet:管理 MySQL Pod 的生命周期

详细部署步骤

1. 创建 ConfigMap

首先创建包含 MySQL 配置的 ConfigMap:

apiVersion: v1
kind: ConfigMap
metadata:
  name: mysql
data:
  primary.cnf: |
    [mysqld]
    log-bin
  replica.cnf: |
    [mysqld]
    super-read-only

这个 ConfigMap 定义了两个配置文件:

  • primary.cnf:主节点配置,启用二进制日志
  • replica.cnf:副本节点配置,设置为只读模式

2. 创建 Service

接下来创建两个 Service:

apiVersion: v1
kind: Service
metadata:
  name: mysql
spec:
  clusterIP: None
  ports:
  - name: mysql
    port: 3306
  selector:
    app: mysql
---
apiVersion: v1
kind: Service
metadata:
  name: mysql-read
spec:
  ports:
  - name: mysql
    port: 3306
  selector:
    app: mysql
  • mysql 是无头服务,用于 Pod 间直接通信
  • mysql-read 是常规服务,用于负载均衡读请求

3. 创建 StatefulSet

最后创建 StatefulSet:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:
  selector:
    matchLabels:
      app: mysql
  serviceName: mysql
  replicas: 3
  template:
    metadata:
      labels:
        app: mysql
    spec:
      initContainers:
      - name: init-mysql
        image: mysql:5.7
        command:
        - bash
        - "-c"
        - |
          set -ex
          # 生成 server-id
          [[ `hostname` =~ -([0-9]+)$ ]] || exit 1
          ordinal=${BASH_REMATCH[1]}
          echo [mysqld] > /mnt/conf.d/server-id.cnf
          echo server-id=$((100 + $ordinal)) >> /mnt/conf.d/server-id.cnf
          # 复制配置文件
          if [[ $ordinal -eq 0 ]]; then
            cp /mnt/config-map/primary.cnf /mnt/conf.d/
          else
            cp /mnt/config-map/replica.cnf /mnt/conf.d/
          fi
        volumeMounts:
        - name: conf
          mountPath: /mnt/conf.d
        - name: config-map
          mountPath: /mnt/config-map
      - name: clone-mysql
        image: gcr.io/google-samples/xtrabackup:1.0
        command:
        - bash
        - "-c"
        - |
          set -ex
          # 跳过主节点的克隆
          [[ `hostname` =~ -([0-9]+)$ ]] || exit 1
          ordinal=${BASH_REMATCH[1]}
          [[ $ordinal -eq 0 ]] && exit 0
          # 从序号较低的 Pod 克隆数据
          ncat --recv-only mysql-$(($ordinal-1)).mysql 3307 | xbstream -x -C /var/lib/mysql
          xtrabackup --prepare --target-dir=/var/lib/mysql
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
          subPath: mysql
      containers:
      - name: mysql
        image: mysql:5.7
        env:
        - name: MYSQL_ALLOW_EMPTY_PASSWORD
          value: "1"
        ports:
        - name: mysql
          containerPort: 3306
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
          subPath: mysql
        - name: conf
          mountPath: /etc/mysql/conf.d
        readinessProbe:
          exec:
            command: ["mysql", "-h", "127.0.0.1", "-e", "SELECT 1"]
          initialDelaySeconds: 5
          periodSeconds: 2
          timeoutSeconds: 1
      - name: xtrabackup
        image: gcr.io/google-samples/xtrabackup:1.0
        ports:
        - name: xtrabackup
          containerPort: 3307
        command:
        - bash
        - "-c"
        - |
          set -ex
          cd /var/lib/mysql
          # 确定是否需要初始化复制
          if [[ -f xtrabackup_slave_info ]]; then
            # 配置复制
            mv xtrabackup_slave_info change_master_to.sql.in
            sed -i 's/^CHANGE MASTER TO/CHANGE MASTER TO MASTER_HOST="mysql-0.mysql", MASTER_USER="root", MASTER_PASSWORD="", MASTER_PORT=3306,/' change_master_to.sql.in
            echo "START SLAVE;" >> change_master_to.sql.in
            mv change_master_to.sql.in change_master_to.sql.gen
          fi
          # 启动复制
          exec ncat --listen --keep-open --send-only --max-conns=1 3307 -c \
            "xtrabackup --backup --slave-info --stream=xbstream --host=127.0.0.1 --user=root"
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
          subPath: mysql
      volumes:
      - name: conf
        emptyDir: {}
      - name: config-map
        configMap:
          name: mysql
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 10Gi

关键实现细节

1. 初始化过程

StatefulSet 中的每个 Pod 都经历以下初始化过程:

  1. init-mysql 容器:

    • 从 Pod 名称中提取序号
    • 生成唯一的 server-id(100 + 序号)
    • 根据序号选择主节点或副本节点配置
  2. clone-mysql 容器:

    • 副本节点从序号较低的 Pod 克隆数据
    • 使用 Percona XtraBackup 工具进行热备份

2. 数据复制

每个副本节点启动后:

  1. 检查是否有需要初始化的数据
  2. 从主节点或其他副本节点同步数据
  3. 配置复制参数并启动复制

3. 高可用设计

  • 主节点(mysql-0)处理所有写请求
  • 读请求可以通过 mysql-read 服务负载均衡到所有节点
  • 使用稳定的 DNS 名称(mysql-0.mysql)确保 Pod 重启后仍能正确连接

测试集群

1. 写入测试

kubectl run mysql-client --image=mysql:5.7 -i --rm --restart=Never -- \
  mysql -h mysql-0.mysql <<EOF
CREATE DATABASE test;
CREATE TABLE test.messages (message VARCHAR(250));
INSERT INTO test.messages VALUES ('hello');
EOF

2. 读取测试

kubectl run mysql-client --image=mysql:5.7 -i -t --rm --restart=Never -- \
  mysql -h mysql-read -e "SELECT * FROM test.messages"

3. 观察负载均衡

kubectl run mysql-client-loop --image=mysql:5.7 -i -t --rm --restart=Never -- \
  bash -ic "while sleep 1; do mysql -h mysql-read -e 'SELECT @@server_id,NOW()'; done"

故障模拟与恢复

1. 模拟 Pod 故障

kubectl exec mysql-2 -c mysql -- mv /usr/bin/mysql /usr/bin/mysql.off

观察读请求是否自动转移到其他可用节点。

2. 扩容测试

kubectl scale statefulset mysql --replicas=5

观察新节点如何自动加入集群并同步数据。

3. 缩容测试

kubectl scale statefulset mysql --replicas=2

确保数据不会丢失,且剩余节点继续正常工作。

生产环境建议

本文示例为了演示简化了安全配置,实际生产环境中应考虑:

  1. 设置正确的 MySQL 密码
  2. 配置适当的资源限制
  3. 实现定期备份策略
  4. 监控数据库性能指标
  5. 考虑使用 MySQL 集群方案如 Group Replication

总结

通过 StatefulSet 部署 MySQL 集群,Kubernetes 提供了:

  1. 稳定的网络标识
  2. 有序的部署和扩展
  3. 持久化存储
  4. 自动化的故障恢复

这种模式不仅适用于 MySQL,也可以应用于其他需要持久化状态和有序部署的应用。

website Kubernetes website and documentation repo: website 项目地址: https://gitcode.com/gh_mirrors/webs/website

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

舒京涌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值