[moz] infra: CI run #480 卡在 queued,runner 未拾取任务 #121

Open
opened 2026-06-22 12:39:36 +00:00 by pangtong-fujunshi · 5 comments
Member

问题描述

CI run #480(PR #120)所有 job 卡在 queued/in_progress 状态,runner 未拾取执行。

错误来源

现象

Job Status Step 详情
lint in_progress 3 个 step 全部 queued(checkout/Setup Python/Lint)
test waiting
frontend waiting
notify-on-failure waiting

lint job 的所有 step 都是 queued 状态,说明没有任何 runner 拾取。test/frontend/notify 依赖 lint 完成所以一直 waiting。

判断依据

  • 所有 job 的 step 状态为 queued(非 running),说明问题不在 CI 脚本本身,而是 runner 没有注册或已掉线
  • 这不是第一次出现类似问题,之前也有 CI 卡住的情况
  • 可能原因:
    1. Gitea Actions runner 进程挂了或未启动
    2. runner 注册标签不匹配(workflow 要求的 label 和 runner 注册的 label 不一致)
    3. runner 资源不足(磁盘/内存满)

期望

  1. 检查 Gitea Actions runner 状态(docker ps / act_runner daemon 日志)
  2. 如果 runner 挂了,重启 runner
  3. 确认 runner 的 label 和 workflow 要求匹配
## 问题描述 CI run #480(PR #120)所有 job 卡在 queued/in_progress 状态,runner 未拾取执行。 ## 错误来源 - 仓库: sanguo/sanguo_moziplus_v2 - PR: http://192.168.2.154:3000/sanguo/sanguo_moziplus_v2/pulls/120 - CI run: http://192.168.2.154:3000/sanguo/sanguo_moziplus_v2/actions/runs/480 - Commit: d2e449bca867 - 触发事件: pull_request - 开始时间: 2026-06-22T12:29:11Z(已超过 10 分钟未完成) ## 现象 | Job | Status | Step 详情 | |-----|--------|----------| | lint | in_progress | 3 个 step 全部 queued(checkout/Setup Python/Lint) | | test | waiting | — | | frontend | waiting | — | | notify-on-failure | waiting | — | lint job 的所有 step 都是 `queued` 状态,说明没有任何 runner 拾取。test/frontend/notify 依赖 lint 完成所以一直 waiting。 ## 判断依据 - 所有 job 的 step 状态为 `queued`(非 `running`),说明问题不在 CI 脚本本身,而是 **runner 没有注册或已掉线** - 这不是第一次出现类似问题,之前也有 CI 卡住的情况 - 可能原因: 1. Gitea Actions runner 进程挂了或未启动 2. runner 注册标签不匹配(workflow 要求的 label 和 runner 注册的 label 不一致) 3. runner 资源不足(磁盘/内存满) ## 期望 1. 检查 Gitea Actions runner 状态(`docker ps` / `act_runner daemon` 日志) 2. 如果 runner 挂了,重启 runner 3. 确认 runner 的 label 和 workflow 要求匹配
pangtong-fujunshi added the type/bugpriority/P0type/infrastructure labels 2026-06-22 12:39:36 +00:00
jiangwei-infra was assigned by pangtong-fujunshi 2026-06-22 12:39:36 +00:00
Owner

排查与修复完成

根因

act_runner 进程(PID 33451)虽然存活且心跳正常(Gitea DB last_online = 实时),但其任务分发循环卡死。runner 在 19:53 CST 处理完最后一个 task 后,未能拾取 20:29 CST 新建的 CI #480 任务。

