CI/CD 常用命令速查表 - CI/CD 流水线调试命令大全
CI 出现 pending、job 无故失败时,第一反应往往是改 YAML 重推,但这通常既慢又不解决根因。更有效的顺序是:确认 Runner 是否在线、标签是否匹配当前 job、并发额度是否用尽;再看失败步骤的实际日志而非最后一行;然后判断本地能否复现,把环境差异逐个消掉。缓存命中率低往往是 key 没包含 lockfile 的 hash 或路径没覆盖真实依赖目录,这类问题看缓存配置即可判断,不必改流水线结构。表里把 GitHub Actions、GitLab CI、Jenkins 三套平台的调试入口分开列清,再加 Docker 多阶段构建与通用实践。遇到挂起先查基础设施状态,遇到失败先看日志,能省掉大半无谓修改。
GitHub Actions 6
gh run list --limit 10gh run view <run-id> --log-failedgh run rerun <run-id> --failedgh run cancel <run-id>gh workflow run deploy.yml --ref maingh api repos/:owner/:repo/actions/runnersGitLab CI 6
gitlab-runner listgitlab-runner verifygitlab-runner rungitlab-ci-lint < .gitlab-ci.ymlglab ci list --per-page 20curl -H "PRIVATE-TOKEN: <token>" "https://gitlab/api/v4/projects/:id/pipelines"Jenkins 5
jenkins-cli build <job> -f -vjenkins-cli console <job> <build>systemctl status jenkinsls -lah /var/lib/jenkins/jobs/<job>/builds/jenkins-cli list-jobsDocker CI 构建 5
docker build -t app:ci --target build .docker build --cache-from=app:base .docker tag app:ci registry/app:$(git rev-parse --short HEAD)docker buildx build --platform linux/amd64,linux/arm64 .docker history app:ci --no-truncRunner 与缓存排查 5
docker ps --filter "name=runner"df -h /var/lib/dockeract -j <job> -W .github/workflows/ci.ymlgitlab-runner exec docker <job>du -sh ~/.cache/pnpm /root/.cache通用模式与最佳实践 6
echo "key=value" >> $GITHUB_OUTPUT${{ secrets.NAME }} / $CI_NAMEneeds: [build] / depends_on: [test]workflow_dispatch: / schedule: / push:rules: / only: / except:artifacts: paths: / expire_in: 1 week命令示例
找出最近失败的流水线并直接拉失败步骤日志
gh run list --limit 10
gh run view <run-id> --log-failed先在列表里确认 run-id 与失败范围,再只取失败步骤的日志,比逐个 job 展开快得多,也更容易看清失败发生在哪条命令上。
输出
STATUS NAME WORKFLOW BRANCH EVENT ID ELAPSED AGE failure CI / build ci.yml main push 1287349921 4m12s 18m === Failed steps === Run gh run view 1287349921 --log-failed Run webapp (build) 4: Run npm ci Error: Process completed with exit code 1.
只重跑失败 job,不动已经成功的部分
gh run rerun <run-id> --failed成功 job 的产物与缓存会继续复用,因此重跑快、结论也干净:只有失败 job 变了,差异就是你的修复。如果失败 job 依赖上游重新生成的产物,就得改用整体重跑。
按需手动触发一次部署,而不是等下一次 push
gh workflow run deploy.yml --ref main该 workflow 必须在配置里声明 workflow_dispatch 触发器,否则命令不会产生任何运行。带上 --ref 明确指定分支,避免在多分支仓库里触发到别的分支。
Runner 排查:在线状态、磁盘水位、容器是否存活
gh api repos/:owner/:repo/actions/runners
docker ps --filter "name=runner"
df -h /var/lib/docker三条命令合起来能解释大部分莫名 pending:runner 离线、标签不匹配、或容器所在磁盘写满导致拉镜像失败。df 的 Use% 逼近 100 时优先清理镜像与旧构建缓存。
输出
{
"total_count": 2,
"runners": [
{ "name": "ci-runner-01", "os": "Linux", "status": "online", "busy": false, "labels": [{"name": "self-hosted"}] },
{ "name": "ci-runner-02", "os": "Linux", "status": "offline", "busy": false, "labels": [{"name": "self-hosted"}] }
]
}
CONTAINER ID IMAGE STATUS NAMES
3f9c1a2b7d10 ci-runner:2024 Up 6 hours runner-controller
Filesystem Size Used Avail Use% Mounted on
/dev/vdb1 100G 98G 2.0G 99% /var/lib/docker常见坑与注意事项
- CI 变量分 protected 与 masked 两类,敏感值必须同时限定环境并勾选 masked;只靠 protected 而未 masked 的值仍可能被打印到日志。
- CI 日志长期留存,任何 echo / print 出去的 token 都等同于已泄露,事后删除日志无效,应立即作废并轮换该凭据。
- 本地用 act 或 gitlab-runner exec 复现时,本机镜像、shell 与工具链版本与线上往往不同;本地通过不代表线上会通过,别据此直接合并。
- docker buildx build --platform 多架构构建后需要推送到远端才生效,本地 --load 装不下多架构镜像;不加推送参数容易白跑一次。
- 在 CI 中用 needs / depends_on 声明依赖时注意,它同时决定执行顺序与产物获取,漏声明会让下游 job 拿不到上游产物而不是报错。
提示
- job 一直 pending 先查 Runner 在线状态和标签匹配,再看并发额度,而不是反复改 YAML。
- 缓存命中率低时检查 key 是否用了 lockfile hash,路径是否覆盖真实依赖目录。
- CI 日志可能长期留存,排查时别用 echo 打印 secrets 或 token。
- Docker 多阶段构建把编译依赖和运行环境分离,可大幅减小最终镜像体积。
- CI 变量分 protected 和 masked,敏感信息务必勾选 masked 防止日志泄露。
- 本地复现 CI 用 act(GitHub Actions)或 gitlab-runner exec,比反复推送省时间。
- GitHub Actions 用 gh run view <run-id> --log-failed 直接拉失败步骤日志,不必逐个 job 点开。
- GitLab CI 用 gitlab-runner exec 配同一份 .gitlab-ci.yml 在本地跑,配置不一致时报错会更明确。
- 缓存 key 里放哈希文件路径而非分支名,改依赖才会失效、只改代码仍能命中缓存。
常见问题
job 一直 pending 不动,为什么不该先去改 YAML?
因为 pending 说明调度器根本没把这个 job 派出去,YAML 此时还没有被执行的机会。先用 gh api repos/:owner/:repo/actions/runners 或 glab ci list 确认 Runner 在线且标签匹配当前 job 的要求,再看并发额度是否已被占满;容器化 Runner 则用 docker ps --filter "name=runner" 确认容器没被驱逐。标签或额度这类基础设施问题,改十遍 YAML 也不会生效。
只有部分 job 失败,为什么要用 rerun --failed 而不是整体重跑?
因为整体重跑会丢掉已经成功且往往最贵的部分。gh run rerun <run-id> --failed 只重跑失败 job,成功的 job 继续复用其产物与缓存,结果既更快也更容易判断问题是否真的修好了。代价是它不会重新计算失败 job 依赖的、需要上游重新产出的东西,所以只有当成功 job 的产物确实可复用时才用这个模式。
缓存命中率低,通常是什么原因?
几乎总是 key 的粒度和路径范围两件事。key 里要放 lockfile 或依赖清单的哈希,而不是分支名,否则改一点代码就整片失效;路径要覆盖真实的依赖目录,key 写对了但 paths 只指到子目录,命中仍是空的。核对办法是看两次运行之间 lockfile 是否变化,再确认缓存配置里的路径和实际安装位置一致。
流水线在本地复现不出来,先确认什么?
先确认复现工具读到的就是同一份配置。act -j <job> -W .github/workflows/ci.yml 显式指定 workflow 文件,gitlab-runner exec docker <job> 则要求本机注册信息与线上 runner 一致,配置不一致时它通常直接报错而不是静默跑偏。本地跑通后若线上仍失败,剩下的差异基本都在环境上:镜像里的系统依赖、环境变量和服务可达性。
为什么排查时不能把 token 打到 CI 日志里?
因为 CI 日志的可见范围通常远大于你以为的:失败步骤的日志会被长期留存,任何能读仓库或读构建记录的人都可能看到,而且一旦进入日志就等于已经泄露,事后删除无效。所以敏感值要放在变量里并勾选 masked,需要临时排查时只打印变量名或前几位,绝不打印值本身。
由 巧匠 维护
公开更新于 2026年7月21日,内容持续校对官方文档。