在 CI 中自动根据模板生成 docker-compose 并部署

解读

国内一线互联网与金融公司普遍采用 GitLab-CI / Jenkins + Harbor + 自建 K8s 的混合云架构。面试官想确认你是否能把「模板化」与「自动化」落地到整条流水线:

  1. 模板如何与 多环境(dev/test/stage/prod) 解耦;
  2. 如何 零停机滚动升级 并保证 回滚秒级可用
  3. 如何 审计 & 合规(等保 2.0、国密、内网安全扫描)。
    回答必须体现「模板即代码」与「不可变基础设施」思想,否则会被认为只能写“静态 YAML”。

知识点

  • Jinja2 / Helm / Kustomize 变量语法差异与选型理由
  • docker-compose --compatibility 与 Swarm overlay 网络限制
  • ARG/ENV 差异多阶段构建缓存复用
  • CI_COMMIT_SHORT_SHACI_MERGE_REQUEST_ID 等 GitLab 预定义变量
  • docker stack deploy --prunerollback-order 参数
  • 健康检查(healthcheck)depends_on 条件化启动
  • Secrets 管理Docker Config vs Swarm Secret vs 外部 Vault
  • 镜像加速阿里云 ACR 镜像缓存 + 个人版 VPC 免公网
  • 合规扫描trivy / Clair / 阿里云云安全中心镜像扫描
  • 蓝绿 / 金丝雀Traefik 权重标签Nginx Ingress canary-by-header

答案

  1. 目录规范(被多家大厂作为 强制代码规约 检查项)

    ├── templates
    │   ├── docker-compose.yml.j2
    │   └── nginx.conf.j2
    ├── environments
    │   ├── dev.env
    │   ├── test.env
    │   └── prod.env
    ├── scripts
    │   └── render_compose.py
    └── .gitlab-ci.yml
    
  2. 模板示例(docker-compose.yml.j2

    version: "3.9"
    services:
      app:
        image: {{ REGISTRY }}/{{ PROJECT }}:{{ CI_COMMIT_SHORT_SHA }}
        deploy:
          replicas: {{ REPLICAS }}
          update_config:
            parallelism: 1
            delay: 10s
            failure_action: rollback
          restart_policy:
            condition: on-failure
            max_attempts: 3
        secrets:
          - source: db_pass
            target: /run/secrets/db_pass
        healthcheck:
          test: ["CMD", "wget", "-q", "--spider", "http://localhost:8080/health"]
          interval: 15s
          retries: 4
    secrets:
      db_pass:
        external: true
    
  3. render_compose.py(Python3 官方镜像 50MB,内网秒拉

    #!/usr/bin/env python3
    import os, sys, jinja2
    env = jinja2.Environment(loader=jinja2.FileSystemLoader('templates'))
    template = env.get_template('docker-compose.yml.j2')
    with open('environments/%s.env' % os.getenv('CI_ENVIRONMENT_NAME'), 'r') as f:
        ctx = dict(line.strip().split('=',1) for line in f if '=' in line)
    ctx.update(os.environ)          # 让 CI 预定义变量覆盖文件变量
    with open('docker-compose.yml', 'w') as f:
        f.write(template.render(ctx))
    
  4. .gitlab-ci.yml(关键阶段必须设置 timeout 与 retry,防止构建队列堆积

    stages:
      - build
      - render
      - deploy
    variables:
      REGISTRY: "registry.cn-hangzhou.aliyuncs.com/yourproj"
      PROJECT: "demo"
    build:
      stage: build
      image: docker:24
      services:
        - docker:24-dind
      before_script:
        - echo $ALIYUN_CR_PWD | docker login -u $ALIYUN_CR_USER --password-stdin $REGISTRY
      script:
        - docker build -t $REGISTRY/$PROJECT:$CI_COMMIT_SHORT_SHA .
        - docker push $REGISTRY/$PROJECT:$CI_COMMIT_SHORT_SHA
        - trivy image --exit-code 1 --severity HIGH,CRITICAL $REGISTRY/$PROJECT:$CI_COMMIT_SHORT_SHA
    render:
      stage: render
      image: python:3.11-alpine
      script:
        - pip install jinja2
        - python scripts/render_compose.py
      artifacts:
        paths:
          - docker-compose.yml
        expire_in: 1 week
    deploy:
      stage: deploy
      image: alpine:3.18
      before_script:
        - apk add --no-cache docker-cli-compose
      script:
        - docker context create remote --docker "host=ssh://$SWARM_MANAGER"
        - docker --context remote stack deploy -c docker-compose.yml $STACK_NAME --with-registry-auth --prune
      environment:
        name: $CI_ENVIRONMENT_NAME
        url: http://$DOMAIN
      only:
        - master
        - /^release\/.*$/
    
  5. 回滚策略
    在 GitLab 界面点击 “回滚” 按钮,CI 会重新跑 上一成功 pipeline 的 CI_COMMIT_SHORT_SHA,确保 镜像与配置双维度可回滚;同时 Swarm 自带 docker stack rollback 可在 30s 内完成

  6. 合规加固

    • 基础镜像统一来自 阿里云 ACR 基础镜像仓库,已做 CentOS 官方源替换为华为开源镜像站
    • 非 root 用户启动:Dockerfile 末尾 USER 1001
    • Secrets 不落盘,通过 Swarm Secret 注入,符合等保 2.0 对敏感数据存储要求
    • 镜像扫描阈值HIGH 及以上漏洞必须清零 才允许 deploy 阶段运行。

拓展思考

  1. 如果公司全面切到 Kubernetes,可将 Jinja2 模板改为 Helm Chart,利用 helm upgrade --atomic --timeout 10m 实现同样效果;Compose 模板沉淀为 v1 版本,降低迁移成本
  2. 对于 边缘机房网络不稳定 场景,可预置 docker-compose 离线包 + 私有 registry 镜像缓存,CI 只下发 manifest 文件,通过 Ansible pull 模式 在边缘节点本地渲染,减少公网依赖
  3. 当业务需要 多区域灰度 时,可在模板里引入 Traefik 权重标签 实现 金丝雀 5%→30%→100%自动梯度切换,结合 Prometheus 指标自动回滚决策,形成 无人值守发布