mysql-operator是目前最稳妥的迁移路径,因statefulset仅管理pod生命周期和pvc绑定,无法理解mysql语义,无法自动执行reset slave all、mgr选主或gtid同步等关键操作,易导致复制链断裂、seconds_behind_master持续增长、io/sql线程中断及binlog位点丢失等问题。

mysql-operator 是目前最稳妥的迁移路径,跳过它直接用 StatefulSet 会卡在主从链断裂、故障无法自愈、binlog位点丢失这些环节上——不是起不来,而是起得“不对”。
为什么不能只靠 StatefulSet 跑 MySQL?
StatefulSet 只管 Pod 生命周期和 PVC 绑定,它不知道 RESET SLAVE ALL 该什么时候执行,也不知道 MGR 选主失败后要不要降级为单节点读写。物理机上 DBA 手动干的活,在 K8s 里必须由控制器接管,否则滚动更新一次就断复制。
常见错误现象包括:SHOW SLAVE STATUS 显示 Seconds_Behind_Master 持续增长、IO_THREAD 或 SQL_THREAD 突然 NO、新 Pod 启动后报 Could not find first log file name in binary log index file。
- Operator 通过 CRD(如
MySQLCluster)把数据库状态映射成 K8s 原生对象,能被kubectl get mysqlclusters查,也能被 Prometheus 抓指标 -
StatefulSet的updateStrategy: RollingUpdate会逐个重建 Pod,但 MySQL 主从拓扑不能简单“删一个再建一个”,否则复制链断裂 - Operator 初始化时会校验
server_id全局唯一性、binlog_format=ROW、max_allowed_packet≥64M,不满足就拒绝创建
迁移前必须验证的 3 个物理机状态
Operator 不修脏数据,只按声明式配置工作。迁移前不检查,后面 mysql-0 Pod 日志里全是 InnoDB assertion failure 或 GTID_PURGED mismatch。
-
binlog_format必须是ROW(STATEMENT或MIXED在 Operator 环境下易导致复制不一致) - 主库
server_id非零且全局唯一;所有从库server_id不能与主库冲突(Operator 初始化时会校验并拒绝非法值) -
max_allowed_packet≥ 64M,避免 Operator 同步大事务时被截断(尤其含LONGTEXT或BLOB字段的表)
全量数据怎么注入到 Operator 初始化流程中?
Operator 不提供“一键导入”,但支持标准初始化路径:停写 → 导出 → 注入。关键在 initContainer 阶段控制权移交,不是靠后期 exec 进去手动 source。
- 用
mysqldump --single-transaction --routines --triggers导出全量,不要加--master-data(Operator 会自己管理 binlog 位点) - 把 dump 文件挂进 Pod 的
/docker-entrypoint-initdb.d/目录,Operator 启动时自动执行(注意文件权限必须是644,否则被跳过) - 若数据量 >50GB,改用
mydumper并配合myloader并行导入,Operator 的initContainer支持 shell 脚本调用
增量同步怎么接上 GTID 链?
全量导入完不立刻切流,必须先确认 GTID 已对齐,否则后续 DML 会丢或重复。Operator 本身不自动拉取 binlog,需人工介入或脚本驱动。
- 导出时加
--set-gtid-purged=ON,确保 dump 文件头部包含SET @@GLOBAL.GTID_PURGED - 导入后,在目标集群执行
SELECT @@global.gtid_executed;,对比源库SELECT BINLOG_GTID_POS('mysql-bin.000001', 123456789); - 确认一致后,用
CHANGE MASTER TO MASTER_AUTO_POSITION = 1开启基于 GTID 的复制,Operator 通常封装了这个逻辑,但要确认 CRD 中replicationSource字段已正确配置
ignore-table 过、哪些用户权限依赖 mysql.user 表结构、GTID 是否在跨版本升级后被重置过。这些细节不会报错,但会让同步慢半拍、切主失败、甚至凌晨三点连不上。











