Skip to content
On this page

GitHub Actions部署策略

部署是软件开发生命周期的关键环节,GitHub Actions提供了强大的自动化部署能力。本文档将详细介绍各种部署策略、最佳实践和实际应用示例。

部署基础概念

部署环境类型

yaml
# .github/workflows/deployment.yml
name: Deployment Pipeline

on:
  push:
    branches: [main, develop]

jobs:
  # 开发环境部署
  deploy-dev:
    runs-on: ubuntu-latest
    environment: development  # 使用GitHub环境
    if: github.ref == 'refs/heads/develop'
    steps:
      - name: Deploy to development
        run: echo "Deploying to development environment"
        env:
          API_URL: ${{ secrets.DEV_API_URL }}
          DEPLOY_TOKEN: ${{ secrets.DEV_DEPLOY_TOKEN }}
  
  # 预发布环境部署
  deploy-staging:
    runs-on: ubuntu-latest
    environment: staging
    if: github.ref == 'refs/heads/main'
    steps:
      - name: Deploy to staging
        run: echo "Deploying to staging environment"
        env:
          API_URL: ${{ secrets.STAGING_API_URL }}
          DEPLOY_TOKEN: ${{ secrets.STAGING_DEPLOY_TOKEN }}
  
  # 生产环境部署
  deploy-production:
    runs-on: ubuntu-latest
    environment: production
    if: github.ref == 'refs/heads/main'
    steps:
      - name: Deploy to production
        run: echo "Deploying to production environment"
        env:
          API_URL: ${{ secrets.PROD_API_URL }}
          DEPLOY_TOKEN: ${{ secrets.PROD_DEPLOY_TOKEN }}

部署触发器

yaml
on:
  # 推送主分支时部署
  push:
    branches: [main]
  
  # 创建标签时部署
  push:
    tags: ['v*']
  
  # 手动触发部署
  workflow_dispatch:
    inputs:
      environment:
        description: 'Target environment'
        required: true
        default: 'staging'
        type: choice
        options:
          - development
          - staging
          - production
      force_deploy:
        description: 'Force deployment'
        required: false
        type: boolean

环境配置和保护

GitHub环境配置

yaml
jobs:
  deploy-with-protection:
    runs-on: ubuntu-latest
    environment:
      name: production
      url: https://example.com  # 部署后的URL
    steps:
      - name: Deploy with protection rules
        run: |
          echo "This deployment requires approval"
          echo "Environment: ${{ env.ENVIRONMENT }}"
        env:
          ENVIRONMENT: production

环境特定配置

yaml
# 不同环境使用不同配置
env:
  APP_ENV: ${{ github.ref == 'refs/heads/main' && 'production' || 'development' }}
  NODE_ENV: ${{ github.ref == 'refs/heads/main' && 'production' || 'development' }}

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: ${{ env.APP_ENV }}
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      
      - name: Setup configuration
        run: |
          if [ "$APP_ENV" = "production" ]; then
            cp config/prod.env .env
            echo "PROD=true" >> .env
          else
            cp config/dev.env .env
            echo "PROD=false" >> .env
          fi
        env:
          APP_ENV: ${{ env.APP_ENV }}
      
      - name: Deploy application
        run: |
          echo "Deploying to $APP_ENV environment"
          # 部署命令

容器化部署

Docker镜像部署

yaml
name: Docker Deployment

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

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    permissions:
      packages: write
      contents: read
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
      
      - name: Login to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      
      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ghcr.io/${{ github.repository }}
          tags: |
            type=ref,event=branch
            type=ref,event=pr
            type=sha,prefix={{branch}}-
            type=raw,value=latest,enable={{is_default_branch}}
            type=semver,pattern={{version}}
      
      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
      
      # 部署到Kubernetes
      - name: Deploy to Kubernetes
        if: startsWith(github.ref, 'refs/tags/v')
        run: |
          # 设置kubectl
          kubectl config use-context production
          
          # 更新部署
          kubectl set image deployment/myapp myapp=ghcr.io/${{ github.repository }}:${{ github.sha }}
          
          # 检查部署状态
          kubectl rollout status deployment/myapp

多环境Docker部署

