Skip to content

GitLab CI YAML 配置深度解析

GitLab CI使用YAML格式的.gitlab-ci.yml文件定义CI/CD流水线,这个配置文件是整个自动化流程的核心。本文详细解析YAML配置的各个组件、语法规则、高级特性和实战配置模式。

📋 YAML 配置基础语法

核心配置元素

yaml
# .gitlab-ci.yml 基础结构
# 全局配置段
image: node:16-alpine                    # 默认镜像
services:                               # 全局服务
  - redis:6-alpine
  - postgres:13

variables:                              # 全局变量
  NODE_ENV: "production"
  API_VERSION: "v1"

stages:                                 # 流水线阶段定义
  - build
  - test
  - security
  - deploy
  - cleanup

# 全局脚本钩子
before_script:                          # 每个作业前执行
  - echo "Starting pipeline"
  - date

after_script:                           # 每个作业后执行
  - echo "Job completed"
  - cleanup_temp_files

# 作业定义
build_job:                              # 作业名称
  stage: build                          # 所属阶段
  image: maven:3.8-openjdk-11          # 作业专用镜像
  script:                              # 执行脚本
    - mvn clean compile
    - mvn package
  artifacts:                           # 构件配置
    paths:
      - target/*.jar
    expire_in: 1 hour
  cache:                               # 缓存配置
    key: maven-$CI_COMMIT_REF_NAME
    paths:
      - .m2/repository/
  rules:                               # 执行条件
    - if: $CI_COMMIT_BRANCH == "main"
yaml
# GitLab CI变量类型和作用域
variables:
  # 全局变量
  GLOBAL_VAR: "global_value"
  
  # 环境变量
  DATABASE_URL: "postgres://localhost:5432/myapp"
  
  # 多行变量
  MULTI_LINE_VAR: |
    This is a multi-line
    variable that can span
    multiple lines
  
  # 引用其他变量
  APP_URL: "https://$CI_PROJECT_NAME.example.com"
  
  # 条件变量
  BUILD_ENV: 
    value: "production"
    description: "Build environment"

# 作业级变量
job_with_variables:
  variables:
    # 作业专用变量
    JOB_SPECIFIC_VAR: "job_value"
    # 覆盖全局变量
    GLOBAL_VAR: "overridden_value"
  script:
    - echo "Global var: $GLOBAL_VAR"
    - echo "Job var: $JOB_SPECIFIC_VAR"

# 预定义变量使用
predefined_variables_example:
  script:
    - echo "Pipeline ID: $CI_PIPELINE_ID"
    - echo "Commit SHA: $CI_COMMIT_SHA"
    - echo "Branch: $CI_COMMIT_BRANCH"
    - echo "Project name: $CI_PROJECT_NAME"
    - echo "Job stage: $CI_JOB_STAGE"
    - echo "Runner description: $CI_RUNNER_DESCRIPTION"

# 受保护变量
protected_variables_usage:
  script:
    - |
      if [ "$CI_COMMIT_REF_PROTECTED" = "true" ]; then
        echo "Using protected variables"
        echo "Production token: $PROD_API_TOKEN"
        echo "SSH key: $PROD_SSH_KEY"
      else
        echo "Using development variables"
        echo "Dev token: $DEV_API_TOKEN"
      fi
  rules:
    - if: $CI_COMMIT_BRANCH == "main"

# 文件变量
file_variables:
  variables:
    # 引用文件内容作为变量
    CONFIG_FILE: file
    CERTIFICATE: file
  script:
    - cat $CONFIG_FILE
    - openssl x509 -in $CERTIFICATE -text -noout
yaml
# rules: 现代条件控制语法
rules_examples:
  # 基于分支的规则
  deploy_main:
    script: echo "Deploying to production"
    rules:
      - if: $CI_COMMIT_BRANCH == "main"
      - if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/

  # 基于变更的规则
  test_backend:
    script: echo "Testing backend"
    rules:
      - changes:
          - backend/**/*
          - api/**/*
      - if: $CI_PIPELINE_SOURCE == "merge_request_event"

  # 复杂条件组合
  security_scan:
    script: echo "Running security scan"
    rules:
      - if: $CI_COMMIT_BRANCH == "main"
        when: always
      - if: $CI_PIPELINE_SOURCE == "merge_request_event"
        when: manual
        allow_failure: true
      - when: never  # 默认不执行

  # 变量条件
  conditional_deploy:
    script: echo "Conditional deployment"
    rules:
      - if: $DEPLOY_ENABLED == "true" && $CI_COMMIT_BRANCH == "main"
      - if: $CI_COMMIT_TAG && $TAG_DEPLOY == "true"

