StatefulSet 必须用 headless Service,因其需稳定网络标识(如 mysql-0.mysql-headless.default.svc.cluster.local),而 headless Service(clusterIP: None)支持 DNS 直接解析各 Pod 的独立 A 记录,普通 ClusterIP 会负载均衡导致无法唯一寻址。

StatefulSet 为什么必须用 headless Service
因为 StatefulSet 依赖稳定的网络标识(如 mysql-0.mysql-headless.default.svc.cluster.local),而普通 ClusterIP Service 会做负载均衡、隐藏后端 Pod IP,导致每个 Pod 无法被唯一寻址。headless Service(即 clusterIP: None)才允许 DNS 直接解析出每个 Pod 的独立 A 记录。
常见错误现象:mysql-0 启动后反复 CrashLoopBackOff,日志里出现 Can't find hostname mysql-0 或 getaddrinfo failed —— 很大概率是 Service 没配成 headless,或者 DNS 解析没生效。
- Service 的
metadata.name必须和 StatefulSet 的serviceName字段严格一致(比如都叫mysql-headless) - DNS 域名格式固定为:
,不能少字段或拼错. . .svc.cluster.local - 确认 CoreDNS 正常运行:
kubectl -n kube-system get pods -l k8s-app=kube-dns
MySQL 实例间如何自动发现主从角色
StatefulSet 本身不处理 MySQL 主从编排,必须靠外部逻辑判断谁当主库、谁连谁。最轻量的做法是:利用 Pod 序号(statefulset.kubernetes.io/pod-name 或 $HOSTNAME)约定规则,例如 mysql-0 固定为主库,其余为从库并自动 CHANGE MASTER TO mysql-0。
容易踩的坑:直接在容器启动脚本里写死 CHANGE MASTER TO MASTER_HOST='mysql-0' 是可行的,但若主库崩溃重建,新 mysql-0 的 binlog position 和 GTID 已重置,从库会报错 Could not find first log file name in binary log index file。
- 主库必须启用
log-bin和server-id=1;从库设不同server-id(如用$HOSTNAME后缀生成) - 从库启动前需等待主库 mysqld 就绪(用
mysqladmin ping轮询,别只靠 readinessProbe) - GTID 模式下,从库首次同步建议用
SET GLOBAL gtid_purged = '...'补齐主库 purged 集合,否则START SLAVE失败
PVC 拓扑绑定失败导致 Pod 卡在 Pending
StatefulSet 每个 Pod 绑定独立 PVC,而云厂商的存储类(如 AWS EBS、Azure Disk)默认不支持跨可用区挂载。如果调度器把 mysql-1 调度到和 mysql-0 不同的 AZ,且 PVC 已绑定到前者所在 AZ 的磁盘,则后者无法挂载,状态卡在 Pending。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
典型错误信息:Unable to attach or mount volumes: unmounted volumes=[data], unattached volumes=[data default-token-xxx]: timed out waiting for the condition。
- StorageClass 必须设置
volumeBindingMode: WaitForFirstConsumer,确保 PVC 绑定延迟到 Pod 调度之后 - StatefulSet 的
podManagementPolicy设为OrderedReady(默认值),避免并行创建引发跨区冲突 - 检查节点标签是否含
topology.kubernetes.io/zone,并确认 StorageClass 的 allowedTopologies 匹配集群实际 AZ 分布
滚动更新时如何避免主从切换中断写入
StatefulSet 默认滚动更新会按序号从高往低删 Pod(如先删 mysql-2),但如果 mysql-0 是主库,它最后更新,中间过程看似安全。问题在于:更新过程中,旧主库可能还在服务,新主库尚未就绪,客户端连接未及时切走,或者从库 IO/SQL 线程断开后没自动重连。
真实风险点不是“能不能更新”,而是“更新期间业务是否感知到主库不可写”。尤其当应用直连 mysql-0 而非通过中间件路由时,风险极高。
- 更新前手动执行
SET GLOBAL super_read_only=ON+FLUSH TABLES WITH READ LOCK(仅限短时维护,勿长期启用) - readinessProbe 必须检查
SELECT @@read_only,确保从库真正只读、主库可写,避免流量误打到只读实例 - 客户端连接池应配置
autoReconnect=true和合理超时,而非依赖 Kubernetes 的 endpoint 刷新速度
StatefulSet 提供的是稳定身份和存储,不是 MySQL 高可用的全部。主从切换、故障检测、数据一致性校验这些事,得靠额外组件(如 Orchestrator、Vitess)或自建脚本兜底——别指望一个 YAML 文件搞定所有。










