mysql容器在swarm中必须通过placement.constraints锁定节点,因绑定本地路径的数据卷无法跨节点访问;主从需严格启用gtid与row格式binlog,并通过手动校验和备份同步保障数据一致性。

MySQL 容器必须固定调度到同一节点
Swarm 默认会跨节点调度服务,但 MySQL 数据卷绑定本地路径后,一旦容器被调度到其他节点,就会读不到原数据,直接启动失败或初始化空实例。这不是配置问题,是数据物理位置硬约束。
必须用 placement.constraints 锁定节点:
- 给目标节点打标签:
docker node update --label-add mysql-node=true <node-id></node-id> - 在 stack 文件中声明约束:
placement: { constraints: [node.labels.mysql-node == true] } - 避免使用
replicas: > 1—— 单节点多副本仍会触发跨节点调度,除非显式指定max_replicas_per_node: 1并配合约束
主从切换时 binlog 与 GTID 必须一致
Swarm 本身不感知 MySQL 复制状态,故障转移靠外部脚本或 Consul 健康检查触发。若主节点宕机,新主未正确拉取全部 binlog 或 GTID set 不全,从库同步就会卡住,报错如 Got fatal error 1236 from master 或 Could not execute Write_rows event on table。
关键动作:
- 主库启用
log-bin、binlog-format=ROW、gtid-mode=ON、enforce-gtid-consistency=ON - 从库启动时必须用
CHANGE MASTER TO ... GET_MASTER_PUBLIC_KEY=1(MySQL 8.0.4+ 才支持) - 定期校验
SELECT @@GLOBAL.GTID_EXECUTED在所有节点是否一致,不一致需手动跳过或重置复制
数据迁移不能依赖 Swarm 的 rolling update
docker stack deploy --with-registry-auth 触发滚动更新时,Swarm 会先删旧容器再启新容器,中间存在几秒无主状态,且新容器挂载的是全新卷(除非显式复用旧 volume 名)。这会导致数据丢失或复制链断裂。
安全迁移流程应为:
- 停写:将应用流量切到只读模式(或通过 proxy 拦截写请求)
- 确认从库已追平:
SHOW SLAVE STATUS\G中Seconds_Behind_Master: 0且Slave_IO_Running和Slave_SQL_Running均为Yes - 导出主库:
mysqldump --all-databases --single-transaction --triggers --routines --set-gtid-purged=ON > backup.sql - 导入目标集群:在新主上执行
mysql ,注意跳过 <code>SET @@SESSION.SQL_LOG_BIN=0导致的 GTID 冲突 - 最后才更新 stack 配置并重新部署
备份卷必须跨节点同步,不能只靠本地 volume
Swarm 节点本地 volume 天然不可迁移,docker volume ls 查到的卷只存在于创建它的节点。如果该节点宕机且无备份,数据即永久丢失。
生产必须做两件事:
- 每日自动备份到共享位置:
mysqldump+gzip后rsync到另一台节点的/backup/mysql/目录,或上传至 S3/OSS - 备份脚本里加入校验:
mysqlcheck --all-databases --check确保 dump 文件可还原,避免压缩损坏或权限问题导致恢复失败 - 禁止把
/var/lib/mysql直接 bind mount 到宿主机路径 —— 这会让 Swarm 调度失效,且无法通过docker volume inspect管理生命周期
Executed_Gtid_Set 是否包含 Retrieved_Gtid_Set。











