GitHub Actions CD自动部署 #

本节通过一个完整的CD示例,展示如何实现自动化部署流程。

部署概述 #

CD流程 #

text
代码合并 → 构建 → 测试 → 部署到Staging → 验证 → 部署到Production

部署环境 #

环境 描述
Development 开发环境
Staging 预发布环境
Production 生产环境

基础部署工作流 #

yaml
name: Deploy

on:
  push:
    branches: [main]
  workflow_dispatch:
    inputs:
      environment:
        description: 'Environment'
        required: true
        type: choice
        options:
          - staging
          - production
        default: 'staging'

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
      - run: npm run build
      
      - uses: actions/upload-artifact@v4
        with:
          name: dist
          path: dist/

  deploy:
    needs: build
    runs-on: ubuntu-latest
    environment: ${{ github.event.inputs.environment || 'staging' }}
    steps:
      - uses: actions/download-artifact@v4
        with:
          name: dist
          path: ./dist
      
      - name: Deploy
        run: |
          echo "Deploying to ${{ github.event.inputs.environment || 'staging' }}"
          ./deploy.sh ${{ github.event.inputs.environment || 'staging' }}

GitHub Pages部署 #

yaml
name: Deploy to GitHub Pages

on:
  push:
    branches: [main]

permissions:
  contents: read
  pages: write
  id-token: write

concurrency:
  group: "pages"
  cancel-in-progress: true

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
      - run: npm run build
      
      - 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:
      - uses: actions/deploy-pages@v4
        id: deployment

Docker部署 #

yaml
name: Docker Deploy

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

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  build:
    runs-on: ubuntu-latest
    outputs:
      image: ${{ steps.build.outputs.image }}
    steps:
      - uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Log in to Container Registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and push
        id: build
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: |
            ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
            ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

  deploy-staging:
    needs: build
    runs-on: ubuntu-latest
    environment: staging
    steps:
      - name: Deploy to staging
        run: |
          echo "Deploying ${{ needs.build.outputs.image }} to staging"

  deploy-production:
    needs: build
    runs-on: ubuntu-latest
    environment: production
    steps:
      - name: Deploy to production
        run: |
          echo "Deploying ${{ needs.build.outputs.image }} to production"

完整CD工作流 #

多环境部署 #

yaml
name: Deploy