# only/except: 传统条件控制(已弃用,建议使用rules)
legacy_conditions:
  deploy_legacy:
    script: echo "Legacy deployment"
    only:
      - main
      - /^release\/.*$/
    except:
      - schedules
      
  test_legacy:
    script: echo "Legacy testing"
    only:
      refs:
        - merge_requests
        - main
      changes:
        - src/**/*
      variables:
        - $ENABLE_TESTS == "true"

# when: 作业执行时机
execution_timing:
  # 自动执行
  auto_job:
    script: echo "Auto execution"
    when: on_success  # 默认值

  # 手动执行
  manual_job:
    script: echo "Manual execution"
    when: manual

  # 总是执行
  cleanup_job:
    script: echo "Always cleanup"
    when: always

  # 失败时执行
  failure_job:
    script: echo "On failure"
    when: on_failure

  # 延迟执行
  delayed_job:
    script: echo "Delayed execution"
    when: delayed
    start_in: 30 minutes

高级配置特性

yaml
# 并行执行配置
parallel_jobs:
  # 基础并行
  simple_parallel:
    script: echo "Parallel job $CI_NODE_INDEX of $CI_NODE_TOTAL"
    parallel: 5

  # 矩阵并行
  matrix_build:
    script:
      - echo "Testing with $RUBY_VERSION and $DATABASE"
      - bundle install
      - bundle exec rspec
    parallel:
      matrix:
        - RUBY_VERSION: ["2.7", "3.0", "3.1"]
          DATABASE: ["mysql", "postgres"]
        - RUBY_VERSION: "3.2"
          DATABASE: "sqlite"
    
  # 复杂矩阵配置
  complex_matrix:
    image: $IMAGE
    script:
      - echo "OS: $OS, Version: $VERSION, Arch: $ARCH"
      - run_tests.sh
    parallel:
      matrix:
        - IMAGE: "ubuntu:20.04"
          OS: "ubuntu"
          VERSION: ["20.04", "22.04"]
          ARCH: ["amd64", "arm64"]
        - IMAGE: "alpine:3.16"
          OS: "alpine" 
          VERSION: "3.16"
          ARCH: "amd64"

# 动态并行
dynamic_parallel:
  generate_jobs:
    stage: prepare
    script:
      - |
        # 生成动态作业配置
        echo "Generating parallel jobs..."
        for i in {1..10}; do
          echo "test_job_$i:" >> generated-jobs.yml
          echo "  script: echo 'Dynamic job $i'" >> generated-jobs.yml
          echo "  parallel: 1" >> generated-jobs.yml
        done
    artifacts:
      paths:
        - generated-jobs.yml

  # 包含生成的作业
  include:
    - local: generated-jobs.yml

# 资源配置
resource_management:
  heavy_job:
    script: echo "Heavy computation"
    tags:
      - high-memory
      - gpu
    resource_group: heavy-jobs  # 资源组限制
    
  light_job:
    script: echo "Light task"
    tags:
      - shared-runner
yaml
# needs: DAG流水线依赖
dependency_management:
  # 简单依赖
  compile:
    stage: build
    script: gcc -o app main.c
    artifacts:
      paths:
        - app

  test:
    stage: test
    needs: ["compile"]  # 只依赖compile作业
    script: ./app --test

  # 跨阶段依赖
  integration_test:
    stage: test
    needs:
      - job: compile
        artifacts: true  # 下载构件
      - job: prepare_data
        artifacts: false # 不下载构件,仅等待完成
    script:
      - ./app --integration-test
      - use_prepared_data.sh

  # 条件依赖
  deploy:
    stage: deploy
    needs:
      - job: test
        artifacts: false
      - job: security_scan
        artifacts: false
        optional: true  # 可选依赖
    script: deploy_application.sh

