GitLab CI/CD 基础:.gitlab-ci.yml 流水线实践

FreeGuideOnline 最新 2026-06-13

GitLab CI/CD 基础:.gitlab-ci.yml 流水线实践

什么是 GitLab CI/CD

GitLab CI/CD 是 GitLab 内置的持续集成、持续交付与部署工具。只需在仓库根目录创建一个名为 .gitlab-ci.yml 的文件,GitLab 就会自动检测并运行其中定义的流水线(Pipeline)。不需要安装第三方插件或服务,配置即用,适合从个人项目到企业级应用的各种场景。

流水线的核心概念:

  • Pipeline(流水线):由阶段和作业组成的自动化过程。
  • Stage(阶段):流水线中的一个逻辑分组,例如 buildtestdeploy
  • Job(作业):具体执行的任务,每个作业属于某一个阶段,可以运行脚本、编译代码、运行测试等。
  • Runner(运行器):实际执行作业的代理,可以是 GitLab 共享的 Runner,或是自行部署的专用 Runner。

.gitlab-ci.yml 文件结构与基础关键字

.gitlab-ci.yml 使用 YAML 格式编写,必须放在仓库根目录。一个最小化的流水线文件如下:

stages:
  - build
  - test

build-job:
  stage: build
  script:
    - echo "Compiling the code..."
    - gcc hello.c -o hello

test-job:
  stage: test
  script:
    - echo "Running tests..."
    - ./hello

核心关键字解释

关键字 说明
stages 定义流水线中的阶段列表,按顺序执行。同一阶段的作业可并行运行,前一个阶段全部成功后才会开始下一阶段。
stage 指定当前作业属于哪个阶段。
script 作业要执行的命令,可以是一系列 shell 命令。这是作业的必填项(除 trigger 等特殊作业外)。
before_script script 之前运行的命令,通常用于安装依赖或设置环境。可定义在全局或单个作业中。
after_script script 之后运行的命令,即使 script 失败也会执行。
variables 定义环境变量,可在全局或作业内声明。
only / except 控制作业在何时触发(基于分支名、标签等)。新版推荐用 rules 替代。
rules 更灵活的条件控制,可基于分支、变量、文件变化等决定是否执行作业。
tags 指定运行该作业的 Runner 标签,用于匹配特定 Runner。
image 用于作业的 Docker 镜像,例如 image: node:18
services 启用的额外服务容器,如数据库。
artifacts 定义要保留并传递给后续作业的文件或目录。
dependencies 控制当前作业只依赖哪些上游作业的产物。
needs 定义作业之间的依赖关系,可创建跨阶段的并行依赖结构(DAG 流水线)。
cache 缓存依赖或编译结果,加快后续流水线速度。
when 控制作业何时运行:on_success(默认)、on_failurealwaysmanualdelayed 等。
environment 关联部署环境名称和 URL,用于监控和回滚。
include 引用外部 YAML 文件,实现配置复用。

从零开始:构建一个多阶段流水线

下面以一个 Node.js 项目为例,展示从安装依赖、测试、构建到部署到测试环境的完整流程。

1. 项目结构假设

my-app/
  .gitlab-ci.yml
  package.json
  src/
  tests/

2. 编写 .gitlab-ci.yml

# 全局默认镜像
image: node:18-alpine

# 定义阶段
stages:
  - install
  - test
  - build
  - deploy

# 全局缓存配置:缓存 node_modules
cache:
  key: ${CI_COMMIT_REF_SLUG}
  paths:
    - node_modules/

# 安装依赖作业
install-deps:
  stage: install
  script:
    - npm ci
  artifacts:
    paths:
      - node_modules/
    expire_in: 1 hour

# 代码质量检查(并行于测试)
lint:
  stage: test
  script:
    - npm run lint
  needs:
    - install-deps

# 单元测试作业
unit-test:
  stage: test
  script:
    - npm run test
  coverage: '/All files[^|]*\|[^|]*\s+([\d\.]+)/'
  artifacts:
    when: always
    reports:
      junit: test-results.xml
      coverage_report:
        coverage_format: cobertura
        path: coverage/cobertura-coverage.xml
  needs:
    - install-deps

# 构建作业
build-app:
  stage: build
  script:
    - npm run build
  artifacts:
    paths:
      - dist/
    expire_in: 1 week
  needs:
    - install-deps

# 部署到测试环境(手动触发)
deploy-staging:
  stage: deploy
  script:
    - echo "Deploying to staging server..."
    - rsync -av dist/ user@staging-server:/var/www/app
  environment:
    name: staging
    url: https://staging.my-app.com
  when: manual
  only:
    - main
  needs:
    - build-app

