CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
GitHub Actions CI/CD/课时/Jobs 与运行器
RawBlame
课时目录12 节

GitHub Actions CI/CD

  1. 01CI/CD 与 GitHub Actions
  2. 02Workflow 文件结构
  3. 03Jobs 与运行器
  4. 04Steps 与 Actions
  5. 05Secrets 与环境
  6. 06依赖缓存
  7. 07构建 Spring Boot
  8. 08构建 Next.js platform
  9. 09构建推送镜像
  10. 10部署策略
  11. 11PR 检查与分支保护
  12. 12综合练习

Jobs 与运行器

runs-on、needs、job 并行与串行、matrix 入门。

30 分钟

Jobs 与运行器

这一课你将能做到

  1. 配置多个 job,理解默认并行与用 needs 串行的差别,并让部署 job 挂在测试 job 之后
  2. 解释 runner 是「一次性虚拟机」:为什么每个 job 都要重新 checkout、装工具
  3. 写一个入门级 strategy.matrix,用同一套步骤测两个 Node(或 Java)版本

为什么要学

一个 workflow 里往往不止一件事:本站要测 backend 与 platform。若塞进同一个 job 的一长串 step,一边失败难看清边界,也无法并行省时间。

Job 是「一台 runner 上的一组 steps」;多 job 默认各开一台机器一起跑。需要「先构建再部署」时,用 needs 排依赖。搞不清并行/串行时,人要么白白串行浪费时间,要么在没产物的情况下就去部署。

理解 runner 原理还有一层面试价值:「你们的 CI 跑在什么机器上?为什么每次都要重新装依赖?」这类问题考的就是对临时虚拟机生命周期的理解。

先建立图像

workflow 被触发
        │
        ├─ job: backend    ── runs-on ubuntu ── steps…
        │                     (独立机器 A)
        │
        └─ job: platform   ── runs-on ubuntu ── steps…
                              (独立机器 B,默认同刻开跑)

若 platform 写了 needs: [backend]
则:backend 成功 → 才启动 platform;backend 失败 → platform 直接 skip

人话:每个 job 像一条独立厨房线。默认两条线同时炒菜;写了 needs 就是「头菜出锅后才能做主菜」。

概念人话
runner执行 job 的虚拟机(托管或自建)
runs-on选哪类机器镜像
needs本 job 依赖哪些 job 成功
matrix同一 job 模板 × 多组变量 = 多次运行
timeout-minutes防卡死的保险丝

跟着做

步骤 1:写两个并行 job

在 .github/workflows/ci.yml 中:

name: CI

on:
  workflow_dispatch:

permissions:
  contents: read

jobs:
  backend:
    runs-on: ubuntu-latest
    steps:
      - name: 假装测 Java
        run: echo "backend ok"

  platform:
    runs-on: ubuntu-latest
    steps:
      - name: 假装测 Node
        run: echo "platform ok"

手动跑一次:Actions 图上应看到 两个 job 几乎同时开始(或短间隔内都变绿)。

步骤 2:加上 needs 看串行

改 platform:

  platform:
    runs-on: ubuntu-latest
    needs: backend   # backend 成功后才跑
    steps:
      - run: echo "platform after backend"

再跑:backend 先完成,platform 后启动。故意让 backend 失败(run: exit 1):platform 应被跳过(skipped)。笔记写:「失败会沿 needs 传播。」

想让失败后仍执行某 job(比如收集日志),用:

  report:
    needs: [backend]
    if: always()   # 无论成败都跑
    steps:
      - run: echo "收尾清理"

步骤 3:对照 monorepo 真实切分

本站推荐心智模型(后续课填真实命令):

jobs:
  backend:
    runs-on: ubuntu-latest
    # checkout → setup-java → ./mvnw verify
  platform:
    runs-on: ubuntu-latest
    # checkout → setup-node → npm ci → lint/test/build

二者无 needs 时并行,总墙钟时间接近「较慢的那一侧」,而不是两者相加。

步骤 4:试一次 matrix(Node)

jobs:
  platform-matrix:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false   # 一腿挂了,其它版本继续跑完
      matrix:
        node: [20, 22]
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node }}
      - run: node -v

应看到两个子 run(node 20 / 22)。Java 可用 java: [17, 21] 配 setup-java 同理。

步骤 5:理解「干净机器」含义

每个 job 默认不共享上一个 job 的磁盘——runner 是临时虚拟机,跑完即销毁。因此每个需要代码的 job 都要自己 actions/checkout;需要把文件交给别的 job,用 upload-artifact / download-artifact。

口头发一句:「并行省时间,但不要假设另一 job 的 target/ 还在。」

步骤 6:加保险丝与防抖

  backend:
    runs-on: ubuntu-latest
    timeout-minutes: 15   # 卡死自动取消,不白烧分钟
    steps:
      - run: echo "..."

workflow 顶层可加 concurrency,同一分支的新 push 自动取消还在跑的旧 run:

concurrency:
  group: ci-${{ github.ref }}
  cancel-in-progress: true

