Kubernetes 部署高可用 MySQL 集群

在 Kubernetes 中部署高可用的 MySQL 集群是一个相对复杂的任务,需要仔细考虑状态管理、持久化存储、网络标识、数据一致性和故障恢复。以下是两种主流方案的详细指南:


方案一:使用 StatefulSet + Headless Service + 自定义初始化脚本(手动管理)

适用场景:学习原理、小规模环境、对自动化要求不高。

核心组件

  1. StatefulSet:为每个 Pod 提供稳定的标识符(pod-name-0, pod-name-1)和持久化存储(每个 Pod 独立的 PVC)。
  2. Headless Service (ClusterIP: None):提供稳定的 DNS 记录(pod-name-0.service-name, pod-name-1.service-name),用于 Pod 间直接通信和主从发现。
  3. ConfigMap:存放 MySQL 配置文件 (my.cnf)。
  4. Secret:存放 MySQL root 密码、复制用户密码等敏感信息。
  5. PersistentVolume (PV) / PersistentVolumeClaim (PVC):提供持久化存储(必须使用支持 ReadWriteOnce 的存储类)。

详细部署步骤 (YAML 示例)

1. 创建 Namespace (可选)
apiVersion: v1
kind: Namespace
metadata:
  name: mysql-cluster
2. 创建 Secret (保存密码)
apiVersion: v1
kind: Secret
metadata:
  name: mysql-secrets
  namespace: mysql-cluster
type: Opaque
data:
  root-password: BASE64_ENCODED_ROOT_PASSWORD   # echo -n "yourpassword" | base64
  repl-password: BASE64_ENCODED_REPL_PASSWORD
3. 创建 ConfigMap (MySQL 配置)
apiVersion: v1
kind: ConfigMap
metadata:
  name: mysql-config
  namespace: mysql-cluster
data:
  my.cnf: |
    [mysqld]
    server_id=1              # 重要!每个 Pod 需配置唯一 server_id(可通过环境变量注入)
    binlog_format=ROW
    log_bin=mysql-bin
    gtid_mode=ON
    enforce_gtid_consistency=ON
    datadir=/var/lib/mysql
    socket=/var/run/mysqld/mysqld.sock
    symbolic-links=0
    skip-name-resolve        # 提升性能,避免 DNS 解析问题
    [client]
    default-character-set=utf8mb4
    [mysql]
    default-character-set=utf8mb4
4. 创建 Headless Service
apiVersion: v1
kind: Service
metadata:
  name: mysql-hs
  namespace: mysql-cluster
  labels:
    app: mysql
spec:
  ports:
  - name: mysql
    port: 3306
  clusterIP: None            # Headless Service 的关键!
  selector:
    app: mysql
5. 创建 StatefulSet (核心)
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
  namespace: mysql-cluster
spec:
  serviceName: mysql-hs      # 关联 Headless Service
  replicas: 3                # 集群节点数(通常奇数个)
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      initContainers:
      - name: init-mysql
        image: mysql:8.0     # 推荐使用固定版本 tag
        command:
        - bash
        - "-c"
        - |
          # 根据 Pod 序号 (0,1,2) 生成唯一 server_id
          [[ `hostname` =~ -([0-9]+)$ ]] || exit 1
          ordinal=${BASH_REMATCH[1]}
          server_id=$((100 + $ordinal))
          sed -i "s/server_id=1/server_id=$server_id/" /mnt/config-map/my.cnf
          # 如果是 Pod-0 (序号0),标记为初始主库 (可选步骤)
          if [[ $ordinal -eq 0 ]]; then
            echo "read_only=0" >> /mnt/config-map/my.cnf
          else
            echo "read_only=1" >> /mnt/config-map/my.cnf
          fi
        volumeMounts:
        - name: config
          mountPath: /mnt/config-map
        - name: conf
          mountPath: /etc/mysql/conf.d
      containers:
      - name: mysql
        image: mysql:8.0
        env:
        - name: MYSQL_ROOT_PASSWORD_FILE
          value: /etc/secrets/root-password
        ports:
        - containerPort: 3306
          name: mysql
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
        - name: conf
          mountPath: /etc/mysql/conf.d
        - name: secrets
          mountPath: /etc/secrets
          readOnly: true
        livenessProbe:
          exec:
            command: ["mysqladmin", "ping", "-uroot", "-p$(cat /etc/secrets/root-password)"]
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          exec:
            command: ["mysql", "-uroot", "-p$(cat /etc/secrets/root-password)", "-e", "SELECT 1"]
          initialDelaySeconds: 5
          periodSeconds: 5
      volumes:
      - name: conf           # 存放最终配置文件
        emptyDir: {}
      - name: config         # 挂载 ConfigMap
        configMap:
          name: mysql-config
      - name: secrets        # 挂载 Secret
        secret:
          secretName: mysql-secrets
  volumeClaimTemplates:      # 核心!为每个 Pod 自动创建 PVC
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      storageClassName: "your-storage-class" # 替换为你的 StorageClass (e.g., gp2, standard, local-path)
      resources:
        requests:
          storage: 10Gi     # 根据需求调整大小
6. 初始化主从复制 (手动或脚本)

