本文将详细介绍如何在 Kubernetes(K8s)中部署 MySQL 主从复制集群,包含配置文件编写、部署步骤、主从同步验证等核心内容。
前提条件
- 已搭建好 K8s 集群(单节点 / 多节点均可,生产环境建议多节点)
- 已安装
kubectl并配置好集群访问权限 - 集群中有可用的存储类(StorageClass),用于动态创建 PV(若没有,可改用 HostPath,仅测试用)
核心思路
- 主从集群架构:1 主(master)+1 从(slave),通过 StatefulSet 部署(保证固定网络标识和存储)
- 主从同步:基于 GTID(全局事务标识)的复制方式,可靠性更高
- 网络:通过 Headless Service 提供固定 DNS 解析,便于主从通信
- 存储:使用 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:部署集群
-
应用配置文件:
kubectl apply -f mysql-statefulset.yaml
-
查看 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:配置主库复制用户
-
进入主库 Pod(mysql-0):
kubectl exec -it mysql-0 -n mysql-cluster -- bash
-
登录 MySQL:
mysql -uroot -p$MYSQL_ROOT_PASSWORD
-
创建复制用户并授权:
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. 查看从库复制状态
-
进入从库 Pod(mysql-1):
kubectl exec -it mysql-1 -n mysql-cluster -- bash
mysql -uroot -p$MYSQL_ROOT_PASSWORD -
执行:
SHOW REPLICA STATUS\G;
关键检查项:
Slave_IO_Running: YesSlave_SQL_Running: YesLast_IO_Error: 空(无错误)Seconds_Behind_Master: 0(延迟为 0)
3. 数据同步测试
-
在主库创建测试数据库和表:
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'); -
在从库验证数据是否同步:
USE test_db;
SELECT * FROM test_table;
若能查询到数据,说明主从同步正常。
常见问题排查
-
从库 Slave_IO_Running 为 No
- 检查主库地址是否正确(mysql-0.mysql)
- 检查复制用户密码是否正确
- 检查主库 3306 端口是否可访问(
telnet mysql-0.mysql 3306)
-
PVC 创建失败
-
确认集群中有可用的 StorageClass
-
测试环境可改用 hostPath 替换 volumeClaimTemplates: yaml
volumes: - name: data hostPath: path: /data/mysql/$(POD_NAME) type: DirectoryOrCreate(需在 StatefulSet 的 pod 模板中添加
$(POD_NAME)环境变量)
-
-
初始化容器执行失败
- 查看日志:
kubectl logs mysql-1 -n mysql-cluster -c clone-mysql - 检查主库是否正常启动,root 密码是否正确
- 查看日志:
生产环境优化建议
- 增加从库数量(修改 StatefulSet 的 replicas)
- 使用 MySQL Operator(如 Percona XtraDB Operator)简化管理
- 配置监控(Prometheus + Grafana)监控主从延迟、同步状态
- 启用数据加密(TLS)和密码轮换
- 配置备份策略(如定时 mysqldump 或使用 xtrabackup)
- 使用 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 管理配置,通过初始化容器完成主从复制的自动配置。