讲清楚要点

  1. 因为每个 job 在独立 runner 上,所以默认互不共享工作区——需要代码就各自 checkout。
  2. 因为无 needs 时 job 并行,所以 monorepo 两侧检查很适合拆成两 job——墙钟更短、失败域更清晰。
  3. 因为 needs 表达依赖,所以「先测后发布」用串行——而不是把部署塞进测试 step 末尾含糊带过,意图可读、失败可跳。
  4. 因为 matrix 是「模板 × 变量」,所以多版本兼容用 matrix——而不是复制粘贴两份几乎相同的 job,改步骤只改一处。
  5. 因为 runner 是短暂的,所以装在机器上的工具要靠 setup action 或自己安装——不要假设机器上有你本机 IDE 里那套 JDK。
  6. 因为失败默认使依赖方 skip,所以修 CI 时先看最早红的那个 job——从根因修起,别逐个重跑。

你可能会懵的地方

  1. 「为什么两个 job 都要 checkout?太浪费?」 它们是两台机器。checkout 很快;硬塞成单 job 反而失去并行与清晰失败点。大产物用 artifact 传递。

  2. 「ubuntu-latest 具体是哪个版本?」 会随 GitHub 滚动升级。需要可重复时锁定如 ubuntu-24.04。面试可答:latest 方便但有漂移风险,生产流水线建议钉具体版本。

  3. 「matrix 失败一个算不算整个 workflow 失败?」 默认任一矩阵腿失败,job 失败(fail-fast 还会取消其余腿)。配 fail-fast: false 让其它版本跑完,便于一次看清兼容面。

  4. 「自托管 runner 什么时候用?」 要访问内网、要特殊硬件(GPU)、或要控成本时。代价是要自己打补丁、管安全——教学主路径用托管 runner 即可。面试若被问「runner 架构」,答:runner 是监听任务分配的代理进程,托管的由 GitHub 生命周期管理,自托管的由你负责更新与隔离。

  5. 「job 之间怎么传数据?」 磁盘不共享。小数据用 job outputs(声明 outputs + GITHUB_OUTPUT),文件用 artifact。跨 job 直接读路径是必踩的坑。

错法对照

错法问题正解
全部步骤塞一个 job 串行测两边慢、日志难拆backend / platform 两 job 并行
部署 job 不写 needs: [ci]测试红了仍可能部署needs 挂上检查 job
matrix 复制成两个几乎相同的 job双倍维护strategy.matrix
以为 job 之间能直接读对方 ./target磁盘不共享artifact 或重新构建
没设 timeout-minutes卡死白烧额度每个长 job 加超时

卡点急救

  1. job 一直停在 Queued:托管 runner 短时繁忙或私有仓库额度耗尽(查 Billing);自托管 runner 则查代理进程是否在线、标签是否与 runs-on 匹配。
  2. 后续 job 显示 skipped 而不是 failed:说明 needs 的上游 job 失败了,失败沿依赖链传播。去展开上游 job 找红 step;若确实需要失败也执行(如通知),加 if: always()。
  3. matrix 某一腿红导致整体红,但看不到其它腿结果:默认 fail-fast 会取消其余腿。调试兼容性时加 strategy.fail-fast: false,让所有版本跑完一次看全。

面试常问

  • GitHub Actions 的 runner 是什么?和 Jenkins agent 有什么区别? runner 是真正执行 job 的虚拟机,job 结束即销毁,环境每次全新。托管 runner 由 GitHub 维护镜像与升级,按分钟计费;Jenkins agent 通常是长期存在的节点,需要自己维护插件与环境一致性,容易积累「节点漂移」。所以 Actions 的答案更干净:环境不可变,构建可重复。

  • 如何缩短 CI 总耗时? 分层答:① 并行化——独立模块拆 job;② 缓存——依赖缓存(Maven/npm)、Docker 层缓存;③ 过滤——paths 只跑改动侧,concurrency 取消过时 run;④ 分级——PR 只跑快速检查,全量验证放 main 或夜间任务;⑤ matrix 控制规模,不为凑数多测版本。结合本站:backend 与 platform 并行、两侧各开依赖缓存、文档改动不触发 CI。

  • needs 的失败传播规则是什么?怎么让失败后也跑清理 job? 默认 needs 的上游失败,下游直接 skipped;想失败也执行用 if: always()(无条件跑)或 if: failure()(仅失败时跑)。这个细节常用来做「测试失败自动收集日志」的模式。

自测三问

  1. 两个 job 都不写 needs 时,执行顺序通常怎样?
  2. 为什么每个 job 常常都要 actions/checkout?
  3. matrix 解决什么问题?举一个 Node 或 Java 的例子。

小结一句话

job = 一台临时 runner 上的一组 steps;默认并行,用 needs 串行,用 matrix 做多版本。

延伸

  • Using jobs in a workflow
  • Using a matrix for your jobs
  • 下一课:Steps 与 Actions——job 里面真正干活的原子单元。
加载本节练习…
← 上一课Workflow 文件结构下一课 →Steps 与 Actions