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: &notify-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

一条设计良好的流水线是团队工程效率的倍增器,值得花时间打磨。

← 返回文章列表