路线图

11-GitLab CI

星辉 2026-07-02 阅读 5 min 956 字 路线图
11-GitLab CI 封面

GitLab CI 是 GitLab 内置的 CI/CD 平台,和代码托管深度集成。它的优势是 All-in-One(代码 + CI + 制品仓库 + 安全扫描一个平台搞定)和私有化免费(CE 版本开源)。本章讲 .gitlab-ci.yml 结构、stage/job/pipeline 概念、runner、artifact/cache、include/extends 复用。

.gitlab-ci.yml 结构#

GitLab CI 的配置文件放在仓库根目录的 .gitlab-ci.yml,YAML 格式:

yaml
# .gitlab-ci.yml
variables:
  IMAGE_NAME: $CI_REGISTRY_IMAGE
  IMAGE_TAG: $CI_COMMIT_SHORT_SHA

# 全局缓存
cache:
  key: "$CI_PROJECT_NAME-go-modules"
  paths:
    - .go/pkg/mod/
    - .go/cache/

stages:
  - test
  - build
  - deploy

# 单元测试
unit-test:
  stage: test
  image: golang:1.22-alpine
  script:
    - go test -race -coverprofile=coverage.out ./...
    - go tool cover -func=coverage.out | tail -1
  coverage: '/total:\s+\(statements\)\s+(\d+\.\d+)%/'
  artifacts:
    reports:
      coverage_report:
        coverage_format: cobertura
        path: coverage.xml
    expire_in: 7 days

# lint
lint:
  stage: test
  image: golangci/golangci-lint:v1.57
  script:
    - golangci-lint run --timeout=5m

# 构建镜像(kaniko,不需要特权)
build-image:
  stage: build
  image:
    name: gcr.io/kaniko-project/executor:v1.21.0-debug
    entrypoint: [""]
  script:
    - mkdir -p /kaniko/.docker
    - |
      cat > /kaniko/.docker/config.json << EOF
      {"credHelpers": {"$CI_REGISTRY": "ecr-login"}}
      EOF
    - /kaniko/executor
        --context $CI_PROJECT_DIR
        --dockerfile $CI_PROJECT_DIR/Dockerfile
        --destination $IMAGE_NAME:$IMAGE_TAG
        --cache=true
        --cache-repo=$IMAGE_NAME/cache
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'

# 部署到 staging
deploy-staging:
  stage: deploy
  image: alpine/git:latest
  script:
    - git clone https://gitlab-ci-token:$GITOPS_TOKEN@$GITOPS_REPO /tmp/gitops
    - cd /tmp/gitops
    - sed -i "s|image: $IMAGE_NAME:.*|image: $IMAGE_NAME:$IMAGE_TAG|g" envs/staging/app/deployment.yaml
    - git add .
    - git commit -m "ci: update app to $IMAGE_TAG [skip ci]"
    - git push
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'
  environment:
    name: staging
    url: https://staging.example.com

# 部署到 production(手动)
deploy-production:
  stage: deploy
  image: alpine/git:latest
  script:
    - git clone https://gitlab-ci-token:$GITOPS_TOKEN@$GITOPS_REPO /tmp/gitops
    - cd /tmp/gitops
    - sed -i "s|image: $IMAGE_NAME:.*|image: $IMAGE_NAME:$IMAGE_TAG|g" envs/production/app/deployment.yaml
    - git add .
    - git commit -m "ci: update app to $IMAGE_TAG [skip ci]"
    - git push
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'
  when: manual                      # 手动触发
  environment:
    name: production
    url: https://example.com

stage / job / pipeline 概念#

text
pipeline(一次 push 触发的整个流水线)
  ├── stage: test
  │   ├── job: unit-test
  │   ├── job: lint
  │   └── job: integration-test     ← 同 stage 内 job 并行
  ├── stage: build                  ← stage 间串行
  │   └── job: build-image
  └── stage: deploy
      ├── job: deploy-staging
      └── job: deploy-production(when: manual)
  • Pipeline:一次 push/MR 触发的完整流水线
  • Stage:阶段,stage 之间串行(前一个完成才进下一个)
  • Job:阶段内的具体任务,同 stage 内 job 并行