Pod-0 启动后

  1. 登录 Pod-0 (kubectl exec -it mysql-0 -n mysql-cluster -- mysql -uroot -p),创建复制用户:
    CREATE USER 'repl'@'%' IDENTIFIED BY 'YOUR_REPL_PASSWORD';
    GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
    FLUSH PRIVILEGES;
    
  2. 获取主库 (mysql-0) 的 GTID 位置:
    SHOW MASTER STATUS\G
    
  3. 从库 Pod (mysql-1, mysql-2) 上配置复制:
    CHANGE MASTER TO
      MASTER_HOST='mysql-0.mysql-hs.mysql-cluster.svc.cluster.local', # 稳定的 DNS 地址!
      MASTER_USER='repl',
      MASTER_PASSWORD='YOUR_REPL_PASSWORD',
      MASTER_AUTO_POSITION=1;
    START SLAVE;
    
  4. 检查从库状态:SHOW SLAVE STATUS\G (确保 Slave_IO_RunningSlave_SQL_RunningYes)。

方案二:使用 MySQL Operator (推荐生产环境)

优点:自动化集群管理(部署、扩缩容、备份恢复、故障转移、升级)、封装最佳实践、简化运维。

主流 Operator 选择

  1. Oracle MySQL Operator for Kubernetes (官方):https://github.com/mysql/mysql-operator
  2. Presslabs MySQL Operator (Vitess)https://github.com/bitpoke/mysql-operator (基于 Vitess,功能强大)
  3. Percona Operator for MySQLhttps://www.percona.com/doc/kubernetes-operator-for-ps/ (包含 XtraBackup 支持)

以 Oracle MySQL Operator 为例 (简化步骤)

1. 安装 Operator
helm repo add mysql-operator https://mysql.github.io/mysql-operator/
helm install my-mysql-operator mysql-operator/mysql-operator --namespace mysql-operator --create-namespace
2. 部署 InnoDB Cluster (使用官方 MySQL Shell 管理集群)
apiVersion: mysql.oracle.com/v2
kind: InnoDBCluster
metadata:
  name: mycluster
  namespace: mysql-cluster
spec:
  secretName: mycluster-secret # 包含 root 密码的 Secret (提前创建)
  tlsUseSelfSigned: true      # 生产环境应使用正式证书
  instances: 3
  router:
    instances: 1              # MySQL Router 实例数
  podSpec:
    imagePullPolicy: IfNotPresent
  volumeClaimTemplate:
    metadata:
      name: datadir
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 10Gi
      storageClassName: "your-storage-class"
Operator 自动完成
  1. 创建 StatefulSet (3 Pods) 和 PVCs。
  2. 初始化第一个节点 (mycluster-0) 为 Primary。
  3. 自动配置 Group Replication (基于 Paxos)。
  4. 部署 MySQL Router (提供读写分离入口)。
  5. 自动处理节点故障转移(Primary 选举)。

关键注意事项 & 优化建议

  1. 存储选择

    • 性能关键:优先考虑本地 SSD (Local Persistent Volumes) 或云提供商的高性能块存储 (如 AWS io2/gp3, Azure Premium SSD v2, GCP pd-ssd)。
    • 可靠性:确保存储后端支持快照和备份功能。
    • Reclaim Policy务必设置为 Retain 或确保有可靠备份!避免 PVC 删除导致数据永久丢失。
  2. 网络优化

    • 使用 CNI 插件(如 Cilium, Calico) 并启用 hostNetworkhostPort(牺牲可移植性换取性能)以减少网络开销。
    • 确保 Pod 间通信低延迟、高带宽
  3. 备份与恢复 (必须配置!)

    • 方案一:定期 mysqldump 或使用 Percona XtraBackup(需在 Pod 内执行或使用 Sidecar 容器)。
    • 方案二 (Operator):利用 Operator 内置的备份功能(通常集成 XtraBackupmysqldump),将备份存储到 S3/MinIO/NFS 等。
  4. 监控与日志

    • 监控:部署 mysql_exporter + Prometheus + Grafana,监控核心指标(QPS、连接数、复制延迟、慢查询、资源使用)。
    • 日志:使用 Fluentd/Fluent Bit 收集 MySQL 日志到 ElasticsearchLoki
  5. 高可用与故障转移

    • 方案一:需要自行监控主库状态(如 mysqladmin ping)并手动/脚本触发主从切换(复杂且易出错)。
    • 方案二 (Operator):Operator 自动监控节点健康并进行故障转移(通常基于 Group Replication 的仲裁机制)。
  6. 访问方式

    • 读写分离:使用 MySQL Router(Operator 通常自带)或中间件(如 ProxySQL)。
    • Service 类型
      • 写请求:指向主库的 Service(Operator 通常会动态更新该 Service 指向当前 Primary)。
      • 读请求:指向所有节点或只读副本的 Service。
  7. 安全

    • 网络策略 (NetworkPolicy):限制访问 MySQL Pod 的来源(仅限应用 Pod)。
    • RBAC:严格控制对 MySQL 相关资源(Secret, StatefulSet, PVC)的访问权限。
    • TLS 加密:强制启用 MySQL 连接加密(Operator 通常支持)。

总结

  • 学习/小规模StatefulSet + Headless Service + 手动配置复制 可行,但运维负担重。
  • 生产环境/追求自动化强烈推荐使用成熟的 MySQL Operator (Oracle, Presslabs, Percona)。它们封装了复杂性,提供了开箱即用的高可用、备份恢复、监控集成等关键功能。
  • 核心挑战:始终是存储性能、网络延迟和运维复杂性。务必进行充分的性能测试和故障演练。
  • 云服务替代方案:如果对自运维无硬性要求,考虑云托管的 MySQL 服务(如 AWS RDS/Aurora, Azure Database for MySQL, GCP Cloud SQL),它们通常提供更简单、更可靠的高可用方案。

部署前务必在非生产环境充分测试备份恢复、故障转移、升级等关键操作流程!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值