GitLab CI/CD 最佳实践
推荐标准三段式:
stages:
- lint
- test
- build
- deploy
理由:
- linters 提前失败 -> 节省 Runner 资源
- 单元测试通过后才允许打包
- 最后才触发部署
- ❌ 反例:在同一个 job 里 lint + test + build
- ✅ 推荐:每个 job 独立,如 lint, unit_test, build_image
避免:
- 修改本地状态
- 写入共享目录
- 依赖上一次构建
确保 Runner、机器差异不会影响构建:
image: python:3.12
before_script:
- pip install -r requirements.txt
典型用法:
cache:
paths:
- .cache/pip/
不要缓存 build 产物(artifact 适合这个场景)。
.default_job: &default_job
tags: [docker]
retry: 2
before_script:
- pip install -r requirements.txt
test:
<<: *default_job
script:
- pytest
好处:
- ✨ 结构更干净
- ✨ 批量修改成本低
rules 可精确控制逻辑:
deploy_prod:
stage: deploy
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
- when: manual
比 only: [main] 更灵活(可加条件)。
unit_test:
stage: test
lint:
stage: test
build:
stage: build
needs: ["unit_test", "lint"]
好处:
- test 和 lint 并行
- 只有 test + lint 成功才 build
build:
stage: build
script:
- npm run build
artifacts:
paths:
- dist/
不要滥用 artifact,否则 CI 会变慢。
不要把 token、密码写进 YAML。
如:
- AWS_KEY
- K8S_TOKEN
- PROD_DB_PASSWORD
不要写在代码里
不要放在 artifact 中
- 部署用独立 Runner
- 构建用普通 Runner
- 避免一个 job 的安全问题影响其他 job
deploy_prod:
when: manual
environment:
name: production
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
只允许 Maintainer 触发。
Docker runner 隔离强、安全、可控。
如 Python/Node/Go 都可以缓存依赖。
比一个超级大 Job 更快且更稳定。
主文件:
include:
- local: ".gitlab/ci/lint.yml"
- local: ".gitlab/ci/docker.yml"
- local: ".gitlab/ci/deploy.yml"
方便管理大型 CI 项目。
build:
stage: build
description: "构建 Docker 镜像并推送到仓库"
网络波动、偶发失败常见。
retry: 2
- Python -> flake8 / black
- JS -> eslint
- Go -> go fmt
遇到问题立即 fail 省资源。
使用 coverage 工具:
coverage: '/^TOTAL.+?(\d+\%)$/'
environment:
name: staging
url: https://staging.example.com
不要在 Runner 上 build 再部署(不稳定)。
如自动构建镜像、定时备份等。
微服务项目非常常见:
trigger:
project: group/subproject
branch: main
| 反模式 | 风险 |
|---|---|
| job 里写多个职责 | 不可维护,难排错 |
| 不使用 cache | CI 超慢 |
| 使用 shell runner | 不安全 |
| 重复拷贝 job 代码 | 无法维护 |
| 把密码写进 .gitlab-ci.yml | 安全灾难 |
| 随意使用 artifact | CI 变慢,存储膨胀 |
| 部署 job 自动执行 | 误触生产环境 |
stages:
- lint
- test
- build
- deploy
.default: &default
tags: [docker]
retry: 2
lint:
<<: *default
stage: lint
script:
- npm run lint
test:
<<: *default
stage: test
needs: ["lint"]
script:
- npm run test
build:
<<: *default
stage: build
needs: ["test"]
script:
- npm run build
artifacts:
paths:
- dist/
deploy_prod:
<<: *default
stage: deploy
needs: ["build"]
when: manual
environment:
name: production
script:
- ./deploy.sh