GitLab CI/CD 实践
GitLab CI/CD 与代码仓库同域,Push 或 Merge Request 即可触发 Pipeline。先让一条最小流水线跑通,再逐步加入缓存、制品、镜像构建和发布 Job;不要在第一天就堆满所有 Stage。
本章假设 GitLab Runner 已注册且 gitlab-runner verify 通过。示例使用 Omnibus 自带的 $CI_REGISTRY_* 变量;自建 Registry 时替换为实际地址。
1. GitLab Pipeline介绍
一次 Pipeline 由触发事件创建,按 Stage 顺序执行多个 Job。常见触发源:Push、Merge Request、Tag、Schedule、Web/API。
在 Build → Pipelines 查看 Job 日志。失败时先查 Runner tags 是否匹配、镜像能否拉取、变量是否注入。
2. .gitlab-ci.yml结构
.gitlab-ci.yml 放在仓库根目录,定义全局默认值和各 Job:
default:
image: alpine:3.20
tags: [docker]
stages: [validate, test, build, deploy]
lint:
stage: validate
script: [shellcheck scripts/*.sh]
build-image:
stage: build
script: [echo "build image"]
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
提交后 GitLab 会做 YAML 语法校验;语法错误时 Pipeline 不会创建。复杂项目可将 Job 拆到 ci/ 目录,用 include 引入:
include:
- local: 'ci/build.yml'
- local: 'ci/deploy.yml'
3. Stage与Job
Stage 按声明顺序串行;同一 Stage 内 Job 默认并行。用 needs 可跳过 Stage 等待,形成 DAG:
stages:
- build
- test
- deploy
build-artifact:
stage: build
script:
- mvn package -DskipTests
artifacts:
paths:
- target/*.jar
integration-test:
stage: test
needs: [build-artifact]
script:
- java -jar target/*.jar --self-test
| 概念 | 要点 |
|---|---|
stage | 串行大顺序;Stage 内任一 Job 失败则后续默认跳过 |
needs | 只等指定 Job,跳过 Stage 等待 |
parallel / retry | 矩阵并行 / 失败重试 |
build 与 deploy 应使用不同 Runner tag。
4. Variables变量管理
变量来源优先级(高到低):Job 级 → 项目 Settings → Group → Instance → default.variables。
variables:
MAVEN_OPTS: "-Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository"
deploy-prod:
variables:
KUBE_NAMESPACE: production
script:
- kubectl rollout status deployment/$APP_NAME -n $KUBE_NAMESPACE
敏感变量在 Settings → CI/CD → Variables 中设置,勾选 Mask 和 Protect(仅受保护分支/Tag 可用)。不要把密钥写进 .gitlab-ci.yml。CI_JOB_TOKEN 可用于同实例内跨项目只读访问,权限比 PAT 更小。
5. Cache缓存机制
Cache 加速依赖下载,键默认包含分支名;不同分支不共享,除非自定义 key:
cache:
key:
files:
- pom.xml
prefix: maven
paths:
- .m2/repository
policy: pull-push
build:
script:
- mvn -B package
policy | 行为 |
|---|---|
pull-push | 默认;读写缓存 |
pull | 只读,适合 MR |
push | 只写,适合 warm-up Job |
Cache 不是制品;可复现构建仍依赖 lock 文件和 Artifacts。
6. Artifacts制品管理
Artifacts 在 Job 之间传递构建产物,并可从 UI 下载:
build:
script:
- go build -o bin/app ./cmd/server
artifacts:
paths: [bin/app]
expire_in: 7 days
reports:
junit: report.xml
MR 页面可展示测试报告。expire_in 过短会导致下游 Job 找不到文件;生产镜像应推送到 Registry,不要只留 Artifacts。
7. Rules规则控制
rules 替代旧的 only/except,按条件决定是否创建 Job:
deploy-staging:
stage: deploy
script:
- ./deploy.sh staging
rules:
- if: $CI_COMMIT_BRANCH == "develop"
when: on_success
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
when: never
- when: manual
allow_failure: true
常用变量:$CI_COMMIT_BRANCH、$CI_COMMIT_TAG、$CI_PIPELINE_SOURCE、$CI_DEFAULT_BRANCH。生产发布建议 Tag 或受保护分支 + when: manual,并启用 Protected environments。
8. Docker镜像构建 (dind / socket caveats)
Docker-in-Docker(Runner 需 privileged = true):
build-image-dind:
stage: build
image: docker:27-cli
services:
- name: docker:27-dind
alias: docker
variables:
DOCKER_TLS_CERTDIR: "/certs"
before_script:
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
script:
- docker build -t "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA" .
- docker push "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA"
tags: [docker]
dind 扩大攻击面:仅 build Runner 开 privileged,与 deploy Runner 分离。挂载 /var/run/docker.sock 等价于宿主机 root 访问,不可用于不可信 MR;仅内网可信环境且无法用 dind/Kaniko 时考虑。
Kaniko(无 privileged,适合多租户 MR):
build-image-kaniko:
image:
name: gcr.io/kaniko-project/executor:debug
entrypoint: [""]
script:
- mkdir -p /kaniko/.docker
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"auth\":\"$(printf "%s:%s" "$CI_REGISTRY_USER" "$CI_REGISTRY_PASSWORD" | base64 | tr -d '\n')\"}}}" > /kaniko/.docker/config.json
- /kaniko/executor --context "$CI_PROJECT_DIR" --dockerfile Dockerfile
--destination "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA"
9. Kubernetes自动发布 (kubectl/helm job example)
将 kubeconfig 存为 File 类型 CI 变量 KUBECONFIG_CONTENT(base64 编码),Job 内写入临时文件:
variables:
HELM_RELEASE: my-service
HELM_CHART: ./deploy/chart
deploy-k8s:
stage: deploy
image: alpine/k8s:1.30.4
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
before_script:
- echo "$KUBECONFIG_CONTENT" | base64 -d > /tmp/kubeconfig
- export KUBECONFIG=/tmp/kubeconfig
script:
- helm upgrade --install "$HELM_RELEASE" "$HELM_CHART"
--namespace production --create-namespace
--set image.repository="$CI_REGISTRY_IMAGE"
--set image.tag="$CI_COMMIT_SHA"
--wait --timeout 10m
- kubectl rollout status deployment/"$HELM_RELEASE" -n production --timeout=5m
environment:
name: production
url: https://my-service.example.com
after_script:
- rm -f /tmp/kubeconfig
GitLab Agent for Kubernetes 可替代长期 kubeconfig,凭证由 GitLab 签发。生产 deploy 应绑定受保护分支和 Protected environment。
10. CI/CD最佳实践
.gitlab-ci.yml走 MR 评审;- lint/unit 前置,重集成测试放 nightly;
- 镜像 Tag 用
$CI_COMMIT_SHA或 semver,避免latest漂移; - build Runner 与 deploy Runner 分机或分 tag;
- CI 变量 Protect + Mask;Registry 用 deploy token;
- staging 完整演练后再对 production 启用 manual + tag rules。
演进路径:validate → test → build image → deploy staging(自动)→ deploy production(manual + tag)。