CI/CD 流水线设计:GitLab CI 最佳实践
一条好的 CI/CD 流水线能让团队每天安心发布十几次。本文分享经过生产验证的 GitLab CI 配置方案。
流水线整体设计
代码提交 → lint/test → build → 安全扫描 → 部署 staging → 手动确认 → 部署 production
原则:快速失败,越早发现问题越好,lint 和单测要在 2 分钟内完成。
基础结构
# .gitlab-ci.yml
stages:
- validate
- test
- build
- scan
- deploy-staging
- deploy-production
variables:
DOCKER_DRIVER: overlay2
IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
default:
image: docker:24
services:
- docker:24-dind
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
Lint 和代码检查
lint:
stage: validate
image: golangci/golangci-lint:latest # 以 Go 项目为例
script:
- golangci-lint run ./...
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == "main"
cache:
key: golangci-lint
paths:
- .golangci-lint-cache/
单元测试 + 覆盖率
test:
stage: test
image: golang:1.23
script:
- go test -v -race -coverprofile=coverage.out ./...
- go tool cover -func=coverage.out
coverage: '/total:\s+\(statements\)\s+(\d+\.\d+)%/'
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage.xml
expire_in: 1 week
cache:
key: go-modules-$CI_COMMIT_REF_SLUG
paths:
- .go/pkg/mod/
构建 Docker 镜像
build:
stage: build
script:
- |
docker build \
--cache-from $CI_REGISTRY_IMAGE:latest \
--build-arg BUILDKIT_INLINE_CACHE=1 \
--tag $IMAGE_TAG \
--tag $CI_REGISTRY_IMAGE:latest \
.
- docker push $IMAGE_TAG
- docker push $CI_REGISTRY_IMAGE:latest
rules:
- if: $CI_COMMIT_BRANCH == "main"
- if: $CI_COMMIT_TAG
安全扫描
container-scan:
stage: scan
image:
name: aquasec/trivy:latest
entrypoint: [""]
script:
- trivy image --exit-code 1 --severity HIGH,CRITICAL $IMAGE_TAG
allow_failure: true # 不阻断流水线,但会标记警告
needs: [build]
部署到 Staging
deploy-staging:
stage: deploy-staging
image: bitnami/kubectl:latest
environment:
name: staging
url: https://staging.example.com
script:
- kubectl config use-context $KUBE_CONTEXT_STAGING
- |
kubectl set image deployment/my-app \
app=$IMAGE_TAG \
-n staging
- kubectl rollout status deployment/my-app -n staging --timeout=5m
needs: [build, container-scan]
rules:
- if: $CI_COMMIT_BRANCH == "main"
手动确认后部署生产
deploy-production:
stage: deploy-production
image: bitnami/kubectl:latest
environment:
name: production
url: https://example.com
script:
- kubectl config use-context $KUBE_CONTEXT_PROD
- |
kubectl set image deployment/my-app \
app=$IMAGE_TAG \
-n production
- kubectl rollout status deployment/my-app -n production --timeout=10m
when: manual # 需要手动触发
needs: [deploy-staging]
rules:
- if: $CI_COMMIT_BRANCH == "main"
缓存优化
# 全局缓存配置
cache:
key: $CI_COMMIT_REF_SLUG
paths:
- node_modules/
- .npm/
policy: pull-push
失败通知
.notify-failure: ¬ify-failure
after_script:
- |
if [ "$CI_JOB_STATUS" == "failed" ]; then
curl -X POST $FEISHU_WEBHOOK \
-H 'Content-Type: application/json' \
-d "{\"msg_type\":\"text\",\"content\":{\"text\":\"流水线失败:$CI_PROJECT_NAME $CI_COMMIT_REF_NAME\n$CI_PIPELINE_URL\"}}"
fi
一条设计良好的流水线是团队工程效率的倍增器,值得花时间打磨。