GitLab CI/CD

GitLab CI/CD 是 GitLab 内置的持续集成和持续交付功能,通过 .gitlab-ci.yml 文件定义流水线,支持 Docker、Kubernetes、云原生构建和部署。

核心概念

概念 说明
Pipeline 完整的 CI/CD 流水线,包含多个 Stage
Stage 阶段,多个 Job 可以在同一阶段并行执行
Job 具体任务,如编译、测试、部署
Runner 执行 Job 的代理,支持共享和专用
Artifact 构建产物,在 Job 之间传递
Cache 缓存,用于加速重复构建
Environment 环境,追踪部署历史

基础配置

.gitlab-ci.yml 基础结构


stages:
  - build
  - test
  - deploy

variables:
  DOCKER_DRIVER: overlay2
  DOCKER_TLS_CERTDIR: ""

before_script:
  - echo "Before script"

after_script:
  - echo "After script"

build:
  stage: build
  image: maven:3.9-eclipse-temurin-17
  script:
    - mvn clean package -DskipTests
  artifacts:
    paths:
      - target/*.jar
    expire_in: 1 hour
  tags:
    - docker

test:unit:
  stage: test
  image: maven:3.9-eclipse-temurin-17
  script:
    - mvn test
  coverage: '/Total:.*?([0-9]{1,3})%/'
  artifacts:
    reports:
      junit: target/surefire-reports/*.xml
    paths:
      - target/surefire-reports/
    expire_in: 1 day
  tags:
    - docker

test:integration:
  stage: test
  image: maven:3.9-eclipse-temurin-17
  services:
    - postgres:15
    - redis:7
  script:
    - mvn verify -Pintegration-tests
  tags:
    - docker

deploy:staging:
  stage: deploy
  only:
    - main
  environment:
    name: staging
    url: https://staging.example.com
    on_stop: stop:staging
  script:
    - kubectl config use-context staging
    - kubectl apply -f k8s/staging/
  tags:
    - kubernetes

stop:staging:
  stage: deploy
  environment:
    name: staging
    action: stop
  script:
    - kubectl config use-context staging
    - kubectl delete -f k8s/staging/
  tags:
    - kubernetes
  when: manual

Docker 镜像构建


variables:
  IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA

build:docker:
  stage: build
  image: docker:24-dind
  services:
    - docker:24-dind
  before_script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
  script:
    - docker build -t $IMAGE_TAG .
    - docker push $IMAGE_TAG
  after_script:
    - docker logout $CI_REGISTRY
  tags:
    - docker
  artifacts:
    reports:
      trivy: trivy-report.json
  only:
    - main
    - tags

多分支策略


stages:
  - build
  - test
  - deploy

# MR 合并请求流水线
merge_request:
  stage: build
  image: node:20-alpine
  script:
    - npm ci
    - npm run build
    - npm run test
  only:
    - merge_requests
  rules:
    - if: $CI_MERGE_REQUEST_ID

# 主分支生产部署
deploy:production:
  stage: deploy
  script:
    - echo "Deploying to production"
  environment:
    name: production
    url: https://example.com
  only:
    - main
  when: manual
  rules:
    - if: $CI_COMMIT_BRANCH == "main"

# 标签发布
release:
  stage: release
  image: ubuntu:22.04
  before_script:
    - apt-get update && apt-get install -y curl
  script:
    - |
      curl --request POST \
        --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
        --data "tag_name=$CI_COMMIT_TAG" \
        --data "description=Release $CI_COMMIT_TAG" \
        "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/releases"
  only:
    - tags
  rules:
    - if: $CI_COMMIT_TAG

Kubernetes 部署


deploy:kubernetes:
  stage: deploy
  image: bitnami/kubectl:latest
  services:
    - docker:24-dind
  before_script:
    - kubectl config use-context $KUBECTL_CONTEXT
    - echo $KUBE_CERT | base64 -d > /tmp/ca.crt
  script:
    - kubectl set image deployment/myapp myapp=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
    - kubectl rollout status deployment/myapp
  environment:
    name: production
    url: https://myapp.example.com
    kubernetes:
      namespace: production
  only:
    - main
  rules:
    - if: $CI_COMMIT_BRANCH == "main"

依赖缓存


cache:
  key: ${CI_COMMIT_REF_SLUG}
  paths:
    - .m2/repository
    - node_modules/
    - .npm/

maven:build:
  stage: build
  image: maven:3.9-eclipse-temurin-17
  cache:
    key: maven-${CI_COMMIT_REF_SLUG}
    paths:
      - .m2/repository
    policy: pull-push
  script:
    - mvn clean package

npm:build:
  stage: build
  image: node:20-alpine
  script:
    - npm ci
    - npm run build
  cache:
    key: npm-${CI_COMMIT_REF_SLUG}
    paths:
      - node_modules/
    policy: pull-push

Matrix Jobs(矩阵构建)


build:matrix:
  stage: build
  image: docker:24-dind
  services:
    - docker:24-dind
  parallel:
    matrix:
      - SERVICE: api
        VERSION: v1
      - SERVICE: api
        VERSION: v2
      - SERVICE: web
        VERSION: v1
      - SERVICE: web
        VERSION: v2
  script:
    - docker build -t myapp/${SERVICE}:${VERSION} .
    - docker push myapp/${SERVICE}:${VERSION}

GitLab Runner 配置

Docker Executor


[[runners]]
  name = "docker-runner"
  url = "https://gitlab.example.com"
  token = "<runner-token>"
  executor = "docker"
  [runners.cache]
    MaxUploadedArchiveSize = 104857600
  [runners.docker]
    tls_verify = false
    image = "docker:24-dind"
    privileged = true
    disable_cache = false
    volumes = ["/cache", "/certs/client"]
    shm_size = 0

Kubernetes Executor


# gitlab-runner-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: gitlab-runner-config
data:
  config.toml: |
    [[runners]]
      name = "kubernetes-runner"
      url = "https://gitlab.example.com"
      token = "<runner-token>"
      executor = "kubernetes"
      [runners.kubernetes]
        namespace = "gitlab-ci"
        image = "ubuntu:22.04"
        privileged = false
        cpu_limit = "2"
        memory_limit = "4Gi"
        cpu_request = "1"
        memory_request = "2Gi"
        service_cpu_limit = "1"
        service_memory_limit = "1Gi"
        helper_cpu_limit = "500m"
        helper_memory_limit = "256Mi"
        [runners.kubernetes.pull_policy]
          policy = "if-not-present"

高级特性

DAG 调度(跳过 Stage)


stages:
  - build
  - test
  - deploy

# 使用 needs 跳过空 Stage,实现 DAG 调度
build:
  stage: build
  script: make build

test:unit:
  stage: test
  needs: [build]
  script: make test:unit

test:integration:
  stage: test
  needs: [build]
  script: make test:integration

deploy:
  stage: deploy
  needs: [test:unit, test:integration]
  script: make deploy

父子流水线


# .gitlab-ci.yml(父流水线)
trigger:child:
  stage: build
  trigger:
    include:
      - local: pipelines/child-pipeline.yml
    strategy: depend

CI/CD Variable 权限级别

变量类型 说明 用途示例
Unprotected 无分支限制 Docker Registry
Protected 仅保护分支可用 生产环境密钥
Masked 输出中隐藏值 密码、Token

故障排除


# 本地调试流水线
gitlab-runner exec docker --docker-privileged build

# 查看 Runner 日志
sudo journalctl -u gitlab-runner -f

# 重新注册 Runner
gitlab-runner register \
  --url https://gitlab.example.com \
  --token <token> \
  --description "docker-runner" \
  --executor docker \
  --docker-image "docker:24-dind"

最佳实践

  • 流水线模块化:使用 include 引用共享 CI 配置
  • 增量测试:优先执行快速单元测试,缓慢集成测试后置
  • 构建缓存:充分利用 Maven/npm 本地仓库加速构建
  • 并行执行:使用 parallel:matrixneeds 实现并行
  • 产物清理:设置合理的 expire_in,避免存储浪费
  • Secret 管理:使用 GitLab CI/CD Variable,不在代码中硬编码

下一步