实战示例与排障
示例 1:测试 + 构建(通用骨架)
stages: [test, build]
default:
image: golang:1.24
test:
script:
- go vet ./...
- go test -race ./...
cache:
key: go-mod-$CI_COMMIT_REF_SLUG
paths: [.go-mod-cache/]
variables:
GOMODCACHE: $CI_PROJECT_DIR/.go-mod-cache
build:
needs: [test]
script: go build -o app ./cmd/app
artifacts:
paths: [app]
expire_in: 1 weekPython 项目把 cache paths 换成
.venv/,Node 项目换成node_modules/或 npm/yarn 缓存目录,key 思路相同。
示例 2:构建并推送 Docker 镜像
方式 A:kaniko(推荐,无需 privileged)
build-image:
stage: build
image:
name: gcr.io/kaniko-project/executor:debug
entrypoint: [""]
script:
- mkdir -p /kaniko/.docker
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
- /kaniko/executor
--context "$CI_PROJECT_DIR"
--dockerfile "$CI_PROJECT_DIR/Dockerfile"
--destination "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA"
--destination "$CI_REGISTRY_IMAGE:latest"方式 B:docker-in-docker(需要 Runner privileged)
build-image:
stage: build
image: docker:27
services:
- docker:27-dind
variables:
DOCKER_HOST: tcp://docker:2375
DOCKER_TLS_CERTDIR: ""
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA示例 3:部署到 Kubernetes
kubeconfig 存为 File 类型变量 KUBE_CONFIG(Settings → CI/CD → Variables):
deploy-prod:
stage: deploy
image: bitnami/kubectl:latest
needs: [build-image]
environment:
name: production
url: https://app.example.com
rules:
- if: $CI_COMMIT_TAG
before_script:
- mkdir -p ~/.kube
- cp $KUBE_CONFIG ~/.kube/config
script:
- kubectl set image deployment/app app=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA -n prod
- kubectl rollout status deployment/app -n prod --timeout=180s要点:
environment让部署记录出现在 Operate → Environments,支持回滚与 stop action- 多 Job 并发部署同一环境时加
resource_group: production互斥 - 更安全的替代:GitLab Agent for Kubernetes(集群内拉模式,CI 里不存 kubeconfig)
示例 4:Job 间传变量(dotenv)
build:
script:
- export BUILD_VERSION=1.2.3-$CI_COMMIT_SHORT_SHA
- echo "BUILD_VERSION=$BUILD_VERSION" >> build.env
artifacts:
reports:
dotenv: build.env
deploy:
needs: [build]
script: echo "deploying $BUILD_VERSION" # 自动注入为环境变量示例 5:多项目流水线
trigger-backend:
stage: deploy
trigger:
project: mygroup/backend
branch: main
strategy: depend # 等待下游结束并继承其状态
forward:
yaml_variables: true # 把上游变量传给下游artifacts vs cache
| artifacts | cache | |
|---|---|---|
| 用途 | Stage/Job 间传递产物、对外下载 | 加速依赖下载(可丢失) |
| 可靠性 | 上传到 GitLab,保证可用 | 尽力而为,miss 属正常 |
| 典型内容 | 二进制、测试报告、dotenv | node_modules、go mod、pip 缓存 |
cache key 建议绑定锁文件或分支(key: files: [package-lock.json]、prefix: $CI_COMMIT_REF_SLUG);多 Runner 共享缓存需 S3/MinIO 后端。
排障工具箱
| 症状 | 排查 |
|---|---|
| Job 一直 pending(stuck) | 项目无可用 Runner,或 tags 不匹配;Settings → CI/CD → Runners 看状态 |
| 某 Job 根本没出现 | rules 没命中;Pipeline editor 逐条验证 |
| 变量值为空 | Protected 变量 + 非 protected 分支;或 Mask 格式不合法没保存成功 |
sh: xxx: command not found | image 缺工具,换镜像或在 before_script 安装 |
| DinD 连不上 daemon | 确认 DOCKER_HOST=tcp://docker:2375 与 DOCKER_TLS_CERTDIR="" 配套;Runner 需 privileged |
| 缓存不命中 | key 变了(锁文件变化)、换了 Runner 机器且缓存非共享 |
| 同一次提交跑了多条流水线 | 缺 workflow:rules(见 cicd-yaml-syntax) |
| 需要现场调试 | Job 运行中点页面右侧 Terminal,进入交互式 Web Terminal(docker/k8s executor) |
其他技巧:
- Pipeline editor 自带 CI Lint,改 yml 先 Validate 再提交
- 失败 Job 直接 Retry;manual job 支持 Run with variables 临时注入变量
CI_DEBUG_TRACE: "true"打印变量展开过程(用完删掉)- artifacts 一律加
expire_in,避免存储配额告警
最佳实践清单
- 用
rules代替only/except(后者已是 legacy) - 用
workflow:rules防止分支 + MR 双流水线 - 镜像固定版本 tag(
golang:1.24而非latest),关键构建 pin 到 digest interruptible: true让过期 Job 可被自动取消,节省 Runner- Job 粒度小、单一职责;用
needs尽早并行 - 敏感信息一律走 CI/CD Variables(Mask + 最小权限),不进 yml
- 部署 Job 加
environment+resource_group,保留审计与互斥
相关
- cicd-overview:概念与快速上手
- cicd-yaml-syntax:语法参考
- cicd-variables:变量体系
- cicd-runner:Runner 部署