Kubernetes 不适合直接运行生产级 MySQL 单实例;应优先选用 mysql-operator(适合熟悉 Operator 的团队)或 bitnami/mysql Helm Chart(快速启动,基于 StatefulSet);必须使用支持 ReadWriteOnce 的网络存储,禁用 emptyDir/hostPath;需手动配置 NetworkPolicy 和端口对齐。

直接上结论:Kubernetes 本身不适合直接跑生产级 MySQL 单实例,更不该用 Deployment + 普通 PersistentVolume 硬凑——数据一致性、主从切换、备份恢复、扩缩容都会出问题。真要快速搭个可用的 MySQL 环境(比如开发/测试),必须绕过“手写全套 YAML”的坑,优先用成熟 Operator 或轻量封装方案。
用 mysql-operator 还是 bitnami/mysql?选哪个取决于你敢不敢碰 CRD
如果你团队已熟悉 Operator 模式、有基础 RBAC 和 CRD 管理能力,mysql-operator(如 Oracle 官方或 Presslabs 版)是更可控的选择;它把 MySQLCluster 当成一等公民,自动处理初始化、主从探活、PVC 绑定、配置热更新。但它的 CRD schema 复杂,spec.backup.enabled 默认关,不显式配 backupSchedule 就没有定时备份。
如果只想 5 分钟起一个可连的 MySQL,bitnami/mysql Helm Chart 是更现实的入口:
- 它用
StatefulSet而非Deployment,保证 Pod 名固定、网络标识稳定 - 默认启用
initContainer做权限修复(避免因 root 用户写入 PVC 导致 mysqld 启动失败) -
values.yaml里改auth.rootPassword和primary.persistence.size就能跑,不用碰 PV 手动绑定逻辑 - 注意:它默认不开启 binlog,要主从或 GTID 必须加
primary.configuration自定义 my.cnf 片段
StatefulSet 的 headless Service 和 volumeClaimTemplates 到底怎么联动
这是最容易配错的一环:MySQL 主节点必须有稳定 DNS 名(如 mysql-0.mysql-headless.default.svc.cluster.local),而从节点要能靠这个名连主库。这依赖两个东西同时生效:
-
serviceName字段必须指向一个 headless Service(clusterIP: None),否则 DNS 不返回每个 Pod 的独立 A 记录 -
volumeClaimTemplates名称必须和容器内mountPath对应,比如模板叫data,容器就得挂载到/bitnami/mysql/data(bitnami 镜像路径)或/var/lib/mysql(官方镜像路径),错一个字就启动失败报Permission denied - StorageClass 必须支持
ReadWriteOnce,且底层存储(如 NFS、Ceph RBD、AWS EBS)要真正支持单节点读写——很多本地 PV 或 hostPath 在多节点集群里会静默失败
为什么不能用 emptyDir 或 hostPath 跑 MySQL
因为它们根本不符合 MySQL 的持久化语义:
-
emptyDir:Pod 重启即丢数据,mysqld crash 后 recovery log 找不到,大概率进crash loop并报InnoDB: Database page corruption -
hostPath:节点故障时 Pod 被调度到新机器,hostPath路径不存在,启动卡在Waiting for source of /var/lib/mysql;即使路径存在,文件属主(uid 1001)也常和新节点上运行的容器用户不匹配,导致chown: changing ownership of '/var/lib/mysql': Operation not permitted - 真正的解法只有两条:用网络块存储(EBS/GP3、Azure Disk、Ceph RBD)配
PersistentVolume,或者用支持动态供给的 StorageClass 配PersistentVolumeClaim
最常被忽略的点:Operator 或 Helm Chart 都不会自动帮你开防火墙或配 NetworkPolicy。MySQL 默认监听 3306,但 Kubernetes Service 的 targetPort 如果没对齐容器实际暴露端口(比如镜像改了 my.cnf 把 port 设成 3307),连接就会超时,错误信息只显示 connection refused,看不出是端口错还是服务根本没起来。










