Chapter 07

部署工作流:云平台与容器化

自动化部署到 AWS、GCP、Azure,构建并推送 Docker 镜像,更新 Kubernetes 集群

部署流水线设计原则

一个可靠的部署工作流需要解决以下核心问题:

不可变镜像(Immutable Images)
每次构建生成唯一标识的镜像(如 commit SHA 作为 tag),部署时始终指定具体版本而非 latest 标签。latest 标签在多环境部署中会导致版本不确定性。
环境保护(Environment Protection)
生产部署需要手动审批(GitHub Environment protection rules),staging 自动部署,production 需要相关人员点击批准。防止意外将 bug 直接推送到生产环境。
部署验证(Deployment Verification)
部署后立即运行冒烟测试(Smoke Tests)验证服务可用性,失败时自动触发回滚。kubectl rollout status / aws ecs wait services-stable 是等待部署完成的标准方式。
回滚策略(Rollback Strategy)
deployment 失败时 if: failure() 触发自动回滚;保留历史镜像版本(至少 3-5 个)确保回滚可用;记录部署日志方便事后审查。

Docker 镜像构建与推送

推送到 GitHub Container Registry(GHCR)

name: Build and Push Docker Image

on:
  push:
    branches: [main]
    tags: ['v*']

jobs:
  docker:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write   # 允许推送到 GHCR

    steps:
      - uses: actions/checkout@v4

      # 提取 Docker 元数据(标签、标记)
      - name: Docker 元数据
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ghcr.io/${{ github.repository }}
          tags: |
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}.{{minor}}
            type=sha,prefix=,format=short
            type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }}

      # 设置 QEMU(多架构构建支持)
      - uses: docker/setup-qemu-action@v3

      # 设置 Buildx(高级构建功能)
      - uses: docker/setup-buildx-action@v3

      # 登录 GHCR
      - name: 登录 GHCR
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      # 构建并推送(多架构)
      - name: 构建并推送
        uses: docker/build-push-action@v6
        with:
          context: .
          platforms: linux/amd64,linux/arm64
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

部署到 AWS

部署到 AWS ECS(Elastic Container Service)

jobs:
  deploy-ecs:
    runs-on: ubuntu-latest
    environment: production

    steps:
      - uses: actions/checkout@v4

      # 配置 AWS 凭证(使用 OIDC,无需长期密钥 - 见第9章)
      - name: 配置 AWS 凭证
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_DEPLOY_ROLE_ARN }}
          aws-region: us-east-1

      # 登录 ECR
      - name: 登录 Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v2

      # 构建并推送到 ECR
      - name: 推送到 ECR
        id: build-image
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          IMAGE_TAG: ${{ github.sha }}
        run: |
          docker build -t $ECR_REGISTRY/my-app:$IMAGE_TAG .
          docker push $ECR_REGISTRY/my-app:$IMAGE_TAG
          echo "image=$ECR_REGISTRY/my-app:$IMAGE_TAG" >> $GITHUB_OUTPUT

      # 更新 ECS Task Definition
      - name: 更新 ECS Task Definition
        id: task-def
        uses: aws-actions/amazon-ecs-render-task-definition@v1
        with:
          task-definition: task-definition.json
          container-name: my-app
          image: ${{ steps.build-image.outputs.image }}

      # 部署到 ECS Service
      - name: 部署到 ECS
        uses: aws-actions/amazon-ecs-deploy-task-definition@v1
        with:
          task-definition: ${{ steps.task-def.outputs.task-definition }}
          service: my-app-service
          cluster: my-cluster
          wait-for-service-stability: true

部署到 Google Cloud Run

jobs:
  deploy-cloud-run:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: 配置 GCP 认证
        uses: google-github-actions/auth@v2
        with:
          workload_identity_provider: ${{ secrets.GCP_WORKLOAD_IDENTITY }}
          service_account: ${{ secrets.GCP_SERVICE_ACCOUNT }}

      - name: 配置 gcloud CLI
        uses: google-github-actions/setup-gcloud@v2

      - name: 构建并推送到 Artifact Registry
        run: |
          gcloud builds submit \
            --tag us-central1-docker.pkg.dev/${{ secrets.GCP_PROJECT }}/my-repo/my-app:${{ github.sha }}

      - name: 部署到 Cloud Run
        run: |
          gcloud run deploy my-app \
            --image us-central1-docker.pkg.dev/${{ secrets.GCP_PROJECT }}/my-repo/my-app:${{ github.sha }} \
            --region us-central1 \
            --platform managed \
            --allow-unauthenticated \
            --memory 512Mi \
            --cpu 1

Kubernetes 滚动更新

jobs:
  deploy-k8s:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: 构建并推送镜像
        run: |
          docker build -t registry.example.com/my-app:${{ github.sha }} .
          docker push registry.example.com/my-app:${{ github.sha }}

      # 配置 kubectl
      - name: 配置 kubeconfig
        run: |
          mkdir -p ~/.kube
          echo "${{ secrets.KUBE_CONFIG }}" | base64 -d > ~/.kube/config

      # 更新镜像触发滚动更新
      - name: 更新 Kubernetes Deployment
        run: |
          kubectl set image deployment/my-app \
            my-app=registry.example.com/my-app:${{ github.sha }} \
            -n production

          # 等待滚动更新完成
          kubectl rollout status deployment/my-app -n production --timeout=5m

      # 部署失败时自动回滚
      - name: 回滚(如果失败)
        if: failure()
        run: |
          kubectl rollout undo deployment/my-app -n production
          echo "⚠️ 部署失败,已自动回滚到上一个版本"

静态网站部署(GitHub Pages / Vercel)

name: Deploy to GitHub Pages

on:
  push:
    branches: [main]

permissions:
  contents: read
  pages: write           # 允许部署到 Pages
  id-token: write        # OIDC 认证

# 只允许一个 deployment 同时运行
concurrency:
  group: pages
  cancel-in-progress: false

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      - run: npm ci && npm run build
      - name: 上传 Pages 产物
        uses: actions/upload-pages-artifact@v3
        with:
          path: dist/              # 构建输出目录

  deploy:
    needs: build
    runs-on: ubuntu-latest
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    steps:
      - name: 部署到 GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v4
蓝绿部署 vs 滚动更新 vs 金丝雀发布

滚动更新(Rolling Update):逐步替换旧版 Pod,资源占用少,但短暂存在新旧版本共存。蓝绿部署(Blue-Green):同时运行两套环境,切换流量实现零停机,需要双倍资源。金丝雀发布(Canary):先将 5-10% 流量导到新版本,验证无问题后再全量切换,降低风险。生产环境推荐使用 ArgoCD 或 Flux 等 GitOps 工具实现更可靠的 Kubernetes 部署管理。

本章小结

本章核心要点