Loading...

文章背景图

(五) 进阶与复用:include - extends - trigger - environment - services

2026-08-10
0
-
- 分钟

一、include:把千行 YAML 拆成乐高积木

include 允许你将 .gitlab-ci.yml 拆分成多个文件,或者引用外部模板,是 GitLab CI/CD 配置模块化的核心。

GitLab 支持六种 include 来源:

来源

语法

典型场景

local

同仓库的文件

按功能拆分(lint.yml, test.yml, deploy.yml)

project

同一 GitLab 实例的其他项目

公司级共享模板仓库

remote

任意 HTTP(S) URL

引用第三方公共模板

template

GitLab 官方模板

Auto DevOps, SAST 等

component

CI/CD Catalog 组件

GitLab 16+ 新生态

rules

条件引入

按分支/变量决定加载哪些配置

1.1 include:local:同仓库拆分(最常用)

# .gitlab-ci.yml(主文件)
include:
  - local: '/ci/build.yml'
  - local: '/ci/test.yml'
  - local: '/ci/deploy.yml'

stages:
  - build
  - test
  - deploy
# ci/build.yml
build-backend:
  stage: build
  image: golang:1.22
  script: go build ./...

build-frontend:
  stage: build
  image: node:20
  script: npm run build
# ci/test.yml
unit-test:
  stage: test
  script: go test ./...

lint:
  stage: test
  script: golangci-lint run

目录结构一览:

my-project/
├── .gitlab-ci.yml      ← 入口文件
└── ci/
    ├── build.yml       ← 构建相关
    ├── test.yml        ← 测试相关
    └── deploy.yml      ← 部署相关

1.2 include:project:跨项目共享模板

适用于公司级别的共享 CI/CD 模板库:

include:
  - project: 'devops/shared-ci-templates'
    ref: v2.0.0                                # 强烈建议指定版本!
    file: '/templates/nodejs.yml'
  - project: 'devops/shared-ci-templates'
    file: '/templates/docker-build.yml'

⚠️ 安全提示:建议用 Git Tag(v2.0.0)或完整 SHA 指定 ref,避免模板仓库变更导致所有项目 Pipeline 突然全部挂掉。

1.3 include:template:使用官方模板

GitLab 提供了大量开箱即用的 CI/CD 模板,涵盖 SAST 安全扫描、Auto DevOps、多种语言框架等:

include:
  - template: Security/SAST.gitlab-ci.yml
  - template: Security/Dependency-Scanning.gitlab-ci.yml
  - template: Code-Quality.gitlab-ci.yml

查看所有可用模板:GitLab CI Templates

1.4 include:rules:条件引入

include:
  - local: '/ci/security-scan.yml'
    rules:
      - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH   # 只在默认分支跑安全扫描
  - local: '/ci/e2e-test.yml'
    rules:
      - if: $CI_PIPELINE_SOURCE == "merge_request_event"  # MR 时跑 E2E

1.5 合并策略与优先级

当同一个 Job 在多个 include 文件中都有定义时,最后定义的那个生效。include 的评估顺序是:

① include 引入的文件先评估
② .gitlab-ci.yml 主文件后评估
③ 同名的配置,后定义的覆盖先定义的

这意味着你可以在主文件中"覆盖"引入的模板中某个 Job 的部分配置:

# 引入模板
include:
  - template: Jobs/Build.gitlab-ci.yml

# 覆盖模板中 build Job 的某些配置
build:
  image: node:22           # ← 覆盖模板中的镜像版本
  variables:               # ← 添加模板中没有的变量
    BUILD_FLAGS: "--production"

二、extends:消除重复配置

2.1 基本用法

# 定义可复用的"基类"
.node-job:
  image: node:20-alpine
  before_script:
    - npm config set registry https://registry.npmmirror.com
  cache:
    key:
      files:
        - package-lock.json
    paths:
      - node_modules/

# 继承基类,覆盖/补充
lint:
  extends: .node-job
  stage: lint
  script:
    - npm run lint

test:
  extends: .node-job
  stage: test
  script:
    - npm test

命名以 . 开头的 Job 被称为"隐藏 Job"——GitLab 不会把它们当作真正的 Job 来执行,仅用于被 extends 引用。

2.2 多层继承

.base:
  before_script:
    - echo "基础准备"

.node:
  extends: .base
  image: node:20

.linter:
  extends: .node
  before_script:
    - echo "代码检查准备"

eslint:
  extends: .linter           # 继承了 .base + .node + .linter
  script:
    - npx eslint src/

2.3 extends vs include 的选择

场景

推荐方案

同一个项目内多个 Job 共享配置

extends + 隐藏 Job

