Appearance
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
通过实施这些部署策略和最佳实践,可以确保应用程序的可靠、安全和高效的部署过程。