# 构件管理
artifacts_configuration:
  build_artifacts:
    script: 
      - make build
      - make docs
    artifacts:
      # 基础配置
      paths:
        - build/
        - docs/
      expire_in: 1 week
      when: on_success
      
      # 构件名称
      name: "$CI_JOB_NAME-$CI_COMMIT_REF_NAME"
      
      # 排除文件
      exclude:
        - build/**/*.tmp
        - docs/draft/
      
      # 报告类型
      reports:
        junit: test-results.xml
        coverage_report:
          coverage_format: cobertura
          path: coverage/cobertura-coverage.xml
        codequality: codequality.json
        sast: security-report.json
        dependency_scanning: dependency-scanning.json
        container_scanning: container-scanning.json
        dast: dast-report.json
        license_scanning: license-scanning.json
        performance: performance.json
        load_performance: load-performance.json
        metrics: metrics.txt

  # 动态构件路径
  dynamic_artifacts:
    script:
      - build_version=$(cat VERSION)
      - make build
      - tar -czf "app-${build_version}.tar.gz" build/
    artifacts:
      paths:
        - "app-*.tar.gz"
      expire_in: 30 days

# 缓存配置
cache_strategies:
  # 基础缓存
  basic_cache:
    cache:
      key: "$CI_COMMIT_REF_NAME"
      paths:
        - node_modules/
        - .npm/
    script:
      - npm ci --cache .npm
      - npm run build

  # 多级缓存
  multilevel_cache:
    cache:
      - key: 
          files:
            - package-lock.json
        paths:
          - node_modules/
      - key: "$CI_COMMIT_REF_NAME"
        paths:
          - .npm/
        policy: pull  # 只拉取,不推送
    script:
      - npm ci --cache .npm
      - npm run build

  # 分布式缓存
  distributed_cache:
    cache:
      key: "global-cache"
      paths:
        - vendor/
      policy: pull-push
    script:
      - composer install --no-dev --optimize-autoloader
yaml
# 容器镜像配置
image_configuration:
  # 全局镜像
  image: 
    name: node:16-alpine
    entrypoint: [""]  # 覆盖默认入口点

  # 作业级镜像
  custom_image_job:
    image:
      name: custom-registry.com/my-image:latest
      pull_policy: always
    script: echo "Using custom image"

  # 私有镜像仓库
  private_registry_job:
    image:
      name: private.registry.com/app:$CI_COMMIT_SHA
      pull_policy: if-not-present
    before_script:
      - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    script: echo "Using private registry image"

# 服务配置
services_configuration:
  # 全局服务
  services:
    - name: postgres:13
      alias: database
      variables:
        POSTGRES_DB: testdb
        POSTGRES_USER: testuser
        POSTGRES_PASSWORD: testpass
    - redis:6-alpine
    - name: elasticsearch:7.17.0
      alias: search
      command: ["elasticsearch", "-Xms512m", "-Xmx512m"]

  # 作业级服务
  integration_test:
    services:
      - name: mysql:8.0
        alias: mysql
        variables:
          MYSQL_ROOT_PASSWORD: rootpass
          MYSQL_DATABASE: testdb
      - name: mongo:5.0
        alias: mongodb
        variables:
          MONGO_INITDB_ROOT_USERNAME: root
          MONGO_INITDB_ROOT_PASSWORD: rootpass
    script:
      - echo "Testing with MySQL and MongoDB"
      - mysql -h mysql -u root -prootpass -e "SHOW DATABASES;"
      - mongo --host mongodb --username root --password rootpass

# 网络配置
network_configuration:
  custom_network:
    image: docker:20.10.16
    services:
      - name: docker:20.10.16-dind
        alias: docker
        command: ["dockerd", "--host=tcp://0.0.0.0:2376"]
    variables:
      DOCKER_HOST: tcp://docker:2376
    script:
      - docker info
      - docker build -t test-image .

# 资源限制
resource_limits:
  memory_cpu_limits:
    image: python:3.9
    script:
      - python memory_intensive_task.py
    variables:
      # 容器资源限制
      KUBERNETES_MEMORY_LIMIT: "2Gi"
      KUBERNETES_CPU_LIMIT: "1000m" 
      KUBERNETES_MEMORY_REQUEST: "1Gi"
      KUBERNETES_CPU_REQUEST: "500m"