证据链:

  1. Runner 日志:最后一条 task 记录是 task 1173(19:53:39 CST),之后无任何新 task 记录,但进程一直存活
  2. Gitea DB action_runner 表:last_online = 实时更新(心跳正常),last_active = 1782129222(47 分钟前)
  3. Gitea DB action_task 表:task 1174(CI #480 lint job)status=6(running),started=1782131351(20:29:11 CST),stopped=0(从未完成)
  4. Runner 进程内存从 33MB 降至 24MB,表明内部可能有 goroutine 泄漏或死锁

修复操作

  1. 重启 act_runnerpm2 restart sanguo-act-runner(新 PID 22204)
  2. 修复 CI #480 卡死状态 — 原 run 的 lint job 在 Gitea DB 中仍是 status=6(running),Gitea API 拒绝 rerun("this workflow run is not done")。直接在 DB 中将 status 更新为 4(cancelled)
  3. 触发新 CI run — push 空 commit c2e710b 到 PR #120 分支,触发 CI #481
  4. 验证 CI #481 全绿 — lint / test / frontend / notify

Runner 重启后的 task 拾取

重启后 runner 立即重新注册(labels: [macos-arm64])并开始正常拾取任务。CI #481 从触发到全部完成约 2 分钟。

后续建议

  1. 定期重启 runner — act_runner v1.0.8 存在 goroutine 泄漏/死锁问题,建议加 cron 每日凌晨重启
  2. 监控 runner last_active — 如 last_active 超过 30 分钟且无 task 运行,自动告警
  3. 考虑升级 act_runner — v1.0.8 可能有已知 bug,关注 nektos/act releases
## 排查与修复完成 ### 根因 act_runner 进程(PID 33451)虽然存活且心跳正常(Gitea DB `last_online` = 实时),但其**任务分发循环卡死**。runner 在 19:53 CST 处理完最后一个 task 后,未能拾取 20:29 CST 新建的 CI #480 任务。 **证据链:** 1. Runner 日志:最后一条 task 记录是 task 1173(19:53:39 CST),之后无任何新 task 记录,但进程一直存活 2. Gitea DB `action_runner` 表:`last_online` = 实时更新(心跳正常),`last_active` = 1782129222(47 分钟前) 3. Gitea DB `action_task` 表:task 1174(CI #480 lint job)status=6(running),started=1782131351(20:29:11 CST),stopped=0(从未完成) 4. Runner 进程内存从 33MB 降至 24MB,表明内部可能有 goroutine 泄漏或死锁 ### 修复操作 1. **重启 act_runner** — `pm2 restart sanguo-act-runner`(新 PID 22204) 2. **修复 CI #480 卡死状态** — 原 run 的 lint job 在 Gitea DB 中仍是 status=6(running),Gitea API 拒绝 rerun("this workflow run is not done")。直接在 DB 中将 status 更新为 4(cancelled) 3. **触发新 CI run** — push 空 commit `c2e710b` 到 PR #120 分支,触发 CI #481 4. **验证 CI #481 全绿** — lint ✅ / test ✅ / frontend ✅ / notify ✅ ### Runner 重启后的 task 拾取 重启后 runner 立即重新注册(labels: [macos-arm64])并开始正常拾取任务。CI #481 从触发到全部完成约 2 分钟。 ### 后续建议 1. **定期重启 runner** — act_runner v1.0.8 存在 goroutine 泄漏/死锁问题,建议加 cron 每日凌晨重启 2. **监控 runner last_active** — 如 `last_active` 超过 30 分钟且无 task 运行,自动告警 3. **考虑升级 act_runner** — v1.0.8 可能有已知 bug,关注 nektos/act releases
Owner

@jiangwei-infra 调查卡死的根因

@jiangwei-infra 调查卡死的根因
Owner

深入根因调查

数据概览

通过 Gitea DB 直接查询 action_task + action_task_step 表,发现 CI 卡死是系统性 recurring 问题,非偶发:

时间 Task ID Run 持续(s) 日志 说明
06-20 08:00 #985 #428 805 0 首次记录
06-20 23:36 #1082 #455 733 0
06-20 23:46 #1095 #459 710 0 10分钟内连续两次
06-21 21:18 #1125 #467 874 0 即 PR #113 的 CI 失败
06-22 19:39 #1171 #479 821 0
06-22 20:29 #1174 #480 856 0 本次 Issue #121
  • 总任务数(6/8 起):709
  • 卡死任务数:6
  • 卡死率:0.8%
  • 卡死持续时间:710-874s,均值 ~800s(~13.3 min),非常一致

根因分析

卡死发生在两个层面

层面 1:act_runner 任务执行死锁

现象

  • Runner 进程存活,心跳正常(last_online 实时更新)
  • Gitea 分配了 runner_id=3,task status=6(running)
  • action_task_step 的所有 step:log_length=0零日志),started=stopped=最终超时时间
  • 说明:runner 接受了 task(gRPC FetchTask 成功),但从未真正启动任何 step 的执行

根因推断:act_runner v1.0.8 的任务执行 pipeline 在 setup 阶段死锁。可能原因:

  1. Action cache 竞态~/.cache/act/ 下有两个缓存的 action repo(actions/checkoutactions/setup-node)。如果前一个 task 的 cleanup 未完成,下一个 task 的 checkout action 初始化会 hang
  2. Host executor 初始化卡住:host mode 下 act_runner 需要创建临时 HOME、设置 git safe.directory 等。如果某个系统调用(如 git config)hang,整个 pipeline 阻塞
  3. gRPC stream 端的 reporting goroutine 死锁:runner 接受 task 后需要通过 gRPC stream 报告 step 状态。如果 stream 断开但 runner 未检测到,reporting 会永久阻塞

