写在前面:为什么必须掌握这三种模式
当你从「能跑的流水线」走向「可维护的流水线」,一定会撞上三堵墙:
- 样板代码爆炸 ------每个仓库都重复写
image / cache / before_script; - Monorepo / 微服务 Pipeline 巨型化 ------一个
.gitlab-ci.yml上千行,Job 列表翻不到底; - 组合爆炸的重复 Job------多版本 × 多环境 × 多租户,手写等于自杀。
include、trigger、matrix 正是 GitLab 官方为这三堵墙准备的「正规解」。本文不讲入门,直接给你原理 → 可运行示例 → 大厂实践 → 面试官追问 四层结构。读完你不仅能用,还能讲清楚什么时候不该用。

一、include:把「交付规范」下沉到模板库
1.1 独到见解:include 不是拆分文件,而是组织能力的分水岭
很多团队把 include 当成「文件太长拆一段」,这是误解。include 的本质是职责分离:
- 业务仓库只声明 「我要什么」(我是 Node 服务、要构建、要跑单测);
- 模板库决定 「怎么做」(用哪个镜像、怎么缓存、怎么跑 Sonar、怎么推镜像)。
能做到这一层,CI 才从「每个项目的负担」变成「平台提供的能力」------这也是大厂 CI 平台化的第一步。
1.2 四种 include 方式(一张表记牢)
| 类型 | 适用场景 | 示例 |
|---|---|---|
local |
同项目内拆分,私有模板 | include: { local: '/ci/base.yml' } |
project |
跨项目模板库(最常用) | include: { project: 'ci/templates', ref: 'v1.2.0' } |
remote |
公网 / 内网固定 URL | include: { remote: 'https://.../base.yml' } |
template |
GitLab 内置模板 | include: { template: 'Node.gitlab-ci.yml' } citation:GitLab Docs --- include |
🔑 关键习惯 :
project方式务必锁ref(tag 或 commit),禁止跟踪main。否则模板库一次提交,全公司流水线集体暴动。
1.3 推荐目录结构(企业级模板库)
text
ci-templates/
├── base/
│ ├── node.gitlab-ci.yml # Node 通用:image/cache/before_script
│ ├── java.gitlab-ci.yml # Maven / Gradle 通用
│ └── docker.gitlab-ci.yml # 构建 & 推送镜像
├── rules/
│ ├── branch-rules.yml # 分支触发规则(rules 集合)
│ └── manual-prod.yml # 生产手动卡点
└── scripts/
└── sonar-scan.sh
1.4 可运行示例:业务仓库被压缩到极致
模板库 ci-templates/base/node.gitlab-ci.yml:
yaml
.node-base:
image: node:18
cache:
key: ${CI_COMMIT_REF_SLUG}
paths: [node_modules]
before_script:
- npm ci --cache ~/.npm
业务仓库 .gitlab-ci.yml:
yaml
include:
- project: 'ci-templates'
ref: v1.2.0
file: '/base/node.gitlab-ci.yml'
stages: [build, test]
build:
extends: .node-base
stage: build
script: [npm run build]
artifacts:
paths: [dist]
test:
extends: .node-base
stage: test
script: [npm run test:ci]
✅ 效果 :业务仓库完全不出现 image / cache / before_script,只关心业务命令。新人加项目,10 行搞定 CI。
📌 组合技 :
include(文件级复用)+extends(Job 级继承)是标准搭配。include把模板拉进来,extends把模板里 hidden job(.node-base)的能力混入当前 job。
二、trigger:把一个巨型 Pipeline 拆成多个自治 Pipeline

2.1 独到见解:trigger 解决的是「解耦」,不是「并行」
很多人把 trigger 当「并行加速工具」,其实 needs 才是 Job 级 DAG 并行 。trigger 的真正价值是:
- 边界解耦:子流水线在独立上下文跑,变量、Runner、权限可以隔离;
- 规模治理:主仓库不再堆上千个 Job;
- 动态生成:根据本次变更 / 外部配置,运行时决定要跑哪些子流水线。
一句话:needs 是 Job 级依赖图,trigger 是 Pipeline 级解耦------这是面试高频考点。
2.2 三种使用姿势
姿势一:父子流水线(最常用)
yaml
microservice-a:
stage: trigger
trigger:
include: ci/microservice-a.yml
strategy: depend # 父 Pipeline 等待子 Pipeline 结果
strategy 二选一:
depend:父 Pipeline 会等子 Pipeline 跑完并继承其结果(子失败 → 父失败);none:触发即返回,不等结果,适合「通知下游」。
姿势二:动态生成子流水线 ⭐(重点)
yaml
generate-pipeline:
stage: generate
script:
- |
cat <<EOF > child.yml
stages: [test]
test_job:
stage: test
script: echo "Running dynamic pipeline"
EOF
artifacts:
paths: [child.yml]
dynamic-child:
stage: trigger
trigger:
include:
- artifact: child.yml
job: generate-pipeline
strategy: depend
典型场景:
- Monorepo 用
git diff算出变更目录,只触发受影响的微服务; - 根据接口定义自动生成测试流水线;
- 性能测试按 TPS 目标动态生成 JMeter 并发模型。
姿势三:跨项目 Pipeline
yaml
deploy-prod:
stage: deploy
trigger:
project: infra/deploy-prod
branch: main
三、matrix:笛卡尔积式并行

