在 Kubernetes 中部署高可用的 MySQL 集群是一个相对复杂的任务,需要仔细考虑状态管理、持久化存储、网络标识、数据一致性和故障恢复。以下是两种主流方案的详细指南:
方案一:使用 StatefulSet + Headless Service + 自定义初始化脚本(手动管理)
适用场景:学习原理、小规模环境、对自动化要求不高。
核心组件
- StatefulSet:为每个 Pod 提供稳定的标识符(
pod-name-0
,pod-name-1
)和持久化存储(每个 Pod 独立的 PVC)。 - Headless Service (
ClusterIP: None
):提供稳定的 DNS 记录(pod-name-0.service-name
,pod-name-1.service-name
),用于 Pod 间直接通信和主从发现。 - ConfigMap:存放 MySQL 配置文件 (
my.cnf
)。 - Secret:存放 MySQL root 密码、复制用户密码等敏感信息。
- 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 启动后:
- 登录 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;
- 获取主库 (
mysql-0
) 的 GTID 位置:SHOW MASTER STATUS\G
- 在 从库 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;
- 检查从库状态:
SHOW SLAVE STATUS\G
(确保Slave_IO_Running
和Slave_SQL_Running
为Yes
)。
方案二:使用 MySQL Operator (推荐生产环境)
优点:自动化集群管理(部署、扩缩容、备份恢复、故障转移、升级)、封装最佳实践、简化运维。
主流 Operator 选择
- Oracle MySQL Operator for Kubernetes (官方):https://github.com/mysql/mysql-operator
- Presslabs MySQL Operator (Vitess):https://github.com/bitpoke/mysql-operator (基于 Vitess,功能强大)
- Percona Operator for MySQL:https://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 自动完成
- 创建 StatefulSet (3 Pods) 和 PVCs。
- 初始化第一个节点 (
mycluster-0
) 为 Primary。 - 自动配置 Group Replication (基于 Paxos)。
- 部署 MySQL Router (提供读写分离入口)。
- 自动处理节点故障转移(Primary 选举)。
关键注意事项 & 优化建议
-
存储选择:
- 性能关键:优先考虑本地 SSD (
Local Persistent Volumes
) 或云提供商的高性能块存储 (如 AWS io2/gp3, Azure Premium SSD v2, GCP pd-ssd)。 - 可靠性:确保存储后端支持快照和备份功能。
Reclaim Policy
:务必设置为Retain
或确保有可靠备份!避免 PVC 删除导致数据永久丢失。
- 性能关键:优先考虑本地 SSD (
-
网络优化:
- 使用 CNI 插件(如 Cilium, Calico) 并启用
hostNetwork
或hostPort
(牺牲可移植性换取性能)以减少网络开销。 - 确保 Pod 间通信低延迟、高带宽。
- 使用 CNI 插件(如 Cilium, Calico) 并启用
-
备份与恢复 (必须配置!):
- 方案一:定期
mysqldump
或使用Percona XtraBackup
(需在 Pod 内执行或使用 Sidecar 容器)。 - 方案二 (Operator):利用 Operator 内置的备份功能(通常集成
XtraBackup
或mysqldump
),将备份存储到 S3/MinIO/NFS 等。
- 方案一:定期
-
监控与日志:
- 监控:部署
mysql_exporter
+Prometheus
+Grafana
,监控核心指标(QPS、连接数、复制延迟、慢查询、资源使用)。 - 日志:使用
Fluentd/Fluent Bit
收集 MySQL 日志到Elasticsearch
或Loki
。
- 监控:部署
-
高可用与故障转移:
- 方案一:需要自行监控主库状态(如
mysqladmin ping
)并手动/脚本触发主从切换(复杂且易出错)。 - 方案二 (Operator):Operator 自动监控节点健康并进行故障转移(通常基于 Group Replication 的仲裁机制)。
- 方案一:需要自行监控主库状态(如
-
访问方式:
- 读写分离:使用 MySQL Router(Operator 通常自带)或中间件(如 ProxySQL)。
- Service 类型:
- 写请求:指向主库的 Service(Operator 通常会动态更新该 Service 指向当前 Primary)。
- 读请求:指向所有节点或只读副本的 Service。
-
安全:
- 网络策略 (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),它们通常提供更简单、更可靠的高可用方案。
部署前务必在非生产环境充分测试备份恢复、故障转移、升级等关键操作流程!