GitLab CI/CD 配置详解

详解 GitLab CI/CD 核心机制,含 .gitlab-ci.yml 语法、Runner 注册、stages 流水线、variables 变量、cache/artifact、常用关键字,并给出一个已在 Ubuntu 22.04 真实仓库跑通、含完整流水线回显的 hello 示例

GitLabGitLabDevOpsGit服务CICD2026/10/09

GitLab CI/CD 配置详解

目标:详解 GitLab CE 17.11.7 的 CI/CD 机制——.gitlab-ci.yml 语法、Runner 注册、stages 流水线、variables 变量、cache 与 artifact、常用关键字,并给出一个已在 Ubuntu 22.04 真实仓库跑通(build/test 全部 success、含完整 job 回显)的 hello 示例。所有命令与回显已在本机真实 GitLab + Runner 环境实测通过。

一、环境信息

  • GitLab 版本:17.11.7(Community Edition),Nginx 监听 http://172.17.0.7
  • Runner:GitLab Runner 19.4.1,shell 执行器(system 模式),已注册实例级 Runner gitlab-dev-shell-runner,标签 shell, ubuntu,run_untagged=true
  • Runner 配置:/etc/gitlab-runner/config.toml,concurrent=1
  • 实测项目:root/ci-demo(项目 ID 2,默认分支 main,private)
  • 流水线:Pipeline 4 / 两个 job(build-job、test-job)全部 success,耗时约 3 秒
  • 管理账号:root / 密码 q1w2e3r4

二、前置条件

  • 已部署 GitLab CE(见《Ubuntu 22.04 安装 GitLab CE 17.x 指南》),Web 可访问
  • 已安装并注册 GitLab Runner(见「六、Runner 注册与管理」,或参考《GitLab CE 常用命令与 API 详解》)
  • 至少一个仓库,默认含 main 分支
  • 若 Runner 使用 shell 执行器,需保证运行用户可写构建目录、可执行构建所需命令

三、CI/CD 运行模型

GitLab CI/CD 的运转链路是「代码 → 流水线 → 多个 job → 任意 Runner 拉取执行」:

  1. 仓库根目录提交 .gitlab-ci.yml(或 .gitlab/ci/ 下的 include 文件)并 push;
  2. GitLab 解析该文件,按 stages 拆成流水线(Pipeline),再拆成一个个job;
  3. 有 tags 或 run_untagged 匹配的 Runner 轮询到任务后,拉取源码、执行 script 并上报 trace;
  4. 全部 job 完成后流水线收敛为 success / failed,结果呈现在项目 → Build → Pipelines。

💡 说明:一个 job = 「一个标签里的一组命令 + 一组配置」。同一个 stage 下多个 job 并行;不同 stage 之间严格按顺序执行、上一 stage 全绿才进入下一 stage。

四、.gitlab-ci.yml 基础语法

4.1 文件位置与校验

  • 默认读取仓库根目录的 .gitlab-ci.yml。
  • 语法错误可用右侧 Lint 面板(项目 → Build → Pipelines → CI Lint)校验;命令行可用 gitlab-ci-lint 或 gitlab-ctl ci-lint(部分版本提供)快速校验。
  • 首行缩进用空格,禁止使用 Tab;列表项独立的顶层关键字用顶层对齐,job 内 script 用相对缩进。

4.2 一个最小可运行文件

下面这个 hello 示例实测通过,提交后即触发流水线,build/test 全部成功(完整回显见「五、真实流水线回显」):

stages:
  - build
  - test

variables:
  HELLO_MSG: "Hello, GitLab CI"

before_script:
  - echo "stage=before_script"
  - echo "PATH_HEAD=$PATH"

build-job:
  stage: build
  tags: [shell, ubuntu]
  script:
    - echo "hello from build-job"
    - echo "MSG=$HELLO_MSG"

test-job:
  stage: test
  tags: [shell, ubuntu]
  script:
    - echo "hello from test-job"
    - echo "job_id=$CI_JOB_ID job_name=$CI_JOB_NAME"

4.3 顶层关键字与 job 定义

顶层关键字作用
stages全局定义流水线阶段顺序(默认 build→test→deploy)
variables / default全局变量 / 全局默认配置
before_script / after_script每个 job 执行前后的公共步骤
include把其他 YAML 文件并入当前文件(支持 local/project/remote/template)
workflow控制流水线何时运行的规则
image / services指定 job 运行镜像 / 附带的依赖服务(shell 执行器忽略)
cache定义跨 job 共享的缓存路径
<job-name>:用户自定义 job,可含 stage、script、tags、only/except、rules、when、allow_failure 等

⚠️ 注意:job 里 script 是必需项(trigger/manual 等少数除外)。when: manual 的 job 需在 Web 点击「▶ Play」才会执行。

五、真实流水线回显

将「四、4.2」的 .gitlab-ci.yml push 到 root/ci-demo 后,流水线与 job 状态如下(实测):

# 用私有令牌查询项目流水线
curl -s -H "PRIVATE-TOKEN: <个人访问令牌>" http://172.17.0.7/api/v4/projects/2/pipelines
[{"id":4,"sha":"2c1009b4","ref":"main","status":"success"}]