on:
  push:
    branches:
      - main
      - develop
    tags:
      - 'v*'
  workflow_dispatch:
    inputs:
      environment:
        description: 'Environment'
        required: true
        type: choice
        options:
          - staging
          - production
        default: 'staging'
      version:
        description: 'Version'
        required: false
        type: string

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  prepare:
    runs-on: ubuntu-latest
    outputs:
      environment: ${{ steps.env.outputs.environment }}
      version: ${{ steps.env.outputs.version }}
      should-deploy: ${{ steps.env.outputs.should-deploy }}
    steps:
      - id: env
        run: |
          if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
            echo "environment=${{ github.event.inputs.environment }}" >> $GITHUB_OUTPUT
            echo "version=${{ github.event.inputs.version || github.sha }}" >> $GITHUB_OUTPUT
            echo "should-deploy=true" >> $GITHUB_OUTPUT
          elif [ "${{ github.ref_type }}" == "tag" ]; then
            echo "environment=production" >> $GITHUB_OUTPUT
            echo "version=${{ github.ref_name }}" >> $GITHUB_OUTPUT
            echo "should-deploy=true" >> $GITHUB_OUTPUT
          elif [ "${{ github.ref }}" == "refs/heads/main" ]; then
            echo "environment=staging" >> $GITHUB_OUTPUT
            echo "version=${{ github.sha }}" >> $GITHUB_OUTPUT
            echo "should-deploy=true" >> $GITHUB_OUTPUT
          elif [ "${{ github.ref }}" == "refs/heads/develop" ]; then
            echo "environment=development" >> $GITHUB_OUTPUT
            echo "version=${{ github.sha }}" >> $GITHUB_OUTPUT
            echo "should-deploy=true" >> $GITHUB_OUTPUT
          else
            echo "should-deploy=false" >> $GITHUB_OUTPUT
          fi

  build:
    needs: prepare
    if: needs.prepare.outputs.should-deploy == 'true'
    runs-on: ubuntu-latest
    outputs:
      image: ${{ steps.build.outputs.image }}
    steps:
      - uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Log in to Container Registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and push
        id: build
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: |
            ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.prepare.outputs.version }}
            ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
          cache-from: type=gha
          cache-to: type=gha,mode=max

  deploy:
    needs: [prepare, build]
    runs-on: ubuntu-latest
    environment: ${{ needs.prepare.outputs.environment }}
    steps:
      - uses: actions/checkout@v4

      - name: Deploy
        run: |
          echo "Deploying version ${{ needs.prepare.outputs.version }}"
          echo "Environment: ${{ needs.prepare.outputs.environment }}"
          echo "Image: ${{ needs.build.outputs.image }}"
          
          # 部署脚本
          ./deploy.sh ${{ needs.prepare.outputs.environment }} ${{ needs.prepare.outputs.version }}

      - name: Verify deployment
        run: |
          echo "Verifying deployment..."
          # 验证脚本
          ./verify.sh ${{ needs.prepare.outputs.environment }}

  notify:
    needs: [prepare, build, deploy]
    if: always()
    runs-on: ubuntu-latest
    steps:
      - name: Notify on success
        if: success()
        uses: slackapi/slack-github-action@v1
        with:
          channel-id: 'C0123456789'
          slack-message: |
            :rocket: Deployment Successful
            Environment: ${{ needs.prepare.outputs.environment }}
            Version: ${{ needs.prepare.outputs.version }}
        env:
          SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}

      - name: Notify on failure
        if: failure()
        uses: slackapi/slack-github-action@v1
        with:
          channel-id: 'C0123456789'
          slack-message: |
            :x: Deployment Failed
            Environment: ${{ needs.prepare.outputs.environment }}
            Version: ${{ needs.prepare.outputs.version }}
        env:
          SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}

回滚工作流 #

yaml
name: Rollback

on:
  workflow_dispatch:
    inputs:
      environment:
        description: 'Environment'
        required: true
        type: choice
        options:
          - staging
          - production
      version:
        description: 'Version to rollback to'
        required: true
        type: string
      reason:
        description: 'Rollback reason'
        required: true
        type: string

jobs:
  rollback:
    runs-on: ubuntu-latest
    environment: ${{ github.event.inputs.environment }}
    steps:
      - uses: actions/checkout@v4

      - name: Rollback
        run: |
          echo "Rolling back to version ${{ github.event.inputs.version }}"
          echo "Environment: ${{ github.event.inputs.environment }}"
          echo "Reason: ${{ github.event.inputs.reason }}"
          
          ./rollback.sh ${{ github.event.inputs.environment }} ${{ github.event.inputs.version }}

      - name: Create issue
        uses: peter-evans/create-issue-from-file@v4
        with:
          title: "Rollback: ${{ github.event.inputs.environment }} to ${{ github.event.inputs.version }}"
          content: |
            ## Rollback Details
            
            - **Environment**: ${{ github.event.inputs.environment }}
            - **Version**: ${{ github.event.inputs.version }}
            - **Reason**: ${{ github.event.inputs.reason }}
            - **Triggered by**: ${{ github.actor }}
          labels: rollback

最佳实践 #

1. 使用环境保护 #

yaml
environment: production

2. 使用版本标签 #

yaml
tags: |
  user/app:latest
  user/app:${{ github.sha }}

3. 添加部署验证 #

yaml
- name: Verify deployment
  run: ./verify.sh

4. 设置通知 #

yaml
- name: Notify
  if: always()
  uses: slackapi/slack-github-action@v1

5. 准备回滚方案 #

创建专门的回滚工作流。

下一步学习 #

小结 #

  • CD实现自动化部署
  • 使用环境保护生产环境
  • 支持多环境部署
  • 使用版本标签管理镜像
  • 添加部署验证
  • 设置通知告警
  • 准备回滚方案
最后更新:2026-03-28