🔧 实战配置模式

企业级配置模板

yaml
# Java Spring Boot应用完整流水线
stages:
  - validate
  - build
  - test
  - security
  - package
  - deploy

variables:
  MAVEN_OPTS: "-Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository"
  MAVEN_CLI_OPTS: "--batch-mode --errors --fail-at-end --show-version"
  DOCKER_REGISTRY: "harbor.company.com"
  APP_NAME: "spring-boot-app"

# 模板定义
.java_template: &java_template
  image: maven:3.8-openjdk-11
  cache:
    key: maven-$CI_COMMIT_REF_NAME
    paths:
      - .m2/repository/
  before_script:
    - mkdir -p .m2/repository

# 代码质量验证
validate:
  <<: *java_template
  stage: validate
  script:
    - mvn $MAVEN_CLI_OPTS validate
    - mvn $MAVEN_CLI_OPTS spotbugs:check
    - mvn $MAVEN_CLI_OPTS checkstyle:check
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_COMMIT_BRANCH == "main"

# 编译构建
compile:
  <<: *java_template
  stage: build
  script:
    - mvn $MAVEN_CLI_OPTS clean compile
  artifacts:
    paths:
      - target/classes/
    expire_in: 1 hour

# 单元测试
unit_tests:
  <<: *java_template
  stage: test
  needs: ["compile"]
  script:
    - mvn $MAVEN_CLI_OPTS test
  coverage: '/Total.*?([0-9]{1,3})%/'
  artifacts:
    reports:
      junit:
        - target/surefire-reports/TEST-*.xml
      coverage_report:
        coverage_format: jacoco
        path: target/site/jacoco/jacoco.xml
    paths:
      - target/surefire-reports/
    expire_in: 1 day

# 集成测试
integration_tests:
  <<: *java_template
  stage: test
  services:
    - name: postgres:13
      alias: database
      variables:
        POSTGRES_DB: testdb
        POSTGRES_USER: testuser
        POSTGRES_PASSWORD: testpass
    - redis:6-alpine
  variables:
    SPRING_PROFILES_ACTIVE: integration
    DATABASE_URL: "jdbc:postgresql://database:5432/testdb"
    REDIS_URL: "redis://redis:6379"
  script:
    - mvn $MAVEN_CLI_OPTS verify -Pintegration-tests
  artifacts:
    reports:
      junit:
        - target/failsafe-reports/TEST-*.xml
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
      when: manual

# 安全扫描
security_scan:
  <<: *java_template
  stage: security
  script:
    - mvn $MAVEN_CLI_OPTS dependency-check:check
    - mvn $MAVEN_CLI_OPTS org.sonarsource.scanner.maven:sonar-maven-plugin:sonar
  artifacts:
    reports:
      dependency_scanning: target/dependency-check-report.json
  variables:
    SONAR_HOST_URL: $SONAR_HOST_URL
    SONAR_TOKEN: $SONAR_TOKEN
  allow_failure: true