多个项目共享配置

include:project

模板需要频繁修改

extends(本地文件修改方便)

模板需要版本锁定

include:project(可按 Tag 锁定)

💡 两者不互斥:可以在 include 引入的模板基础上再用 extends 做二次继承。


三、trigger:多级流水线

3.1 触发下游项目

trigger-deploy:
  stage: deploy
  trigger:
    project: ops/infrastructure-repo     # 下游项目
    branch: main
    strategy: depend                     # 等待下游 Pipeline 完成后才继续
  variables:
    APP_VERSION: $CI_COMMIT_SHORT_SHA
    TRIGGERED_BY: $CI_PROJECT_NAME

strategy: depend 意味着当前 Pipeline 会等待下游 Pipeline 完成后,才能进入下一个阶段。这在多仓库关联部署时非常有用。

3.2 父子 Pipeline(Parent-Child)

这是 GitLab 13.2+ 引入的强大功能——在一个 Pipeline 中动态生成子 Pipeline:

# .gitlab-ci.yml(父 Pipeline)
generate-config:
  stage: build
  script:
    - ./generate-child-pipeline.sh     # 生成子 Pipeline 配置
  artifacts:
    paths:
      - child-pipeline.yml

child-pipeline:
  stage: test
  trigger:
    include:
      - artifact: child-pipeline.yml   # 使用上一步生成的配置
      - job: generate-config
    strategy: depend

一个极端实用的例子:Monorepo 中根据代码变更动态决定跑哪些子项目的 CI:

# generate-child-pipeline.sh
#!/bin/bash
echo "stages:" > child-pipeline.yml
echo "  - test" >> child-pipeline.yml

# 检测哪些子项目有变更
for project in frontend backend admin; do
  if git diff --name-only HEAD~1 | grep "^$project/"; then
    cat >> child-pipeline.yml << EOF
${project}-test:
  stage: test
  script:
    - cd ${project}
    - npm test
EOF
  fi
done

3.3 三种触发方式对比

方式

适用场景

配置复杂度

trigger:project

固定下游项目

🟢

trigger:include + artifact

动态生成子 Pipeline

🟡

trigger:include + local

静态父子 Pipeline

🟢


四、environment:环境管理与部署追踪

4.1 声明环境

deploy-staging:
  stage: deploy
  script: ./deploy.sh
  environment:
    name: staging
    url: https://staging.example.com

deploy-production:
  stage: deploy
  script: ./deploy.sh
  environment:
    name: production
    url: https://example.com

效果:

  • GitLab 自动在 Deployments → Environments 页面中展示每个环境的部署历史

  • 每个 Merge Request 中能看到"部署到了哪个环境"

  • 可以直接在 GitLab UI 中查看部署的应用(通过 url 链接)

4.2 环境级别的变量范围

deploy:
  stage: deploy
  variables:
    DB_HOST: staging-db.example.com    # staging 环境的数据库
  environment:
    name: staging
    url: https://staging.example.com
    on_stop: stop-staging              # ← 关联停止部署的 Job
  script:
    - ./deploy.sh staging

stop-staging:
  stage: deploy
  variables:
    GIT_STRATEGY: none                 # 不需要拉代码
  when: manual
  environment:
    name: staging
    action: stop                       # ← 停止环境的动作
  script:
    - ./destroy.sh staging

4.3 自动停止环境

review:
  stage: deploy
  script: ./deploy-review-app.sh
  environment:
    name: review/$CI_COMMIT_REF_SLUG      # ← 动态环境名!每个分支独立
    url: https://$CI_COMMIT_REF_SLUG.review.example.com
    on_stop: stop-review
    auto_stop_in: 3 days                   # ← 3 天后自动停止(清理资源)

这样,每个 MR 都会自动创建一个独立的环境用于预览(Review App),3 天后自动销毁。

4.4 Protected Environment:部署审批

在 GitLab UI 中 → Settings → CI/CD → Protected Environments,你可以设置:

  • 哪些用户/角色可以部署到某环境

  • 部署是否需要审批

配合 when: manual,实现生产环境的审批流程。


五、services:为 Job 提供附加服务

5.1 基本概念

services 启动一个额外的容器,为 Job 容器提供辅助服务——最常见的是数据库。

integration-test:
  image: node:20
  services:
    - postgres:16-alpine     # ← 启动一个 PostgreSQL 容器
  variables:
    POSTGRES_DB: testdb
    POSTGRES_USER: tester
    POSTGRES_PASSWORD: secret
    # 注意:连接数据库时用 hostname "postgres",不是 "localhost"
    DATABASE_URL: "postgresql://tester:secret@postgres:5432/testdb"
  script:
    - npm test

