单节点Docker Swarm global mode更新时服务副本超标及MySQL无故更新问题
Docker Swarm 问题排查与解决方案
问题1:MySQL无代码变更仍被触发更新
根本原因
- 执行
docker image prune -a会删除所有未被运行中容器使用的本地镜像,包括之前构建的MySQL镜像,导致下次构建时无缓存可用,即便代码没有变更,也会重新构建所有镜像层 - 统一使用
latest标签标记镜像,Docker Swarm执行stack deploy时默认会校验镜像的摘要值(digest),重新构建推送的镜像摘要值会发生变化,Swarm就会判定为镜像更新,触发MySQL容器重建 - MySQL Dockerfile中使用了ARG参数构建,若CI环境中UID/GID存在动态变化,也会导致构建层不缓存,每次生成新镜像
解决方案
- 替换
latest标签为唯一版本标识,比如使用Git提交哈希、CI构建号作为标签(如registry.address/db:${CI_COMMIT_SHORT_SHA}),只有当MySQL相关代码变更时才生成新的版本标签 - 修改镜像清理命令,去掉
-a参数,仅清理悬空镜像:docker image prune -f,保留已打标签的本地镜像复用构建缓存 - CI流程中增加变更校验逻辑,仅当MySQL关联文件(
db-prod.Dockerfile、init目录下的配置/初始化脚本)发生变更时,才执行MySQL镜像的构建和推送,否则直接跳过该步骤
问题2:依赖服务副本数超标、旧副本不销毁
根本原因
- 依赖服务(如core)的更新策略配置为
order: start-first,Swarm会先启动新副本,等待新副本健康检查通过后再销毁旧副本 - MySQL更新时采用
stop-first策略,会先停止旧的MySQL容器再启动新容器,期间数据库完全不可用,导致依赖服务的新副本启动后无法连接数据库,健康检查持续失败 - 配置了
failure_action: pause,当新副本健康检查失败次数超过阈值后,Swarm会直接暂停更新流程,不会自动销毁旧副本,等后续数据库恢复、新副本健康检查通过后,已暂停的更新流程也不会自动恢复,最终出现新旧两个副本同时运行的情况
解决方案
- 调整更新配置参数
- 给依赖服务的
deploy.update_config增加monitor: 120s(更新后监控时长),同时将健康检查的start_period调整为120s以上,预留足够时间等待数据库恢复,避免误判更新失败 - 增加
rollback_config配置,更新失败后自动回滚,避免流程卡住:
rollback_config: order: stop-first failure_action: continue - 给依赖服务的
- 优化服务启动逻辑
- 在依赖服务的启动脚本中增加数据库就绪检测逻辑,轮询数据库端口直到连接成功后再启动业务服务,避免服务提前启动导致健康检查失败
- 调整CI部署顺序
- 部署时先单独更新数据库服务,通过
docker service inspect --format '{{.UpdateStatus.State}}' test_db命令校验数据库更新完成、状态为completed且健康检查通过后,再部署其他依赖服务
- 部署时先单独更新数据库服务,通过
- 可选调整:如果对业务零停机要求不高,可以将依赖服务的更新策略
order改为stop-first,先销毁旧副本再启动新副本,从根源上避免双副本同时存在的问题
内容的提问来源于stack exchange,提问作者Pepsko
相关产品推荐
相关产品推荐

