必须用statefulset而非deployment,因mysql是有状态应用:需固定pod名(如mysql-0)、独立pvc、有序启动、稳定dns;deployment的随机命名、共享pvc、无序扩缩容会导致数据错绑、复制断裂、角色识别失败。

为什么必须用 StatefulSet 而不是 Deployment
MySQL 是典型有状态应用:每个实例需要固定身份、独立存储、有序启动、稳定网络标识。Deployment 的 Pod 名随机、PVC 共享或复用、无启动顺序,直接导致以下问题:
-
mysql-0重建后可能挂载mysql-1的 PVC,数据目录混用,mysqld启动失败或覆盖元数据 - 主从复制依赖
server-id和 binlog position,Pod 无序重建会丢失复制上下文 - Headless Service 解析不到固定 DNS 名(如
mysql-0.mysql.database.svc.cluster.local),从库无法连接主库
StatefulSet 保证:mysql-0 永远用同一 PVC、永远调度到同类型节点(配合 nodeAffinity)、永远先于 mysql-1 启动——这是 MySQL 主从拓扑成立的前提。
StatefulSet 中必须配置的 4 个关键字段
缺一不可,否则集群无法形成主从关系或数据无法持久:
-
serviceName: mysql:必须指向一个 Headless Service(clusterIP: None),否则 Pod 无法通过 DNS 获取彼此地址 -
volumeClaimTemplates:声明每个 Pod 独立的 PVC 模板,不能写死claimName;否则所有 Pod 共享同一 PVC,数据冲突 -
env中避免硬编码密码:用secretKeyRef引用 Secret,例如MYSQL_ROOT_PASSWORD和MYSQL_REPLICATION_USER -
args或configMap注入 MySQL 配置:主库需--log-bin、--server-id=1;从库需--server-id=2、--super-read-only,不能靠镜像默认值
ConfigMap 分发主从配置的正确姿势
不能把主从配置写进同一个文件再靠脚本判断,K8s 原生不支持“按 Pod 序号加载不同 section”。可靠做法是用两个独立键 + 初始化逻辑:
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql-config
data:
master.cnf: |
[mysqld]
log-bin=mysql-bin
server-id=1
gtid-mode=ON
enforce-gtid-consistency=1
slave.cnf: |
[mysqld]
server-id=2
super-read-only=1
gtid-mode=ON
enforce-gtid-consistency=1
然后在容器启动命令中根据 HOSTNAME 判断角色:
- 主库 Pod(
mysql-0)挂载master.cnf到/etc/mysql/conf.d/ - 从库 Pod(
mysql-1)挂载slave.cnf到相同路径,并在initContainers中执行mysql -h mysql-0.mysql.database.svc.cluster.local -e "CHANGE MASTER TO ..."
注意:server-id 必须全局唯一,StatefulSet 的 ordinal(0/1/2)可映射为 server-id,但需在启动脚本中动态生成,不能全写死。
PV/PVC 绑定失败的三个高频原因
常见现象:kubectl get pvc 显示 Pending,kubectl describe pvc 提示 no persistent volumes available for this claim:
- StorageClass 名称不匹配:
volumeClaimTemplates.spec.storageClassName必须与集群中已存在的StorageClass名完全一致(区分大小写),云环境常用standard,本地测试常用local-storage - PV
nodeAffinity错误:HostPath 类型 PV 必须指定nodeSelectorTerms匹配真实节点名(kubectl get nodes输出),写成159m却实际是node-159m就会失败 - accessModes 不兼容:MySQL 要求
ReadWriteOnce,但某些 NFS PV 声明了ReadWriteMany,PVC 请求ReadWriteOnce时无法绑定
最稳妥的调试方式:kubectl get pv,pvc -o wide 对照 STATUS 和 CLAIM 列,再查对应 PV 的 Events。
nodeAffinity 和节点 hostname 对不上,或者 ConfigMap 挂载路径没覆盖到 MySQL 默认读取位置(/etc/mysql/conf.d/)。这些细节不报错,但 MySQL 启动后发现没开 binlog、没设 server-id,主从就彻底断了。