3. 配置解读

  • image: node:18-alpine 指定所有作业默认使用的轻量 Docker 镜像。
  • cache 通过分支名缓存 node_modules,避免每次重新下载依赖,但缓存不会跨分支污染。
  • install-deps 使用 npm ci 严格依照 lock 文件安装依赖,并将 node_modules 设为制品供后续作业使用。
  • needs 关键字让 lintunit-test 不必等待整个阶段完成,只要依赖的作业完成即可开始,实现更快的并行。
  • unit-test 中的 coverage 使用正则从测试输出中提取覆盖率并展示在合并请求中。
  • artifacts 将测试报告上传为 GitLab 可解析的格式,用于合并请求中的测试和覆盖率视图。
  • deploy-staging 作业设置为 when: manual,需要手动点击触发;environment 设置使其在“环境”页面中可跟踪部署历史和回滚。

条件控制与最佳实践

使用 rules 精确控制作业触发

推荐用 rules 代替旧的 only/except。例如,仅在合并请求或主分支上运行部署:

deploy-production:
  stage: deploy
  script: ./deploy-prod.sh
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
      when: manual
    - when: never

更复杂的规则可以结合变量、文件变化等:

run-e2e-tests:
  stage: test
  script: npm run e2e
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - changes:
        - tests/**/*
      when: on_success

环境变量管理

敏感信息(如密码、密钥)应在 GitLab 项目的 Settings > CI/CD > Variables 中设置,然后在 .gitlab-ci.yml 中通过 $VARIABLE_NAME 引用。将变量标记为 MaskedProtected 以增强安全性。

示例:

deploy:
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY

缓存与制品的区别

特性 Cache(缓存) Artifacts(制品)
目的 加速后续流水线,存储依赖或中间文件 传递构建结果给后续作业或下载
持久性 非保证,可能被清理 保证在设定时间内可用
跨流水线 是,通过 key 复用 否(默认只在当前流水线内传递)
上传到 GitLab 否(存在 Runner 本地)

合理使用缓存(如 node_modulesvendor)和制品(如编译后的二进制、测试报告)可以大幅优化流水线效率。

定义可复用的配置模板

使用 include 和 YAML 锚点(&*)减少重复。例如,定义通用的部署设置:

.deploy_template: &deploy_template
  stage: deploy
  script:
    - echo "Deploying to ${DEPLOY_ENV}"
    - ./deploy.sh

deploy-staging:
  <<: *deploy_template
  environment:
    name: staging
  variables:
    DEPLOY_ENV: staging
  only:
    - develop

deploy-production:
  <<: *deploy_template
  environment:
    name: production
  variables:
    DEPLOY_ENV: production
  only:
    - main
  when: manual

常见问题与调试技巧

  • 作业一直处于 pending 状态:没有匹配的 Runner,检查 tags 是否正确,或共享 Runner 是否启用。
  • 脚本执行失败但日志不清晰:在 script 中开启详细输出,如 set -x,或分段输出。
  • 缓存未生效:确保 cache:key 稳定,作业使用相同的 Runner 且路径正确。
  • 无法下载制品:使用 needsdependencies 必须明确定义依赖关系,否则默认会下载前序所有作业的制品(可能导致下载无关制品)。
  • YAML 语法错误:在 GitLab CI/CD 编辑器(Pipeline Editor)中可直接验证语法。

完整的进阶流水线示例

以下流水线集成了代码检测、多版本测试、容器化构建与自动发布:

include:
  - template: Security/SAST.gitlab-ci.yml

stages:
  - prepare
  - test
  - build
  - release
  - deploy

variables:
  DOCKER_DRIVER: overlay2
  IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG

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

prepare:
  stage: prepare
  image: node:18
  script: npm ci
  artifacts:
    paths:
      - node_modules/

unit-test:
  stage: test
  image: node:18
  script: npm test
  needs: ["prepare"]

build-docker:
  stage: build
  image: docker:20.10
  services:
    - docker:20.10-dind
  before_script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
  script:
    - docker build -t $IMAGE_TAG .
    - docker push $IMAGE_TAG
  needs: ["unit-test"]

release:
  stage: release
  image: registry.gitlab.com/gitlab-org/release-cli:latest
  script:
    - echo "Creating release $CI_COMMIT_TAG"
  release:
    name: 'Release $CI_COMMIT_TAG'
    description: 'Auto-generated release from tag.'
  rules:
    - if: $CI_COMMIT_TAG

deploy-staging:
  stage: deploy
  script:
    - kubectl set image deployment/my-app app=$IMAGE_TAG -n staging
  environment:
    name: staging
  rules:
    - if: $CI_COMMIT_BRANCH == "main"

通过以上从基础到进阶的讲解,你已经可以上手编写自己的 .gitlab-ci.yml 流水线。记住,GitLab CI/CD 的强大在于其灵活性与内置集成,反复在 CI/CD > Pipelines 页面查看执行结果,并结合合并请求的 Pipeline 状态,可以快速优化交付流程。