3.1 独到见解:matrix ≠ 并发,是「Job 膨胀」
这是最容易误会的点。matrix 的每一个组合 = 一个独立的 Job = 占用一个独立 Runner slot = 真正并行。
所以「并行度」不等于「并发数」:你定义了 6 个 matrix Job,如果 Runner 只有 2 个 executor,那也是排队跑。matrix 决定的是 Job 数量,真实并发由 Runner 容量决定。
3.2 基础示例:多版本测试
yaml
test:
stage: test
image: node:${NODE_VERSION}
parallel:
matrix:
- NODE_VERSION: ["16", "18", "20"]
OS: ["ubuntu", "alpine"]
script:
- node -v
- npm run test
➡ 生成 3 × 2 = 6 个 Job,全部并行。
3.3 高阶:接口自动化 × 多环境 × 多账号
yaml
api-test:
stage: test
parallel:
matrix:
- ENV: ["dev", "test"]
TENANT: ["A", "B"]
script:
- echo "Test $ENV / $TENANT"
- npm run test -- --env=$ENV --tenant=$TENANT
3.4 matrix + include(大厂常用)
yaml
include:
- local: '/ci/test-base.yml'
test-matrix:
extends: .test-base
parallel:
matrix:
- SERVICE: ["user", "order", "pay"]
四、三者组合拳:真实大厂案例
4.1 Monorepo + 多服务 + 多版本测试
yaml
include:
- project: 'ci-templates'
ref: v1.2.0
file: '/base/go.gitlab-ci.yml'
stages: [detect, build, test, deploy]
detect-changes:
stage: detect
script:
- ./scripts/detect-changes.sh > changed-services.txt
artifacts:
paths: [changed-services.txt]
build-services:
stage: build
trigger:
include:
- artifact: child-build.yml
job: generate-build-pipeline
strategy: depend
test-services:
extends: .go-test
parallel:
matrix:
- SERVICE: ${CHANGED_SERVICES}
4.2 性能测试场景(顺手接 AI/接口压测)
yaml
perf-test:
stage: perf
parallel:
matrix:
- TPS_TARGET: ["1000", "3000", "6000"]
SCENARIO: ["login", "chat", "search"]
script:
- jmeter -n -t ${SCENARIO}.jmx -Jtps=${TPS_TARGET}
➡ 一次提交跑出 9 组压测基线,天然覆盖「场景 × 目标 TPS」的全量矩阵。
五、面试官追问(高阶必背)
| 问题 | 高分回答要点 |
|---|---|
include 和 extends 区别? |
include 是文件级 复用(跨文件拉模板);extends 是 Job 级继承(合并当前 job 配置) |
trigger 和 needs 区别? |
needs 是 Job 级 DAG 依赖(同 Pipeline 内);trigger 是 Pipeline 级解耦(跨 Pipeline,可隔离资源/权限) |
| matrix 会不会撑爆 Runner? | 会。Job 数 = 各轴取值的笛卡尔积 ,须配合 resource_group + Runner 并发上限治理 |
| 子流水线失败如何重试? | strategy: depend 下,父 Pipeline 会标红,GitLab UI 直接 Retry 子 Pipeline |
| 如何保证模板库版本可控? | include 锁 ref: <tag>,禁止跟踪 main;重大变更走 deprecation + 双版本过渡 |
rules 为何取代 only/except? |
rules 支持 if / changes / exists / allow_failure 组合,语义更精确,是现行推荐写法 citation:GitLab Docs --- rules |
参考资料
- GitLab Docs --- include
- GitLab Docs --- trigger (parent-child pipelines)
- GitLab Docs --- parallel (matrix)
- GitLab Docs --- rules
- GitLab Docs --- needs (DAG)