yaml
jobs:
  deploy-containers:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        environment: [development, staging, production]
        include:
          - environment: development
            registry: ghcr.io
            namespace: dev
            replicas: 1
          - environment: staging
            registry: ghcr.io
            namespace: staging
            replicas: 2
          - environment: production
            registry: ghcr.io
            namespace: prod
            replicas: 3
    
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      
      - name: Login to registry
        uses: docker/login-action@v3
        with:
          registry: ${{ matrix.registry }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      
      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ${{ matrix.registry }}/${{ github.repository }}:${{ matrix.environment }}-${{ github.sha }}
      
      - name: Deploy to ${{ matrix.environment }}
        run: |
          echo "Deploying to ${{ matrix.environment }}"
          echo "Image: ${{ matrix.registry }}/${{ github.repository }}:${{ matrix.environment }}-${{ github.sha }}"
          echo "Replicas: ${{ matrix.replicas }}"
          # 实际部署命令

云平台部署

AWS部署

yaml
name: AWS Deployment

on:
  push:
    branches: [main]

jobs:
  deploy-aws:
    runs-on: ubuntu-latest
    environment: aws-production
    
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: us-west-2
      
      - name: Deploy to S3
        run: |
          aws s3 sync dist/ s3://my-website-bucket \
            --delete \
            --cache-control "public, max-age=31536000" \
            --exclude "*.html" \
            --exclude "*.json"
          aws s3 sync dist/ s3://my-website-bucket \
            --delete \
            --cache-control "public, max-age=86400" \
            --include "*.html" \
            --include "*.json"
      
      - name: Invalidate CloudFront
        run: |
          DISTRIBUTION_ID=$(aws cloudfront list-distributions --query "DistributionList.Items[?Origins.Items[?DomainName=='my-website-bucket.s3.amazonaws.com']].Id" --output text)
          aws cloudfront create-invalidation --distribution-id $DISTRIBUTION_ID --paths "/*"

Azure部署

yaml
name: Azure Deployment

on:
  push:
    branches: [main]

jobs:
  deploy-azure:
    runs-on: ubuntu-latest
    environment: azure-production
    
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      
      - name: Login to Azure
        uses: azure/login@v1
        with:
          creds: ${{ secrets.AZURE_CREDENTIALS }}
      
      - name: Deploy to Azure Web App
        uses: azure/webapps-deploy@v2
        with:
          app-name: my-web-app
          slot-name: production
          package: dist/
      
      - name: Deploy ARM templates
        run: |
          az deployment group create \
            --resource-group my-resource-group \
            --template-file infrastructure/main.bicep \
            --parameters @infrastructure/parameters.json

Google Cloud部署

yaml
name: Google Cloud Deployment

on:
  push:
    branches: [main]

jobs:
  deploy-gcp:
    runs-on: ubuntu-latest
    environment: gcp-production
    
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      
      - name: Authenticate to Google Cloud
        uses: google-github-actions/auth@v1
        with:
          credentials_json: ${{ secrets.GCP_SA_KEY }}
      
      - name: Set up Cloud SDK
        uses: google-github-actions/setup-gcloud@v1
      
      - name: Deploy to Cloud Run
        run: |
          gcloud run deploy my-service \
            --image gcr.io/${{ secrets.GCP_PROJECT_ID }}/my-app:${{ github.sha }} \
            --platform managed \
            --region us-central1 \
            --allow-unauthenticated

部署策略

蓝绿部署

yaml
name: Blue-Green Deployment

on:
  push:
    branches: [main]

jobs:
  blue-green-deploy:
    runs-on: ubuntu-latest
    environment: production
    
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      
      - name: Determine active environment
        id: active
        run: |
          # 检查当前活跃环境
          ACTIVE_ENV=$(curl -s https://api.example.com/health | jq -r '.environment')
          echo "ACTIVE_ENV=$ACTIVE_ENV" >> $GITHUB_OUTPUT
          if [ "$ACTIVE_ENV" = "blue" ]; then
            NEXT_ENV="green"
          else
            NEXT_ENV="blue"
          fi
          echo "NEXT_ENV=$NEXT_ENV" >> $GITHUB_OUTPUT
      
      - name: Deploy to ${{ steps.active.outputs.NEXT_ENV }}
        run: |
          echo "Deploying to ${{ steps.active.outputs.NEXT_ENV }} environment"
          # 部署到非活跃环境
          # 部署命令
      
      - name: Test new environment
        run: |
          # 在新环境中运行测试
          curl -f https://test-${{ steps.active.outputs.NEXT_ENV }}.example.com/health
          # 运行集成测试
      
      - name: Switch traffic
        run: |
          echo "Switching traffic to ${{ steps.active.outputs.NEXT_ENV }}"
          # 更新DNS或负载均衡器配置
          # 切换命令

金丝雀部署

yaml
name: Canary Deployment

on:
  push:
    branches: [main]

jobs:
  canary-deploy:
    runs-on: ubuntu-latest
    environment: production
    
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      
      - name: Deploy canary version
        run: |
          # 部署金丝雀版本(例如20%流量)
          kubectl apply -f manifests/canary-deployment.yaml
          kubectl patch deployment myapp-canary -p '{"spec":{"replicas":1}}'
      
      - name: Monitor canary metrics
        run: |
          # 监控金丝雀版本的指标
          sleep 300  # 等待5分钟收集指标
          
          # 检查错误率
          ERROR_RATE=$(curl -s "https://monitoring.example.com/api/errors?deployment=canary" | jq -r '.rate')
          if (( $(echo "$ERROR_RATE > 0.05" | bc -l) )); then
            echo "Error rate too high: $ERROR_RATE"
            exit 1
          fi
          
          # 检查响应时间
          RESPONSE_TIME=$(curl -s "https://monitoring.example.com/api/response-time?deployment=canary" | jq -r '.avg')
          if (( $(echo "$RESPONSE_TIME > 1.0" | bc -l) )); then
            echo "Response time too high: $RESPONSE_TIME"
            exit 1
          fi
      
      - name: Gradual rollout
        run: |
          # 逐步增加金丝雀版本的流量
          kubectl patch deployment myapp-canary -p '{"spec":{"replicas":2}}'
          sleep 300
          
          kubectl patch deployment myapp-canary -p '{"spec":{"replicas":5}}'
          sleep 300
          
          # 如果一切正常,完全切换到新版本
          kubectl scale deployment myapp --replicas=0
          kubectl scale deployment myapp-canary --replicas=5
          kubectl patch deployment myapp-canary -p '{"metadata":{"name":"myapp"}}'

滚动部署

yaml
name: Rolling Deployment

on:
  push:
    branches: [main]

jobs:
  rolling-deploy:
    runs-on: ubuntu-latest
    environment: production
    
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      
      - name: Setup kubectl
        uses: azure/setup-kubectl@v3
      
      - name: Update deployment
        run: |
          # 使用滚动更新策略
          kubectl set image deployment/myapp myapp=ghcr.io/${{ github.repository }}:${{ github.sha }}
          
          # 监控部署进度
          kubectl rollout status deployment/myapp --timeout=10m
          
          # 检查部署状态
          kubectl get pods

部署验证和监控

部署后验证

yaml
jobs:
  deploy-and-validate:
    runs-on: ubuntu-latest
    environment: production
    
    steps:
      - name: Deploy application
        run: |
          echo "Deploying application..."
          # 部署命令
      
      - name: Wait for deployment
        run: sleep 60  # 等待应用启动
      
      - name: Health check
        run: |
          # 检查应用健康状态
          for i in {1..30}; do
            if curl -f http://localhost:8080/health; then
              echo "Application is healthy"
              break
            fi
            echo "Waiting for application to be healthy..."
            sleep 10
          done
      
      - name: Functional tests
        run: |
          # 运行功能测试
          npm run test:e2e -- --baseUrl=https://myapp.example.com
      
      - name: Performance tests
        run: |
          # 运行性能测试
          npx artillery run tests/performance.yml --target https://myapp.example.com

部署监控

yaml
jobs:
  monitored-deployment:
    runs-on: ubuntu-latest
    environment: production
    
    steps:
      - name: Start deployment
        run: |
          echo "Starting deployment..."
          # 开始部署
          DEPLOY_START=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
          echo "DEPLOY_START=$DEPLOY_START" >> $GITHUB_ENV
      
      - name: Deploy application
        run: |
          # 部署应用
          # 部署命令
      
      - name: Monitor deployment
        run: |
          # 监控部署指标
          DEPLOY_END=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
          echo "Deployment completed at $DEPLOY_END"
          
          # 发送监控数据
          curl -X POST https://monitoring.example.com/api/deployment \
            -H "Content-Type: application/json" \
            -d '{
              "repository": "${{ github.repository }}",
              "sha": "${{ github.sha }}",
              "environment": "production",
              "startTime": "'"$DEPLOY_START"'",
              "endTime": "'"$DEPLOY_END"'",
              "status": "success"
            }'

回滚策略

自动回滚

yaml
name: Deployment with Auto-Rollback

on:
  push:
    branches: [main]

jobs:
  deploy-with-rollbacks:
    runs-on: ubuntu-latest
    environment: production
    
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      
      - name: Record current deployment
        id: current
        run: |
          CURRENT_SHA=$(kubectl get deployment myapp -o jsonpath='{.spec.template.spec.containers[0].image}' | cut -d':' -f2)
          echo "CURRENT_SHA=$CURRENT_SHA" >> $GITHUB_OUTPUT
      
      - name: Deploy new version
        run: |
          kubectl set image deployment/myapp myapp=ghcr.io/${{ github.repository }}:${{ github.sha }}
          kubectl rollout status deployment/myapp --timeout=5m
      
      - name: Validate deployment
        id: validate
        run: |
          # 验证部署
          sleep 60
          
          # 检查错误率
          ERROR_RATE=$(curl -s "https://monitoring.example.com/api/errors" | jq -r '.last_hour.rate')
          
          if (( $(echo "$ERROR_RATE > 0.1" | bc -l) )); then
            echo "High error rate detected: $ERROR_RATE"
            echo "rollback_needed=true" >> $GITHUB_OUTPUT
          else
            echo "Deployment successful"
            echo "rollback_needed=false" >> $GITHUB_OUTPUT
          fi
      
      - name: Rollback if needed
        if: steps.validate.outputs.rollback_needed == 'true'
        run: |
          echo "Rolling back to previous version: ${{ steps.current.outputs.CURRENT_SHA }}"
          kubectl set image deployment myapp myapp=ghcr.io/${{ github.repository }}:${{ steps.current.outputs.CURRENT_SHA }}
          kubectl rollout status deployment/myapp --timeout=5m

手动回滚

yaml
name: Manual Rollback

on:
  workflow_dispatch:
    inputs:
      rollback_version:
        description: 'Version to rollback to'
        required: true
      reason:
        description: 'Reason for rollback'
        required: true

jobs:
  manual-rollback:
    runs-on: ubuntu-latest
    environment: production
    
    steps:
      - name: Perform rollback
        run: |
          echo "Rolling back to version: ${{ github.event.inputs.rollback_version }}"
          echo "Reason: ${{ github.event.inputs.reason }}"
          
          # 执行回滚
          kubectl set image deployment/myapp myapp=ghcr.io/${{ github.repository }}:${{ github.event.inputs.rollback_version }}
          kubectl rollout status deployment/myapp --timeout=10m
          
          # 记录回滚事件
          curl -X POST https://monitoring.example.com/api/rollback \
            -H "Content-Type: application/json" \
            -d '{
              "repository": "${{ github.repository }}",
              "from": "${{ github.sha }}",
              "to": "${{ github.event.inputs.rollback_version }}",
              "reason": "${{ github.event.inputs.reason }}",
              "actor": "${{ github.actor }}"
            }'

部署最佳实践

安全部署

yaml
jobs:
  secure-deployment:
    runs-on: ubuntu-latest
    environment: production
    permissions:
      id-token: write  # 用于OIDC
      contents: read
    
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      
      - name: Assume AWS role with OIDC
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/GitHubActionsDeployRole
          role-session-name: GitHubActionsSession
          aws-region: us-west-2
      
      - name: Deploy securely
        run: |
          # 使用临时凭证进行部署
          aws s3 sync dist/ s3://my-secure-bucket

零停机部署

yaml
jobs:
  zero-downtime-deploy:
    runs-on: ubuntu-latest
    environment: production
    
    steps:
      - name: Pre-deployment health check
        run: |
          # 检查当前系统健康状态
          curl -f https://myapp.example.com/health || exit 1
      
      - name: Deploy with blue-green strategy
        run: |
          # 部署到备用环境
          # 更新备用环境
          
          # 运行健康检查
          sleep 30
          curl -f https://standby.example.com/health || exit 1
          
          # 切换流量
          # 更新DNS/负载均衡器
          
          # 验证切换
          curl -f https://myapp.example.com/health || exit 1

通过实施这些部署策略和最佳实践,可以确保应用程序的可靠、安全和高效的部署过程。