关键理解:Job 容器和 service 容器在同一个 Docker 网络中,Job 可以通过 service 的镜像名(如 postgres)来访问它。

5.2 自定义别名

integration-test:
  services:
    - name: postgres:16-alpine
      alias: db                             # ← 自定义别名
  variables:
    DATABASE_URL: "postgresql://tester:secret@db:5432/testdb"

5.3 常用服务组合

# Node.js + PostgreSQL + Redis
fullstack-test:
  image: node:20
  services:
    - postgres:16-alpine
    - redis:7-alpine
  variables:
    PGHOST: postgres
    REDIS_URL: redis://redis:6379
  script:
    - npm test

# Java + MySQL
java-test:
  image: maven:3.9-eclipse-temurin-21
  services:
    - mysql:8.4
  variables:
    MYSQL_ROOT_PASSWORD: rootpass
    MYSQL_DATABASE: testdb
    JDBC_URL: jdbc:mysql://mysql:3306/testdb
  script:
    - mvn test

5.4 启动等待

某些服务(如数据库)启动需要时间。GitLab Runner 内置了健康检查,但你也可以用 docker-wait 或脚本做自定义等待:

services:
  - name: postgres:16-alpine
    command: ["postgres", "-c", "max_connections=200"]

script:
  - |
    echo "等待 PostgreSQL 就绪..."
    for i in $(seq 1 30); do
      if pg_isready -h postgres -U tester; then
        echo "PostgreSQL 已就绪"
        break
      fi
      sleep 1
    done
  - npm test

六、release:自动发布

release-job:
  stage: deploy
  image: registry.gitlab.com/gitlab-org/release-cli:latest
  rules:
    - if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/
  script:
    - echo "Creating release $CI_COMMIT_TAG"
  release:
    name: 'Release $CI_COMMIT_TAG'
    tag_name: $CI_COMMIT_TAG
    description: './CHANGELOG.md'
    assets:
      links:
        - name: 'binary-linux-amd64'
          url: 'https://example.com/releases/$CI_COMMIT_TAG/binary-linux-amd64'
        - name: 'binary-darwin-arm64'
          url: 'https://example.com/releases/$CI_COMMIT_TAG/binary-darwin-arm64'

推送一个语义化版本标签(如 v1.2.0),GitLab 会自动创建一个 Release 条目,包含 Release Notes 和资产链接。


七、综合示例:Monorepo 的模块化 CI/CD 架构

my-monorepo/
├── .gitlab-ci.yml
├── ci/
│   ├── workflow.yml          # workflow:rules
│   ├── common.yml            # extends 基类
│   ├── frontend/
│   │   ├── build.yml
│   │   ├── test.yml
│   │   └── deploy.yml
│   └── backend/
│       ├── build.yml
│       ├── test.yml
│       └── deploy.yml
# .gitlab-ci.yml
include:
  - local: '/ci/workflow.yml'
  - local: '/ci/common.yml'
  - local: '/ci/frontend/build.yml'
  - local: '/ci/frontend/test.yml'
  - local: '/ci/frontend/deploy.yml'
  - local: '/ci/backend/build.yml'
  - local: '/ci/backend/test.yml'
  - local: '/ci/backend/deploy.yml'

stages:
  - build
  - test
  - deploy-staging
  - deploy-prod
# ci/common.yml
.frontend-base:
  image: node:20-alpine
  before_script:
    - cd frontend && npm ci
  rules:
    - changes:
        - frontend/**/*

.backend-base:
  image: golang:1.22
  before_script:
    - cd backend
  rules:
    - changes:
        - backend/**/*
# ci/frontend/deploy.yml
deploy-frontend-staging:
  extends: .frontend-base
  stage: deploy-staging
  script: npm run deploy:staging
  environment:
    name: staging
    url: https://staging.example.com

八、小结与下篇预告

配置工程化路径

单个 .gitlab-ci.yml          # 入门
    ↓
include:local 按阶段拆分     # 模块化
    ↓
extends 消除重复              # DRY 原则
    ↓
include:project 跨项目复用   # 组织级共享
    ↓
include:component 发布到目录  # 生态化

下篇预告

理论和方法论已全部就位。第六篇《实战场景 + 排错 + 最佳实践》是本系列的收官之作——我们不再讲解新的关键词,而是给你拿来即用的场景模板(Node.js / Java / Docker / Monorepo)、常见错误的排查指南,以及经过验证的最佳实践清单


参考资料:

原创

(五) 进阶与复用:include - extends - trigger - environment - services

本文链接: (五) 进阶与复用:include - extends - trigger - environment - services

本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。

文章目录