复制 ECS 后云效 Flow 部署串机:Runner 身份被一并复制
复制一台正在承担部署任务的 ECS 后,云效 Flow 开始把制品随机部署到原机器或复制机。两台机器的 ECS 实例 ID 不同,但在 Flow 看来,它们使用的是同一个 Runner 身份。
最后的止血动作很简单:停止并禁用复制机上的 Runner。真正值得记录的是,为什么复制磁盘会复制部署身份,以及 Flow Runner 到底怎样领取和执行任务。
现象
原 ECS 承担前端发布。复制实例启动后,流水线构建仍然成功,但主机部署阶段不再稳定:
1 | |
仓库里的 Webhook 脚本只负责触发流水线,并不携带 ECS 地址:
1 | |
因此问题不在业务代码或 Webhook,而在主机部署阶段使用的 Runner。
现场证据
两台 ECS 的真实实例 ID 不同:
1 | |
但两台机器上存在完全相同的 systemd 服务:
1 | |
Runner 配置位于:
1 | |
配置结构包含:
1 | |
关键证据有两个:
- 两台机器的
config.ymlSHA-256 完全相同。 - 复制机配置中的
instanceId仍然是原 ECS,而不是复制机自己的实例 ID。
这说明 ECS 复制过程把 Runner 的 UUID、Token、租户和实例绑定信息一起复制了。hostname 相同只会增加运维混淆,真正导致串机的是 Runner 注册身份相同。
Flow Runner 怎么工作
云效官方将 Runner 定义为安装在构建机或部署主机上的本地服务。它通过长轮询从 Flow 服务端拉取任务,再同步执行状态和日志。流水线 Runner
可以把它理解成一个常驻任务 Worker:
| Flow 概念 | 后端开发中的对应概念 |
|---|---|
| Flow 服务端 | 调度中心 |
| 主机组 | Worker 分组和部署范围 |
| Runner | 常驻任务消费者 |
| Runner UUID、Token | Worker 身份和凭证 |
| Build UUID | 单次任务实例 ID |
| Step | 任务中的执行步骤 |
| Shell Executor | 本机任务执行器 |
现场使用的二进制自报版本为 v0.3.0,支持 register、install、run、exec 和 upgrade 等命令。systemd 启动 runner run 后,它会持续执行下面的链路:
1 | |
一次任务在本机留下的结构大致如下:
1 | |
当前主机部署任务的实际动作也很直接:创建制品目录、下载压缩包、生成用户命令脚本、解压到站点目录,然后把退出码和日志回传给 Flow。
主机组决定部署范围。Flow 可以把上游制品下载到主机组中的机器,并支持分批发布;YAML 流水线则通过 machineGroup 指定主机组。流水线关键概念
为什么会随机部署
复制完成后,实际形成了两个使用相同身份的消费者:
1 | |
Flow 服务端如何处理重复 Runner 身份没有公开实现,无法从源码确认;但两台机器的配置完全相同、日志显示它们使用相同 Runner 标识轮询,而且部署记录确实分别落到两台机器,已经足以确认故障链路。
修复与验证
复制机暂时不需要参与 Flow 部署,因此直接停止并禁用它的 Runner:
1 | |
验证结果:
1 | |
后续流水线重新稳定部署到原 ECS,说明止血动作与根因一致。
停用不等于彻底清理
disable --now 只停止进程并取消开机启动,复制机磁盘中仍然保留原 Runner Token 和配置。有人重新启用该服务,问题还会复发。
长期处理应根据复制机用途二选一:
- 不需要 Flow:卸载复制过来的 Runner,并删除对应注册配置。
- 需要独立部署:清理旧 Runner,再从 Flow 控制台重新接入,让它获得新的 UUID、Token 和实例绑定,并加入独立主机组。
阿里云的主机部署故障文档也建议:通过镜像生成的新主机,不应继续使用复制的代理身份,应卸载后重新添加。主机部署问题
以后复制 ECS 或制作系统镜像时,应该额外检查这些有注册身份的常驻代理:
- CI/CD Runner
- GitLab Runner
- 监控和日志采集 Agent
- 服务注册 Agent
- 节点证书和机器级 Token
这类组件不是普通软件包。复制磁盘不仅复制了程序,也可能复制它在外部控制平面中的身份。
- 写正文
- 本地预览:
npm run server - 发布:
bash bin/publish.sh -m "post: ..."