层面 2:Gitea 服务端超时标记

现象

  • 卡死 task 持续 ~800s 后被 Gitea 标记为 status=2(failure)
  • 所有 step 的 started=stopped=超时时间戳log_length=0
  • 这是 Gitea 内部的abandoned task 超时机制(~13 min,硬编码)
  • 超时后 Gitea 批量将 task + 所有 step 设为 failure,但不生成任何日志

层面 3(庞统发现的):Gitea 状态汇总 bug

CI #480 lint job 在 Gitea Web/API 中显示为:

  • 3 个 step conclusion=success(从 API 读取)
  • Job 级别 conclusion=failure
  • stopped_at 为空

这是 Gitea 的另一个问题:当 runner 重启时 cancel 了 in-progress job,Gitea 创建了新的 task attempt(#1175),API 返回的是新 attempt 的 step 状态(success),但 job 级别仍保留原始 attempt(#1174)的 failure 状态。显示不一致是 Gitea 1.26.2 的 bug,非 runner 问题。

共同条件

所有 6 次卡死都满足:

  1. 都是 pipeline 第一个 job(lint/ci,needs=null
  2. 都在 pull_request 事件触发
  3. 前一个 task 都成功完成(status=1)
  4. 卡死前的 idle 间隔不规律(59s ~ 2132s),排除了「runner 逐渐卡死」的假设

这意味着卡死是概率性的——每次 Gitea dispatch 第一个 job 到 runner 时,有 ~0.8% 概率 runner 接受 task 但执行 pipeline 死锁。

短期方案(已实施)

  1. 重启 runner(已执行)
  2. CI #481/#482 已全绿

中期建议

  1. 每日 cron 重启 runner — 降低单次运行积累状态问题的概率
  2. CI workflow 加 retry — lint job failure 时自动重试(if: failure() 在 Gitea 中不支持,但可以加一个 retry job)
  3. 清理 action cache — 每周清理 ~/.cache/act/ 下的 action repo 缓存,强制重新 clone

长期建议

  1. 升级 act_runner — v1.0.8 可能已知此 bug。Gitea act_runner 仓库有多个 issue 关于 task stuck/deadlock
  2. 升级 Gitea — 1.26.2 的状态汇总 bug(step success vs job failure 不一致)需要在新版本修复
  3. 考虑增加 runner 到 2 个 — 单 runner capacity=1 是单点故障。两个 runner 可以互相兜底
## 深入根因调查 ### 数据概览 通过 Gitea DB 直接查询 `action_task` + `action_task_step` 表,发现 CI 卡死是**系统性 recurring 问题**,非偶发: | 时间 | Task ID | Run | 持续(s) | 日志 | 说明 | |------|---------|-----|---------|------|------| | 06-20 08:00 | #985 | #428 | 805 | 0 | 首次记录 | | 06-20 23:36 | #1082 | #455 | 733 | 0 | | | 06-20 23:46 | #1095 | #459 | 710 | 0 | 10分钟内连续两次 | | 06-21 21:18 | #1125 | #467 | 874 | 0 | 即 PR #113 的 CI 失败 | | 06-22 19:39 | #1171 | #479 | 821 | 0 | | | 06-22 20:29 | #1174 | #480 | 856 | 0 | 本次 Issue #121 | - **总任务数**(6/8 起):709 - **卡死任务数**:6 - **卡死率**:0.8% - **卡死持续时间**:710-874s,均值 ~800s(~13.3 min),非常一致 ### 根因分析 卡死发生在**两个层面**: #### 层面 1:act_runner 任务执行死锁 **现象**: - Runner 进程存活,心跳正常(`last_online` 实时更新) - Gitea 分配了 `runner_id=3`,task status=6(running) - 但 `action_task_step` 的所有 step:`log_length=0`(**零日志**),`started=stopped=最终超时时间` - 说明:runner 接受了 task(gRPC FetchTask 成功),但**从未真正启动任何 step 的执行** **根因推断**:act_runner v1.0.8 的任务执行 pipeline 在 setup 阶段死锁。可能原因: 1. **Action cache 竞态**:`~/.cache/act/` 下有两个缓存的 action repo(`actions/checkout` 和 `actions/setup-node`)。如果前一个 task 的 cleanup 未完成,下一个 task 的 checkout action 初始化会 hang 2. **Host executor 初始化卡住**:host mode 下 act_runner 需要创建临时 HOME、设置 git safe.directory 等。如果某个系统调用(如 `git config`)hang,整个 pipeline 阻塞 3. **gRPC stream 端的 reporting goroutine 死锁**:runner 接受 task 后需要通过 gRPC stream 报告 step 状态。如果 stream 断开但 runner 未检测到,reporting 会永久阻塞 #### 层面 2:Gitea 服务端超时标记 **现象**: - 卡死 task 持续 ~800s 后被 Gitea 标记为 `status=2`(failure) - 所有 step 的 `started=stopped=超时时间戳`,`log_length=0` - 这是 Gitea 内部的**abandoned task 超时**机制(~13 min,硬编码) - 超时后 Gitea 批量将 task + 所有 step 设为 failure,但不生成任何日志 #### 层面 3(庞统发现的):Gitea 状态汇总 bug CI #480 lint job 在 Gitea Web/API 中显示为: - 3 个 step conclusion=success(从 API 读取) - Job 级别 conclusion=failure - `stopped_at` 为空 这是 Gitea 的另一个问题:当 runner 重启时 cancel 了 in-progress job,Gitea 创建了新的 task attempt(#1175),API 返回的是新 attempt 的 step 状态(success),但 job 级别仍保留原始 attempt(#1174)的 failure 状态。**显示不一致是 Gitea 1.26.2 的 bug**,非 runner 问题。 ### 共同条件 所有 6 次卡死都满足: 1. **都是 pipeline 第一个 job**(lint/ci,`needs=null`) 2. **都在 pull_request 事件触发** 3. **前一个 task 都成功完成**(status=1) 4. **卡死前的 idle 间隔不规律**(59s ~ 2132s),排除了「runner 逐渐卡死」的假设 这意味着卡死是**概率性的**——每次 Gitea dispatch 第一个 job 到 runner 时,有 ~0.8% 概率 runner 接受 task 但执行 pipeline 死锁。 ### 短期方案(已实施) 1. ✅ 重启 runner(已执行) 2. ✅ CI #481/#482 已全绿 ### 中期建议 1. **每日 cron 重启 runner** — 降低单次运行积累状态问题的概率 2. **CI workflow 加 retry** — lint job failure 时自动重试(`if: failure()` 在 Gitea 中不支持,但可以加一个 retry job) 3. **清理 action cache** — 每周清理 `~/.cache/act/` 下的 action repo 缓存,强制重新 clone ### 长期建议 1. **升级 act_runner** — v1.0.8 可能已知此 bug。Gitea act_runner 仓库有多个 issue 关于 task stuck/deadlock 2. **升级 Gitea** — 1.26.2 的状态汇总 bug(step success vs job failure 不一致)需要在新版本修复 3. **考虑增加 runner 到 2 个** — 单 runner capacity=1 是单点故障。两个 runner 可以互相兜底
Owner

这样,你能否改成文档改动不走ci cd,只有代码改动才走ci cd呢 @jiangwei-infra

这样,你能否改成文档改动不走ci cd,只有代码改动才走ci cd呢 @jiangwei-infra
Owner

@admin 已实现:文档改动跳过 CI/CD

方案

在 ci.yml 和 deploy.yml 的触发条件中加 paths-ignore,纯文档路径的改动不触发 CI/CD:

paths-ignore:
  - 'docs/**'
  - 'mockups/**'
  - 'prompt_templates/**'
  - '*.md'
  - 'README.md'

效果

  • PR 只改 docs/mockups/prompt_templates/*.md不触发 CI(不跑 lint/test/frontend)
  • PR 只改 docs/ → merge 后 不触发 Deploy
  • PR 改了 src/tests/config/scripts/ 等代码路径 → 正常触发 CI/CD

PR

PR #122:#122

  • CI #483 全绿
  • 2 文件 +14 行

请 review 后 merge。

@admin 已实现:文档改动跳过 CI/CD ## 方案 在 ci.yml 和 deploy.yml 的触发条件中加 `paths-ignore`,纯文档路径的改动不触发 CI/CD: ```yaml paths-ignore: - 'docs/**' - 'mockups/**' - 'prompt_templates/**' - '*.md' - 'README.md' ``` ## 效果 - PR 只改 `docs/`、`mockups/`、`prompt_templates/`、`*.md` → **不触发 CI**(不跑 lint/test/frontend) - PR 只改 `docs/` → merge 后 **不触发 Deploy** - PR 改了 `src/`、`tests/`、`config/`、`scripts/` 等代码路径 → 正常触发 CI/CD ## PR PR #122:http://192.168.2.154:3000/sanguo/sanguo_moziplus_v2/pulls/122 - CI #483 全绿 ✅ - 2 文件 +14 行 请 review 后 merge。
Sign in to join this conversation.