关键控制:

  • needs:可以让 job 跨 stage 依赖,不必等整个 stage 完成
  • rules:条件控制,决定 job 是否执行
  • when: manual:手动触发
  • allow_failure: true:失败不影响流水线整体状态

Runner 注册与标签#

Runner 是跑 job 的执行器。GitLab Runner 支持多种 executor:shell、docker、kubernetes、parallels、virtualbox。

K8s 动态 Pod Agent 是生产推荐#

Runner 跑在 K8s 上,每个 job 是一个 Pod,用完即销。这种模式解决了静态 Slave 的所有痛点:环境污染(每次干净容器)、资源浪费(用完即删)、扩容慢(K8s 自动调度)、配置漂移(Pod Template 声明式)。

yaml
# runner-values.yaml(Helm Chart 配置)
gitlabUrl: https://gitlab.example.com
# GitLab 16+ 已弃用 registration token(17.0 起默认禁用),改用认证 token(glrt- 前缀)
runnerToken: "glrt-xxxxxxxxxxxxxxxx"

runners:
  config: |
    [[runners]]
      [runners.kubernetes]
        namespace = "cicd"
        image = "alpine:latest"
        privileged = false           # kaniko 不需要特权
        cpu_request = "100m"
        memory_request = "128Mi"
        cpu_limit = "2"
        memory_limit = "2Gi"
        service_account = "gitlab-runner"
        image_pull_secrets = ["regcred"]

关键配置:

  • privileged = false:kaniko 构建镜像不需要特权,安全合规友好
  • service_account:deploy 阶段访问 K8s API 用
  • image_pull_secrets:拉私有镜像的 Secret,必须在 Runner 创建 Job Pod 的 namespace 里存在(这是常见坑)
  • RBAC:Runner 的 ServiceAccount 需要在 agent namespace 创建 Pod 的权限;deploy 阶段如果要更新其他 namespace 的 Deployment,需要 ClusterRoleBinding(或按目标 namespace 单独绑 Role)

标签(Tags)#

Job 通过 tags 选择 Runner:

yaml
build-image:
  tags:
    - docker           # 只在有 docker 标签的 runner 上跑
  script:
    - docker build ...

gpu-test:
  tags:
    - gpu              # 只在 GPU runner 上跑
  script:
    - python train.py

Runner 注册时声明自己的标签,job 通过标签匹配。

artifact / cache 传递#

Artifact:job 间产物传递#

yaml
build:
  script:
    - go build -o app ./cmd/server
  artifacts:
    paths:
      - app
    expire_in: 1 hour    # 1 小时后自动删除

test:
  needs: build
  script:
    - ./app --test       # 拿到 build job 的产物

Artifact 在 job 之间通过 GitLab 服务器中转,job 结束后上传、下游 job 下载。

Cache:跨流水线复用#

yaml
cache:
  key: "$CI_PROJECT_NAME-go-modules"
  paths:
    - .go/pkg/mod/
    - .go/cache/

unit-test:
  script:
    - go test ./...

Cache 是跨流水线的——这次跑完上传缓存,下次流水线下载复用。适合依赖包这种"变化少、体积大"的内容。

区别#

维度ArtifactCache
用途job 间传递产物跨流水线复用依赖
存储GitLab 服务器Runner 本地或 S3
失效expire_in 控制key 变化时失效
典型场景构建产物、测试报告Go modules、npm node_modules

include / extends 复用#

GitLab CI 的复用机制比 GitHub Actions 更原生——不需要搞 reusable workflow / composite action,YAML 原生的 include + extends 就够用。

include:引用外部配置#

yaml
# .gitlab-ci.yml
include:
  - project: 'devops/ci-templates'      # 引用其他仓库
    ref: main
    file: '/go-service.yml'
  - template: Auto-DevOps.gitlab-ci.yml  # 引用 GitLab 内置模板
  - local: '/.gitlab/ci/test.yml'        # 引用本仓库其他文件

# 覆盖或扩展
test:
  extends: .test-template                # 引用本地模板
  variables:
    GO_VERSION: "1.22"

extends:本地模板继承#