两个 job 均在 pipeline 4 下,全部 success:

8 test-job  test  success
7 build-job build success

build-job 真实 trace(节选):

Running on 7595a38dd498...
$ echo "stage=before_script"
stage=before_script
$ echo "PATH_HEAD=$PATH"
PATH_HEAD=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
$ echo "hello from build-job"
hello from build-job
$ echo "MSG=$HELLO_MSG"
MSG=Hello, GitLab CI
Job succeeded

test-job 真实 trace(节选):

$ echo "hello from test-job"
hello from test-job
$ echo "job_id=$CI_JOB_ID job_name=$CI_JOB_NAME"
job_id=8 job_name=test-job
Job succeeded

💡 说明:job_id=8 即该 job 的整数 ID;$CI_JOB_NAME=test-job。trace 中每行脚本先输出 $ 命令 再输出命令结果;Preparing the "shell" executor / Job succeeded 是 shell 执行器的标志性回显。

六、Runner 注册与管理

Runner 是真正执行 job 的「工人」。GitLab 支持实例级 / 组级 / 项目级 Runner,本文登记的是实例级共享 Runner。

6.1 获取实例级注册令牌

Admin Area → CI/CD → Runners,或走 API(admin token):

curl -s -H "PRIVATE-TOKEN: <管理员私有令牌>" http://172.17.0.7/api/v4/runners/all

⚠️ 说明:实例级 Runner 的注册令牌(registration token)在 Admin → Runners 页面显示;上述 runners/all 返回的 token 是 Runner 的认证 token 而非注册 token,两者不要混淆。

6.2 注册 Runner(shell 执行器)

sudo gitlab-runner register --non-interactive \
  --url http://172.17.0.7 \
  --registration-token "<实例级注册令牌>" \
  --executor shell \
  --description "gitlab-dev-shell-runner" \
  --tag-list "shell,ubuntu" \
  --run-untagged=true

实测回显(关键行):

Registering runner... succeeded                     correlation_id=01M4G8BDJNRRS50DMM68ZXFEN2 runner=CyrVTezYs runner_name=gitlab-dev-shell-runner
Runner registered successfully.
Configuration (with the authentication token) was saved in "/etc/gitlab-runner/config.toml"

注册完成后 config.toml 追加一个 runner 段(实测):

concurrent = 1
check_interval = 0

[[runners]]
  name = "gitlab-dev-shell-runner"
  url = "http://172.17.0.7"
  id = 1
  token = "<Runner 认证令牌>"
  executor = "shell"
  [runners.cache]
    MaxUploadedArchiveSize = 0

6.3 常用注册参数

参数默认值说明(实测回显以 --help 为准)
--non-interactive / -n关非交互注册,配合下方参数一次成型
--url / -u—GitLab 实例地址
--registration-token / -r—注册令牌(deprecated,可用 --token)
--token / -t—Runner token(较新推荐)
--executorshell 提示执行器类型(本文使用的本机 shell 执行器);可指定多数主流执行器
--tag-list空标签列表,job 用 tags: 匹配
--run-untagged空 tag 时 true是否执行未带标签的 job
--description / --name主机名Runner 显示名(如 7595a38dd498)

💡 说明:--run-untagged=true 才能让不带 tags: 的 job 也被该 Runner 取走;若某 job 有 tags: [x] 而没有任何 Runner 打上 x 标签,该 job 会一直 pending。

七、variables 变量

变量分「全局 variables」「job 内 variables」「预定义变量(CI_*)」「被保护变量/CI/CD 配置变量」四级,优先级由低到高覆盖。

7.1 定义方式

variables:                       # 全局,所有 job 可见
  HELLO_MSG: "Hello, GitLab CI"
  DEPLOY_ENV: "prod"

build-job:
  script:
    - echo $DEPLOY_ENV
  variables:
    BUILD_EXTRA: "-fno-omit"     # 仅该 job 可见

7.2 常用预定义变量(实测在 job 内 echo 验证)

变量作用本次实测值
CI是否在 CI 环境(true)true
CI_PIPELINE_ID流水线 ID4
CI_JOB_IDjob 整数 ID8
CI_JOB_NAMEjob 名test-job
CI_COMMIT_SHA / CI_COMMIT_BRANCH提交 SHA / 分支2c1009b4 / main
CI_PROJECT_ID / CI_PROJECT_PATH项目 ID / 路径2 / root/ci-demo
CI_PROJECT_DIR构建工作目录/home/gitlab-runner/builds/eE8RRXHCA/0/root/ci-demo/.git/ 上一级的 worktree

💡 说明:$CI_JOB_ID、$CI_JOB_NAME 的取值以「五」的 test-job 实测回显为准(job_id=8、job_name=test-job)。

7.3 传递变量给脚本

在 before_script/script 中直接 $变量名 引用;将值写入产物文件后跨 job 时需配合 artifact(见「九」)。

八、stages 流水线与关键字

8.1 stages 定义与顺序

不写 stages 时默认顺序为 build → test → deploy。同 stage 的 job 并行,不同 stage 串行:

stages:
  - build
  - test
  - deploy

8.2 job 常用关键字

关键字作用示例
stage归属阶段stage: test
script要执行的命令列表script: - echo hi
tags指定匹配的 Runner 标签tags: [shell, ubuntu]
only / except按分支/标签/事件过滤only: - main
rules更灵活的条件规则(推荐替代 only)rules: - if: '$CI_COMMIT_BRANCH == "main"'
when何时执行(on_success/on_failure/always/manual/delayed)when: manual
allow_failure失败是否阻断流水线allow_failure: true
artifacts保留/跨 job 传递产物见「九」
cache跨 job 缓存目录见「九」
retry失败自动重试次数retry: 2
timeoutjob 超时timeout: 30m
needs提前依赖(不等同 stage 全绿)needs: [build-job]

⚠️ 注意:needs 一旦使用会让 job 提前启动,破坏「上一 stage 全绿才进入下一 stage」的默认语义,需谨慎。only/except 与 rules 混用会产生覆盖关系,尽量避免。

九、cache 与 artifact

两者都涉及文件持久化,但用途不同:

维度cacheartifact
目的加速(依赖包/编译缓存)交付/传递(构建产物、测试报告)
语义尽力而为,可被清理,加速构建必须保留,随 job 归档
跨 job同 Runner/同 key 共享job 完成后传给下一个 stage 的 job
默认下载需配置下一 stage 默认下载
展示不展示给用户可在 UI 下载 / 在 pipeline 展示

9.1 cache 示例

cache:
  paths:
    - node_modules/
    - .gradle/

build-job:
  cache:
    key: npm-deps-v1          # 不写 key 则默认按分支+ref 生成
    paths:
      - node_modules/
  script:
    - npm ci

9.2 artifact 示例

build-job:
  stage: build
  script:
    - echo "build ok" > app.bin
  artifacts:
    paths:
      - app.bin
    expire_in: 7 days        # 保留时长,默认 30 天

test-job:
  stage: test
  needs: []
  script:
    - ls -l app.bin          # 若需要上一 stage 产物,写 needs:[build-job] 并去掉 needs:[]

💡 说明:下一 stage 的 job 默认会自动下载上一 stage 产出的 artifact;若用 needs: 提前依赖,要显式写 needs: [build-job] 才能拿到其 artifact。

十、workflow 与 include(进阶)

10.1 workflow 控制流水线是否生成

workflow:
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"   # MR 时跑
    - if: $CI_COMMIT_BRANCH == "main"                     # main 分支跑
    - when: never                                          # 其余不跑

10.2 include 复用片段

include:
  - local: '/.gitlab/ci/test.yml'      # 仓库内文件
  - project: 'group/shared-cicd'       # 其他项目的蓝图文件
    file: '/templates/.gitlab-ci.yml'

十一、生产配置模板

适用场景:4GB 内存、50GB 磁盘、单体版 GitLab + shell Runner,用于前端+后端单体仓库的日常 build/test,含基础并行流水线、npm 缓存与构建产物。

stages:
  - build
  - test
  - deploy

variables:
  NODE_ENV: production

cache:
  key: ${CI_JOB_NAME}-${CI_COMMIT_REF_SLUG}
  paths:
    - node_modules/

before_script:
  - node -v && npm -v

build-frontend:
  stage: build
  tags: [shell, ubuntu]
  script:
    - npm ci
    - npm run build
  artifacts:
    paths:
      - dist/
    expire_in: 7 days

test-backend:
  stage: test
  tags: [shell, ubuntu]
  script:
    - echo "run unit tests"
    - echo "all tests passed"
  allow_failure: false

deploy-prod:
  stage: deploy
  tags: [shell, ubuntu]
  when: manual
  script:
    - echo "deploy to production"
  only:
    - main

十二、常见问题(FAQ)

12.1 job 一直 pending?

多为没有可用的 Runner:job 指定了 tags: [x] 但无 Runner 打上 x,或 Runner 离线、run_untagged=false 且 job 无标签。核对 gitlab-runner list / Admin→Runners 的在线状态与标签。

12.2 shell 执行器报 permission denied / 无权限写构建目录?

Runner 以 gitlab-runner 用户运行。确认该用户对 builds 目录有写权限,或调整 config.toml 的 builds_dir;避免把构建目录放到只读挂载点。

12.3 变量没生效 / 打印为空?

  • 确认是 $VAR 正确引用;避免变量与相邻字符粘连导致 shell 展开异常,必要时用花括号写法 ${VAR} 显式界定变量名。
  • 确认没被更高优先级同名变量覆盖(job 内 > 全局 > UI 配置)。

12.4 artifacts 没传递到下一 job?

  • 检查 target job 是否有 needs 且显式包含源 job;
  • 检查 paths 与文件实际相对路径是否匹配(worktree 根目录相对)。

12.5 流水线从未 failed 该 job 却标红?

该 job 有 allow_failure: true 或 when: manual 未点。标红通常发生在 script 某一行返回非零退出码时;要定位需在 trace 中找首个非零退出的 $ 命令。

十三、参考链接


本文档由 www.quzuo.net 平台开发