GitHub Actions 如何在主 self-hosted runner 失败后串行切换到备用 runner?
可以将主、备用执行分别配置为两个 job:主 job 指定 runner-01,备用 job 指定 runner-02。通过 needs 建立依赖,再根据主 job 的结果决定是否执行备用 job。
备用 job 必须等主 job 结束后才能运行。主 runner 离线后,GitHub Actions 可能仍让 job 排队或等待故障确认,因此这个方案不能保证立即切换。备用 runner 也不会自动接管主 runner 的进程或内存状态,任务需要重新执行,或读取任务自行保存的检查点。
为什么 runs-on 不能表达主备顺序?
runs-on: [self-hosted, runner-01, runner-02] 要求同一台 runner 同时具备这三个标签,数组顺序不表示优先级。应给两台机器分别配置唯一的自定义标签 runner-01 和 runner-02;机器显示名称不会自动成为同名标签。标签匹配方式见 GitHub 的 self-hosted runner 使用说明。
如何配置串行重试?
下面假设两台 runner 都能运行 Bash,仓库中已有 scripts/long-task.sh,并且已经安装任务所需的依赖。120 分钟只是示例,需要按任务正常耗时调整。
name: Sequential runner failover
on:
workflow_dispatch:
permissions:
contents: read
jobs:
primary:
runs-on: [self-hosted, runner-01]
timeout-minutes: 120
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Run task on primary runner
shell: bash
run: bash ./scripts/long-task.sh
fallback:
needs: primary
if: ${{ !cancelled() && (needs.primary.result == 'failure' || needs.primary.result == 'cancelled') }}
runs-on: [self-hosted, runner-02]
timeout-minutes: 120
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Retry task on fallback runner
shell: bash
run: bash ./scripts/long-task.sh
这段配置中:
needs: primary让备用 job 等待主 job 完成,建立两次尝试的先后关系。- 条件显式检查主 job 的
failure或cancelled结果;主 job 成功或被跳过时,不执行备用 job。 !cancelled()是状态检查函数,可以覆盖默认的成功条件,并避免在整个 workflow 已被取消后继续发起备用执行。
只写 needs 时,上游失败通常会导致下游跳过;只写普通结果比较而不使用状态检查函数,也可能受到默认 success() 条件的影响。相关行为见 job 依赖规则、状态检查函数和 needs 上下文。
任务脚本必须在失败时返回非零退出码。不要通过 || true 或 continue-on-error 隐藏需要触发重试的错误,否则备用 job 可能无法按预期启动。
主 runner 离线时,为什么没有立即切换?
需要区分两种情况:
- job 尚未开始。没有在线且空闲的 runner 匹配
runner-01时,job 会排队。GitHub 文档说明,self-hosted job 排队超过 24 小时会失败。在此之前,依赖它的备用 job 仍需等待。参见 self-hosted runner 路由规则。 - job 已经开始。执行中断线不一定会立刻变成失败;备用 job 要等 GitHub 将主 job 判定为结束后,才会评估执行条件。
timeout-minutes 用于限制 job 的执行时间,不能当作自定义排队超时,也不能保证断线后在指定时间内完成切换。其定义见 job 超时配置。
如果必须在较短时间内处理离线 runner,需要额外的调度或监控机制。例如,在独立执行环境中监测排队时长和任务心跳,达到阈值后终止原尝试,再启动明确指定 runner-02 的新尝试。仅在执行前检查 runner 是否在线仍有竞态:检查通过后,runner 依然可能断线。
长任务切换时需要注意什么?
needs 保证的是 GitHub Actions job 的依赖顺序。网络中断时,原机器上的任务进程可能仍在运行;如果两次尝试绝对不能同时修改业务数据,就需要任务层面的租约或 fencing token,让旧执行者在失去执行资格后无法继续写入。
重试还应考虑以下问题:
- 重复操作:让任务具备幂等性,或使用稳定的业务任务 ID 去重。
- 中间进度:将检查点保存到备用 runner 可访问的持久存储。主机突然故障时,不能依赖最后一步上传本地文件。
- 执行环境:两台 runner 应具备相同的代码、依赖、配置和必要凭据,不能假设它们共享工作目录。
- 最终状态:示例保留了主 job 的失败结果,即使备用 job 成功,整个 workflow 仍可能显示失败。如果业务要求“任一次成功即整体成功”,需要额外设计结果汇总逻辑。
备注:内容仅供参考。