yaml
.test-template:
  image: golang:1.22-alpine
  before_script:
    - go mod download
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'

unit-test:
  extends: .test-template
  script:
    - go test ./...

integration-test:
  extends: .test-template
  script:
    - go test -tags=integration ./...

hidden key + anchor(YAML 原生复用)#

yaml
.deploy-template: &deploy-template     # hidden key(点开头)+ anchor
  image: alpine/git:latest
  before_script:
    - git config --global user.email "ci@example.com"

deploy-staging:
  <<: *deploy-template                  # 合并 anchor
  script:
    - ./deploy.sh staging

deploy-production:
  <<: *deploy-template
  script:
    - ./deploy.sh production

extends 比 YAML anchor 更易读,推荐用 extends。

环境与变量#

CI/CD Variables#

在项目 Settings → CI/CD → Variables 配置:

  • Protected:只在 protected branch/tag 上可用(main、release/* 等)
  • Masked:值不在 log 显示
  • File type:变量内容写到临时文件,适合存证书、kubeconfig
yaml
deploy:
  variables:
    KUBECONFIG: $KUBECONFIG_FILE       # File 类型变量
  script:
    - kubectl --kubeconfig=$KUBECONFIG apply -f deploy.yaml

Environment#

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

在 GitLab UI → Deploy → Environments 可以看到所有环境的部署历史、回滚按钮。

rules:精细控制#

yaml
# 只在 main 分支 push 时构建
build:
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'
      when: on_success
    - when: never                      # 其他情况不执行

# PR 时跑测试但不部署
test:
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'

# 路径过滤(Monorepo)
build-service-a:
  rules:
    - changes:
        - services/service-a/**/*
        - libs/shared/**/*

# tag 发版
release:
  rules:
    - if: '$CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/'

resource_group:并发控制#

同一项目的部署不能并发——避免两个流水线同时改 GitOps 仓库冲突:

yaml
deploy-production:
  resource_group: production
  # 同一时间只有一个 job 持有这个 resource_group
  script:
    - ./deploy.sh

resource_group 的本质是互斥锁——同一 group 名的 job 会自动排队。这在 GitOps 模式下特别重要:多个流水线同时 git push 同一个 GitOps 仓库会冲突,加 resource_group 后自动串行。

实战要点#

  • Kubernetes executor + kaniko 是生产标配。不需要特权容器、不需要 DinD、安全合规友好。
  • cache 和 artifact 要分清。依赖用 cache(跨流水线),产物用 artifact(job 间)。
  • Protected variables 保护生产密钥。PROD_KUBECONFIG 标记 protected,只在 protected branch 上可用,feature 分支 PR 拿不到。
  • resource_group 防并发。GitOps 仓库的 push 容易冲突,加 resource_group 自动排队。
  • include 做组织级模板。把所有服务共用的 stage 抽到独立仓库,业务仓库 include 引用,改一处全组织生效。这是 GitLab CI 相比 GitHub Actions 的一个优势——include 是 YAML 原生能力,不需要学 reusable workflow 的语法。
  • rules 替代 only/except。新版 GitLab 推荐 rules,表达力更强,能写 changes 路径过滤、if 条件组合。
  • CI/CD Variables 优先用 File 类型存证书。kubeconfig、SSL 证书这类多行内容用 File 类型变量,GitLab 自动写到临时文件,脚本里直接用路径引用。

小结#

GitLab CI 的优势是和 GitLab 代码托管深度集成——同一平台、同一权限模型、同一 UI 体验。.gitlab-ci.yml 配置简洁,include/extends 复用机制清晰,适合私有化部署和 All-in-One 场景。

GitLab CI 相比 GitHub Actions 的一个显著优势是 include 跨仓库引用更原生——不需要学 reusable workflow 的语法,YAML 原生的 include: - project: ... 就能引用其他仓库的配置。这让组织级 CI 模板化在 GitLab 里更容易落地。

它的劣势是生态不如 GitHub Actions 大(marketplace Action 少),且只能配合 GitLab 用——不像 GitHub Actions 可以被任何 Git 平台通过 webhook 调用。下一章讲老牌 CI 工具 Jenkins。