# 应用打包
package:
  <<: *java_template
  stage: package
  needs: ["unit_tests"]
  script:
    - mvn $MAVEN_CLI_OPTS package -DskipTests
    - echo "APP_VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)" >> version.env
  artifacts:
    paths:
      - target/*.jar
    reports:
      dotenv: version.env
    expire_in: 1 week

# Docker镜像构建
build_image:
  stage: package
  image: docker:20.10.16
  services:
    - docker:20.10.16-dind
  needs: ["package"]
  variables:
    DOCKER_DRIVER: overlay2
    DOCKER_TLS_CERTDIR: "/certs"
  before_script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $DOCKER_REGISTRY
  script:
    - docker build -t $DOCKER_REGISTRY/$CI_PROJECT_PATH:$CI_COMMIT_SHA .
    - docker build -t $DOCKER_REGISTRY/$CI_PROJECT_PATH:$APP_VERSION .
    - docker push $DOCKER_REGISTRY/$CI_PROJECT_PATH:$CI_COMMIT_SHA
    - docker push $DOCKER_REGISTRY/$CI_PROJECT_PATH:$APP_VERSION
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
    - if: $CI_COMMIT_TAG

# 部署到开发环境
deploy_dev:
  stage: deploy
  image: alpine/helm:latest
  needs: ["build_image"]
  variables:
    ENVIRONMENT: "development"
    NAMESPACE: "app-dev"
  before_script:
    - kubectl config use-context $DEV_KUBE_CONTEXT
  script:
    - |
      helm upgrade --install $APP_NAME ./helm-chart \
        --namespace $NAMESPACE \
        --create-namespace \
        --set image.repository=$DOCKER_REGISTRY/$CI_PROJECT_PATH \
        --set image.tag=$CI_COMMIT_SHA \
        --set environment=$ENVIRONMENT \
        --wait --timeout 10m
  environment:
    name: development
    url: https://app-dev.company.com
    on_stop: stop_dev
  rules:
    - if: $CI_COMMIT_BRANCH == "develop"

# 部署到生产环境
deploy_prod:
  stage: deploy
  image: alpine/helm:latest
  needs: ["security_scan"]
  variables:
    ENVIRONMENT: "production"
    NAMESPACE: "app-prod"
  before_script:
    - kubectl config use-context $PROD_KUBE_CONTEXT
  script:
    - |
      helm upgrade --install $APP_NAME ./helm-chart \
        --namespace $NAMESPACE \
        --set image.repository=$DOCKER_REGISTRY/$CI_PROJECT_PATH \
        --set image.tag=$APP_VERSION \
        --set environment=$ENVIRONMENT \
        --set resources.requests.memory=1Gi \
        --set resources.limits.memory=2Gi \
        --wait --timeout 15m
  environment:
    name: production
    url: https://app.company.com
  rules:
    - if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/
      when: manual

# 停止环境
stop_dev:
  stage: deploy
  image: alpine/helm:latest
  script:
    - helm uninstall $APP_NAME --namespace app-dev
  environment:
    name: development
    action: stop
  when: manual
yaml
# 前端React/Vue应用流水线
stages:
  - deps
  - build
  - test
  - security
  - deploy

variables:
  NODE_VERSION: "18"
  NPM_CACHE_FOLDER: "$CI_PROJECT_DIR/.npm"
  CYPRESS_CACHE_FOLDER: "$CI_PROJECT_DIR/.cypress"

# 依赖安装
install_deps:
  stage: deps
  image: node:$NODE_VERSION-alpine
  cache:
    key:
      files:
        - package-lock.json
    paths:
      - node_modules/
      - $NPM_CACHE_FOLDER
  script:
    - npm ci --cache $NPM_CACHE_FOLDER --prefer-offline
  artifacts:
    paths:
      - node_modules/
    expire_in: 1 hour

# 代码质量检查
lint:
  stage: build
  image: node:$NODE_VERSION-alpine
  needs: ["install_deps"]
  script:
    - npm run lint
    - npm run type-check
  artifacts:
    reports:
      codequality: lint-report.json

# 构建应用
build_app:
  stage: build
  image: node:$NODE_VERSION-alpine
  needs: ["install_deps"]
  script:
    - npm run build
    - echo "BUILD_SIZE=$(du -sh dist/ | cut -f1)" >> build.env
  artifacts:
    paths:
      - dist/
    reports:
      dotenv: build.env
    expire_in: 1 day
  variables:
    NODE_ENV: "production"

# 单元测试
unit_tests:
  stage: test
  image: node:$NODE_VERSION-alpine
  needs: ["install_deps"]
  script:
    - npm run test:unit -- --coverage --watchAll=false
  coverage: '/All files[^|]*\|[^|]*\s+([\d\.]+)/'
  artifacts:
    reports:
      junit: test-results.xml
      coverage_report:
        coverage_format: cobertura
        path: coverage/cobertura-coverage.xml
    paths:
      - coverage/

# E2E测试
e2e_tests:
  stage: test
  image: cypress/included:12.0.0
  needs: ["build_app"]
  services:
    - name: nginx:alpine
      alias: app-server
  cache:
    key: cypress-$CI_COMMIT_REF_NAME
    paths:
      - $CYPRESS_CACHE_FOLDER
  before_script:
    - cp -r dist/* /usr/share/nginx/html/
    - nginx -g 'daemon off;' &
    - sleep 5
  script:
    - cypress run --config baseUrl=http://app-server
  artifacts:
    when: on_failure
    paths:
      - cypress/screenshots/
      - cypress/videos/
    expire_in: 1 week
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
      when: manual

# 安全扫描
security_audit:
  stage: security
  image: node:$NODE_VERSION-alpine
  needs: ["install_deps"]
  script:
    - npm audit --audit-level high
    - npx retire --js --node
  allow_failure: true
  artifacts:
    reports:
      dependency_scanning: security-audit.json

# 部署到CDN
deploy_cdn:
  stage: deploy
  image: amazon/aws-cli:latest
  needs: ["build_app", "unit_tests"]
  before_script:
    - aws configure set aws_access_key_id $AWS_ACCESS_KEY_ID
    - aws configure set aws_secret_access_key $AWS_SECRET_ACCESS_KEY
    - aws configure set region $AWS_DEFAULT_REGION
  script:
    - aws s3 sync dist/ s3://$S3_BUCKET_NAME --delete
    - aws cloudfront create-invalidation --distribution-id $CLOUDFRONT_DISTRIBUTION_ID --paths "/*"
  environment:
    name: production
    url: https://app.example.com
  rules:
    - if: $CI_COMMIT_BRANCH == "main"

# 性能测试
lighthouse_audit:
  stage: deploy
  image: markhobson/node-chrome:latest
  needs: ["deploy_cdn"]
  script:
    - npm install -g lighthouse
    - lighthouse https://app.example.com --output json --output-path lighthouse-report.json
    - lighthouse https://app.example.com --output html --output-path lighthouse-report.html
  artifacts:
    reports:
      performance: lighthouse-report.json
    paths:
      - lighthouse-report.html
    expire_in: 1 week
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
      when: manual
yaml
# 微服务单体仓库(Monorepo)流水线
stages:
  - changes
  - build
  - test
  - deploy

variables:
  DOCKER_REGISTRY: "harbor.company.com"
  KUBERNETES_NAMESPACE: "microservices"

# 检测变更服务
detect_changes:
  stage: changes
  image: alpine/git:latest
  script:
    - |
      # 检测每个服务的变更
      services=(user-service order-service payment-service notification-service)
      changed_services=()
      
      for service in "${services[@]}"; do
        if git diff --quiet HEAD~1 HEAD -- "services/$service/"; then
          echo "No changes in $service"
        else
          echo "Changes detected in $service"
          changed_services+=("$service")
        fi
      done
      
      # 生成动态变量
      echo "CHANGED_SERVICES=${changed_services[*]}" > changes.env
      echo "TOTAL_CHANGES=${#changed_services[@]}" >> changes.env
      
      # 为每个变更的服务生成构建标志
      for service in "${changed_services[@]}"; do
        service_upper=$(echo $service | tr '[:lower:]' '[:upper:]' | tr '-' '_')
        echo "BUILD_${service_upper}=true" >> changes.env
      done
  artifacts:
    reports:
      dotenv: changes.env

# 服务构建模板
.build_service: &build_service
  stage: build
  image: docker:20.10.16
  services:
    - docker:20.10.16-dind
  before_script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $DOCKER_REGISTRY
  script:
    - |
      cd services/$SERVICE_NAME
      docker build -t $DOCKER_REGISTRY/$CI_PROJECT_PATH/$SERVICE_NAME:$CI_COMMIT_SHA .
      docker push $DOCKER_REGISTRY/$CI_PROJECT_PATH/$SERVICE_NAME:$CI_COMMIT_SHA
  artifacts:
    reports:
      dotenv: service-version.env

# 具体服务构建
build_user_service:
  <<: *build_service
  variables:
    SERVICE_NAME: "user-service"
  rules:
    - if: $BUILD_USER_SERVICE == "true"

build_order_service:
  <<: *build_service
  variables:
    SERVICE_NAME: "order-service"
  rules:
    - if: $BUILD_ORDER_SERVICE == "true"

build_payment_service:
  <<: *build_service
  variables:
    SERVICE_NAME: "payment-service"
  rules:
    - if: $BUILD_PAYMENT_SERVICE == "true"

build_notification_service:
  <<: *build_service
  variables:
    SERVICE_NAME: "notification-service"
  rules:
    - if: $BUILD_NOTIFICATION_SERVICE == "true"

# 集成测试
integration_test:
  stage: test
  image: docker/compose:latest
  services:
    - docker:20.10.16-dind
  needs: 
    - job: detect_changes
      artifacts: true
  script:
    - |
      if [ "$TOTAL_CHANGES" -gt 0 ]; then
        echo "Running integration tests for changed services"
        docker-compose -f docker-compose.test.yml up --build --abort-on-container-exit
        docker-compose -f docker-compose.test.yml down
      else
        echo "No services changed, skipping integration tests"
      fi
  rules:
    - if: $TOTAL_CHANGES != "0"

# 部署模板
.deploy_service: &deploy_service
  stage: deploy
  image: bitnami/kubectl:latest
  script:
    - |
      if [ -n "${!BUILD_FLAG}" ]; then
        echo "Deploying $SERVICE_NAME"
        envsubst < k8s/$SERVICE_NAME-deployment.yaml | kubectl apply -f -
        kubectl rollout status deployment/$SERVICE_NAME -n $KUBERNETES_NAMESPACE
      else
        echo "No changes in $SERVICE_NAME, skipping deployment"
      fi
  environment:
    name: $CI_COMMIT_REF_NAME
    url: https://$SERVICE_NAME-$CI_COMMIT_REF_NAME.company.com

# 具体服务部署
deploy_user_service:
  <<: *deploy_service
  variables:
    SERVICE_NAME: "user-service"
    BUILD_FLAG: "BUILD_USER_SERVICE"
  needs:
    - job: detect_changes
      artifacts: true
    - job: build_user_service
      artifacts: false
      optional: true

deploy_order_service:
  <<: *deploy_service
  variables:
    SERVICE_NAME: "order-service"
    BUILD_FLAG: "BUILD_ORDER_SERVICE"
  needs:
    - job: detect_changes
      artifacts: true
    - job: build_order_service
      artifacts: false
      optional: true

# 通知部署结果
notify_deployment:
  stage: deploy
  image: alpine:latest
  script:
    - |
      if [ "$TOTAL_CHANGES" -gt 0 ]; then
        echo "Deployment completed for services: $CHANGED_SERVICES"
        # 发送Slack通知
        curl -X POST -H 'Content-type: application/json' \
          --data "{\"text\":\"🚀 Deployed services: $CHANGED_SERVICES\"}" \
          $SLACK_WEBHOOK_URL
      fi
  needs:
    - job: detect_changes
      artifacts: true
  rules:
    - if: $TOTAL_CHANGES != "0"
      when: always

📋 YAML配置面试重点

语法基础类

  1. GitLab CI YAML的基本结构?

    • 全局配置元素
    • 作业定义规范
    • 阶段和依赖关系
    • 变量和环境设置
  2. variables的类型和作用域?

    • 全局变量vs作业变量
    • 预定义变量使用
    • 受保护变量机制
    • 文件变量的应用
  3. rules与only/except的区别?

    • 现代条件控制语法
    • 复杂条件组合
    • 性能和可维护性对比
    • 迁移最佳实践

高级特性类

  1. 如何实现复杂的并行构建?

    • parallel关键字使用
    • 矩阵构建配置
    • 动态并行生成
    • 资源管理和优化
  2. needs和artifacts的协作机制?

    • DAG流水线设计
    • 跨阶段依赖配置
    • 构件传递策略
    • 性能优化考虑
  3. 缓存策略的设计原则?

    • 缓存键值策略
    • 分布式缓存配置
    • 多级缓存设计
    • 缓存失效处理

实战应用类

  1. 企业级配置模板设计?

    • YAML模板和继承
    • 环境配置管理
    • 安全和合规要求
    • 可维护性设计
  2. 容器化应用的CI/CD配置?

    • Docker集成配置
    • 镜像构建优化
    • 多阶段构建策略
    • 安全扫描集成
  3. 单体仓库的流水线设计?

    • 变更检测机制
    • 智能构建策略
    • 服务依赖管理
    • 部署协调配置

🔗 相关内容


GitLab CI的YAML配置是实现高效CI/CD流水线的核心,通过掌握其丰富的语法特性和配置模式,可以构建适应各种复杂场景的自动化工作流。深入理解YAML配置的各个组件和最佳实践,是成为GitOps专家的重要基础。

正在精进