Ngày triển khai từng có nghĩa là những đêm mất ngủ, những lần khôi phục khẩn cấp và những buổi khắc phục sự cố điên cuồng. Bạn có thể đã từng trải qua khoảnh khắc thót tim khi quá trình triển khai production bị hỏng, người dùng bắt đầu phàn nàn và bạn đang loay hoay tìm hiểu xem điều gì đã xảy ra trong khi toàn bộ nhóm đang thở dốc.
Các chiến lược triển khai hiện đại loại bỏ căng thẳng này bằng cách làm cho việc triển khai có thể dự đoán được, có thể đảo ngược và có mức độ rủi ro thấp. Khi bạn hiểu cách triển khai an toàn trên các môi trường khác nhau, các bản phát hành sẽ trở thành sự kiện thường xuyên diễn ra thường xuyên mà không có kịch tính.
Tại sao chiến lược triển khai lại quan trọng
Sự khác biệt giữa chiến lược triển khai tốt và chiến lược triển khai tồi là sự khác biệt giữa việc ngủ ngon và nhận được cuộc gọi khẩn cấp lúc 3 giờ sáng. Một chiến lược tốt sẽ phát hiện các vấn đề trước khi chúng tiếp cận người dùng, cung cấp các tùy chọn khôi phục nhanh chóng khi có sự cố xảy ra và giúp bạn tin tưởng rằng quá trình triển khai sẽ hoạt động như mong đợi.
Hầu hết các lỗi triển khai đều xuất phát từ sự khác biệt giữa các môi trường hoặc quy trình triển khai chưa được kiểm tra. Ứng dụng của bạn hoạt động hoàn hảo trong quá trình phát triển nhưng sử dụng các cấu hình cơ sở dữ liệu khác nhau trong production. Tập lệnh triển khai của bạn hoạt động trên máy của bạn nhưng không thành công trên máy chủ production do vấn đề về quyền. Những vấn đề này có thể ngăn ngừa được bằng cách tiếp cận đúng đắn.
Cái nhìn sâu sắc quan trọng là việc triển khai sẽ nhàm chán. Phần thú vị là xây dựng các tính năng - việc triển khai phải được tự động hóa, các quy trình có thể dự đoán được của Just Work™.
Thiết lập hệ thống phân cấp môi trường
Hầu hết các nhóm sử dụng nhiều môi trường phản ánh lộ trình từ quá trình phát triển đến production. Mỗi môi trường phục vụ một mục đích cụ thể và nắm bắt các loại vấn đề khác nhau trước khi chúng đến tay người dùng.
Dưới đây là cách thiết lập tiến trình môi trường thực tế:
name: Multi-Environment Deployment Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
# Build once, deploy everywhere
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Build application
run: npm run build
env:
NODE_ENV: production
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: app-build-${{ github.sha }}
path: dist/
# Automatically deploy to development environment
deploy-dev:
runs-on: ubuntu-latest
needs: build
if: github.ref == 'refs/heads/develop'
environment: development
steps:
- uses: actions/checkout@v4
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: app-build-${{ github.sha }}
path: dist/
- name: Deploy to development
env:
DEPLOY_HOST: ${{ secrets.DEV_DEPLOY_HOST }}
DEPLOY_KEY: ${{ secrets.DEV_DEPLOY_KEY }}
API_URL: https://api.dev.example.com
run: |
echo "Deploying to development environment..."
# Configure SSH for deployment
mkdir -p ~/.ssh
echo "$DEPLOY_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
# Deploy files
rsync -avz --delete \
-e "ssh -o StrictHostKeyChecking=no" \
dist/ deployer@$DEPLOY_HOST:/var/www/dev/
# Restart application
ssh -o StrictHostKeyChecking=no deployer@$DEPLOY_HOST \
"sudo systemctl restart app-dev"
echo "✅ Development deployment completed"
# Deploy to staging with manual approval
deploy-staging:
runs-on: ubuntu-latest
needs: build
if: github.ref == 'refs/heads/main'
environment: staging
steps:
- uses: actions/checkout@v4
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: app-build-${{ github.sha }}
path: dist/
- name: Deploy to staging
env:
DEPLOY_HOST: ${{ secrets.STAGING_DEPLOY_HOST }}
DEPLOY_KEY: ${{ secrets.STAGING_DEPLOY_KEY }}
API_URL: https://api.staging.example.com
run: |
echo "Deploying to staging environment..."
# Deploy with staging-specific configuration
mkdir -p ~/.ssh
echo "$DEPLOY_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
rsync -avz --delete \
-e "ssh -o StrictHostKeyChecking=no" \
dist/ deployer@$DEPLOY_HOST:/var/www/staging/
ssh -o StrictHostKeyChecking=no deployer@$DEPLOY_HOST \
"sudo systemctl restart app-staging"
- name: Run smoke tests
run: |
echo "Running smoke tests on staging..."
# Wait for service to start
sleep 30
# Basic health check
curl --fail https://staging.example.com/health || exit 1
# Test critical functionality
curl --fail https://staging.example.com/api/status || exit 1
echo "✅ Smoke tests passed"
# Production deployment with additional safety checks
deploy-production:
runs-on: ubuntu-latest
needs: [build, deploy-staging]
if: github.ref == 'refs/heads/main'
environment: production
steps:
- uses: actions/checkout@v4
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: app-build-${{ github.sha }}
path: dist/
- name: Pre-deployment checks
run: |
echo "Running pre-deployment safety checks..."
# Verify staging is healthy
curl --fail https://staging.example.com/health || {
echo "❌ Staging environment is unhealthy, aborting production deployment"
exit 1
}
# Check for any ongoing incidents
echo "✅ Pre-deployment checks passed"
- name: Deploy to production
env:
DEPLOY_HOST: ${{ secrets.PROD_DEPLOY_HOST }}
DEPLOY_KEY: ${{ secrets.PROD_DEPLOY_KEY }}
API_URL: https://api.example.com
run: |
echo "Deploying to production environment..."
mkdir -p ~/.ssh
echo "$DEPLOY_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
# Create backup before deployment
ssh -o StrictHostKeyChecking=no deployer@$DEPLOY_HOST \
"sudo cp -r /var/www/prod /var/www/prod-backup-$(date +%Y%m%d-%H%M%S)"
# Deploy new version
rsync -avz --delete \
-e "ssh -o StrictHostKeyChecking=no" \
dist/ deployer@$DEPLOY_HOST:/var/www/prod/
# Graceful restart
ssh -o StrictHostKeyChecking=no deployer@$DEPLOY_HOST \
"sudo systemctl reload app-prod"
- name: Post-deployment verification
run: |
echo "Verifying production deployment..."
# Wait for service to stabilize
sleep 60
# Health check
curl --fail https://example.com/health || {
echo "❌ Production health check failed"
exit 1
}
# Test critical user journeys
curl --fail https://example.com/api/status || {
echo "❌ API status check failed"
exit 1
}
echo "✅ Production deployment verified"
- name: Send deployment notification
if: always()
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
run: |
if [ "${{ job.status }}" = "success" ]; then
MESSAGE="✅ Production deployment successful for commit ${{ github.sha }}"
else
MESSAGE="❌ Production deployment failed for commit ${{ github.sha }}"
fi
curl -X POST "$SLACK_WEBHOOK" \
-H 'Content-type: application/json' \
--data "{\"text\":\"$MESSAGE\"}"
Quy trình công việc này thể hiện tiến trình môi trường thực tế trong đó việc triển khai phát triển diễn ra tự động, staging yêu cầu nhánh chính và production yêu cầu cả thành công của staging và kiểm tra an toàn bổ sung.
Triển khai Xanh-Xanh
Việc triển khai xanh lam giúp loại bỏ thời gian ngừng hoạt động bằng cách duy trì hai môi trường production giống hệt nhau. Bạn triển khai đến môi trường không hoạt động, kiểm tra kỹ lưỡng rồi chuyển lưu lượng truy cập. Nếu có sự cố xảy ra, bạn có thể chuyển trở lại ngay lập tức.
Đây là cách triển khai xanh lam hoạt động:
Initial State - Blue is Live:
┌─────────────────┐
User Traffic ──▶│ Load Balancer │
└─────────────────┘
│
▼
┌───────────────────────────────────────┐
│ Blue Environment │
│ (ACTIVE) │
│ ┌───────┐ ┌───────┐ ┌───────┐ │
│ │App v1 │ │App v1 │ │App v1 │ │
│ └───────┘ └───────┘ └───────┘ │
└───────────────────────────────────────┘
┌───────────────────────────────────────┐
│ Green Environment │
│ (IDLE) │
│ ┌───────┐ ┌───────┐ ┌───────┐ │
│ │ - │ │ - │ │ - │ │
│ └───────┘ └───────┘ └───────┘ │
└───────────────────────────────────────┘
Step 1 - Deploy to Green:
┌─────────────────┐
User Traffic ──▶│ Load Balancer │
└─────────────────┘
│
▼
┌───────────────────────────────────────┐
│ Blue Environment │
│ (ACTIVE) │
│ ┌───────┐ ┌───────┐ ┌───────┐ │
│ │App v1 │ │App v1 │ │App v1 │ │
│ └───────┘ └───────┘ └───────┘ │
└───────────────────────────────────────┘
┌───────────────────────────────────────┐
│ Green Environment │
│ (DEPLOYING) │
│ ┌───────┐ ┌───────┐ ┌───────┐ │ ← Deploy v2 here
│ │App v2 │ │App v2 │ │App v2 │ │ Test thoroughly
│ └───────┘ └───────┘ └───────┘ │
└───────────────────────────────────────┘
Step 2 - Switch Traffic to Green:
┌─────────────────┐
User Traffic ──▶│ Load Balancer │
└─────────────────┘
│
▼
┌───────────────────────────────────────┐
│ Blue Environment │
│ (IDLE) │ ← Now idle
│ ┌───────┐ ┌───────┐ ┌───────┐ │ (rollback ready)
│ │App v1 │ │App v1 │ │App v1 │ │
│ └───────┘ └───────┘ └───────┘ │
└───────────────────────────────────────┘
┌───────────────────────────────────────┐
│ Green Environment │
│ (ACTIVE) │ ← Now serving users
│ ┌───────┐ ┌───────┐ ┌───────┐ │
│ │App v2 │ │App v2 │ │App v2 │ │
│ └───────┘ └───────┘ └───────┘ │
└───────────────────────────────────────┘
Dưới đây là cách triển khai triển khai xanh lam:
name: Blue-Green Deployment
on:
workflow_dispatch:
inputs:
target_environment:
description: 'Target environment (blue or green)'
required: true
type: choice
options: [blue, green]
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: production-build
path: dist/
- name: Deploy to ${{ github.event.inputs.target_environment }} environment
env:
TARGET_ENV: ${{ github.event.inputs.target_environment }}
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
run: |
echo "Deploying to $TARGET_ENV environment..."
# Deploy to the target environment
mkdir -p ~/.ssh
echo "$DEPLOY_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
# Deploy files to target environment
rsync -avz --delete \
-e "ssh -o StrictHostKeyChecking=no" \
dist/ deployer@$DEPLOY_HOST:/var/www/app-$TARGET_ENV/
# Start services on target environment
ssh -o StrictHostKeyChecking=no deployer@$DEPLOY_HOST \
"sudo systemctl start app-$TARGET_ENV"
- name: Run comprehensive tests on ${{ github.event.inputs.target_environment }}
run: |
TARGET_ENV="${{ github.event.inputs.target_environment }}"
# Wait for application to start
sleep 30
# Get the internal URL for testing
if [ "$TARGET_ENV" = "blue" ]; then
TEST_URL="https://blue.internal.example.com"
else
TEST_URL="https://green.internal.example.com"
fi
echo "Testing $TARGET_ENV environment at $TEST_URL"
# Health check
curl --fail "$TEST_URL/health" || exit 1
# API functionality test
curl --fail "$TEST_URL/api/status" || exit 1
# Database connectivity test
curl --fail "$TEST_URL/api/db-check" || exit 1
echo "✅ All tests passed on $TARGET_ENV environment"
- name: Switch traffic to ${{ github.event.inputs.target_environment }}
env:
TARGET_ENV: ${{ github.event.inputs.target_environment }}
LOAD_BALANCER_HOST: ${{ secrets.LOAD_BALANCER_HOST }}
LB_API_KEY: ${{ secrets.LOAD_BALANCER_API_KEY }}
run: |
echo "Switching traffic to $TARGET_ENV environment..."
# Update load balancer to point to new environment
curl -X POST "https://$LOAD_BALANCER_HOST/api/switch" \
-H "Authorization: Bearer $LB_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"active_environment\": \"$TARGET_ENV\"}"
echo "✅ Traffic switched to $TARGET_ENV environment"
- name: Monitor new deployment
run: |
echo "Monitoring new deployment for 5 minutes..."
for i in {1..10}; do
echo "Health check $i/10..."
if ! curl --fail https://example.com/health; then
echo "❌ Health check failed, consider rollback"
exit 1
fi
sleep 30
done
echo "✅ Deployment stable after monitoring period"
Triển khai xanh lam là lựa chọn hoàn hảo cho các ứng dụng có thời gian ngừng hoạt động không thể chấp nhận được nhưng chúng yêu cầu cơ sở hạ tầng có thể hỗ trợ chạy đồng thời hai môi trường đầy đủ.
Triển khai lăn
Triển khai luân phiên cập nhật từng máy chủ một, duy trì tính khả dụng trong suốt quá trình. Chiến lược này hoạt động tốt khi bạn có nhiều máy chủ phía sau bộ cân bằng tải.
Đây là cách triển khai cuộn hoạt động:
Initial State - All servers running v1:
┌─────────────────┐
User Traffic ───▶│ Load Balancer │
└─────────────────┘
│
┌─────────────────┼─────────────────┐
│ │ │
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│Server 1 │ │Server 2 │ │Server 3 │
│ App v1 │ │ App v1 │ │ App v1 │
│(active) │ │(active) │ │(active) │
└─────────┘ └─────────┘ └─────────┘
Step 1 - Remove Server 1, Deploy v2:
┌─────────────────┐
User Traffic ───▶│ Load Balancer │
└─────────────────┘
│
┌─────┼─────┐
│ │ │
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│Server 1 │ │Server 2 │ │Server 3 │
│ App v2 │ │ App v1 │ │ App v1 │
│(deploy) │ │(active) │ │(active) │ ← Traffic continues
└─────────┘ └─────────┘ └─────────┘
↑
Removed from
load balancer
Step 2 - Add Server 1 back, Remove Server 2:
┌─────────────────┐
User Traffic ───▶│ Load Balancer │
└─────────────────┘
│
┌─────────┼─────────┐
│ │ │
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│Server 1 │ │Server 2 │ │Server 3 │
│ App v2 │ │ App v2 │ │ App v1 │
│(active) │ │(deploy) │ │(active) │ ← Still serving users
└─────────┘ └─────────┘ └─────────┘
↑
Removed from
load balancer
Final State - All servers updated:
┌─────────────────┐
User Traffic ───▶│ Load Balancer │
└─────────────────┘
│
┌─────────────────┼─────────────────┐
│ │ │
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│Server 1 │ │Server 2 │ │Server 3 │
│ App v2 │ │ App v2 │ │ App v2 │
│(active) │ │(active) │ │(active) │ ← All updated!
└─────────┘ └─────────┘ └─────────┘
name: Rolling Deployment
on:
push:
branches: [main]
jobs:
rolling-deploy:
runs-on: ubuntu-latest
environment: production
strategy:
# Deploy to servers one at a time
max-parallel: 1
matrix:
server: [web1, web2, web3, web4]
steps:
- uses: actions/checkout@v4
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: production-build
path: dist/
- name: Remove ${{ matrix.server }} from load balancer
env:
SERVER: ${{ matrix.server }}
LB_HOST: ${{ secrets.LOAD_BALANCER_HOST }}
LB_API_KEY: ${{ secrets.LOAD_BALANCER_API_KEY }}
run: |
echo "Removing $SERVER from load balancer..."
curl -X POST "https://$LB_HOST/api/servers/$SERVER/disable" \
-H "Authorization: Bearer $LB_API_KEY"
# Wait for connections to drain
sleep 30
- name: Deploy to ${{ matrix.server }}
env:
SERVER: ${{ matrix.server }}
DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
run: |
echo "Deploying to $SERVER..."
mkdir -p ~/.ssh
echo "$DEPLOY_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
# Get server hostname
SERVER_HOST="${{ secrets[format('{0}_HOST', matrix.server)] }}"
# Deploy files
rsync -avz --delete \
-e "ssh -o StrictHostKeyChecking=no" \
dist/ deployer@$SERVER_HOST:/var/www/app/
# Restart application
ssh -o StrictHostKeyChecking=no deployer@$SERVER_HOST \
"sudo systemctl restart app"
- name: Verify ${{ matrix.server }} health
env:
SERVER: ${{ matrix.server }}
run: |
SERVER_HOST="${{ secrets[format('{0}_HOST', matrix.server)] }}"
echo "Testing $SERVER health..."
# Wait for service to start
sleep 30
# Health check
for i in {1..6}; do
if curl --fail "http://$SERVER_HOST:8080/health"; then
echo "✅ $SERVER is healthy"
break
fi
if [ $i -eq 6 ]; then
echo "❌ $SERVER failed health check"
exit 1
fi
sleep 10
done
- name: Add ${{ matrix.server }} back to load balancer
env:
SERVER: ${{ matrix.server }}
LB_HOST: ${{ secrets.LOAD_BALANCER_HOST }}
LB_API_KEY: ${{ secrets.LOAD_BALANCER_API_KEY }}
run: |
echo "Adding $SERVER back to load balancer..."
curl -X POST "https://$LB_HOST/api/servers/$SERVER/enable" \
-H "Authorization: Bearer $LB_API_KEY"
echo "✅ $SERVER deployment completed"
Triển khai luân phiên mang lại sự cân bằng tốt giữa tính an toàn và tính đơn giản, mặc dù chúng mất nhiều thời gian hơn các chiến lược khác vì máy chủ được cập nhật tuần tự.
Canary phát hành
Các bản phát hành của Canary trước tiên sẽ triển khai các phiên bản mới cho một nhóm nhỏ người dùng, cho phép bạn phát hiện sự cố trước khi chúng ảnh hưởng đến mọi người. Chiến lược này rất tuyệt vời để phát hiện các vấn đề chỉ xuất hiện khi người dùng thực tải.
name: Canary Release
on:
workflow_dispatch:
inputs:
canary_percentage:
description: 'Percentage of traffic for canary'
required: true
default: '10'
type: choice
options: ['5', '10', '25', '50', '100']
jobs:
canary-deploy:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: production-build
path: dist/
- name: Deploy canary version
env:
DEPLOY_HOST: ${{ secrets.CANARY_DEPLOY_HOST }}
DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
run: |
echo "Deploying canary version..."
mkdir -p ~/.ssh
echo "$DEPLOY_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
# Deploy to canary servers
rsync -avz --delete \
-e "ssh -o StrictHostKeyChecking=no" \
dist/ deployer@$DEPLOY_HOST:/var/www/app-canary/
ssh -o StrictHostKeyChecking=no deployer@$DEPLOY_HOST \
"sudo systemctl restart app-canary"
- name: Configure traffic split
env:
CANARY_PERCENTAGE: ${{ github.event.inputs.canary_percentage }}
LB_HOST: ${{ secrets.LOAD_BALANCER_HOST }}
LB_API_KEY: ${{ secrets.LOAD_BALANCER_API_KEY }}
run: |
echo "Configuring $CANARY_PERCENTAGE% traffic to canary..."
curl -X POST "https://$LB_HOST/api/traffic-split" \
-H "Authorization: Bearer $LB_API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"canary_percentage\": $CANARY_PERCENTAGE,
\"canary_backend\": \"canary\",
\"stable_backend\": \"stable\"
}"
echo "✅ Traffic split configured"
- name: Monitor canary metrics
env:
CANARY_PERCENTAGE: ${{ github.event.inputs.canary_percentage }}
run: |
echo "Monitoring canary for 10 minutes..."
for i in {1..20}; do
echo "Check $i/20 - monitoring canary health..."
# Check error rates
ERROR_RATE=$(curl -s "https://monitoring.example.com/api/error-rate/canary")
STABLE_ERROR_RATE=$(curl -s "https://monitoring.example.com/api/error-rate/stable")
echo "Canary error rate: $ERROR_RATE%"
echo "Stable error rate: $STABLE_ERROR_RATE%"
# Abort if canary error rate is significantly higher
if (( $(echo "$ERROR_RATE > $STABLE_ERROR_RATE * 2" | bc -l) )); then
echo "❌ Canary error rate too high, rolling back..."
# Reset traffic to stable version
curl -X POST "https://$LB_HOST/api/traffic-split" \
-H "Authorization: Bearer $LB_API_KEY" \
-H "Content-Type: application/json" \
-d '{"canary_percentage": 0, "stable_percentage": 100}'
exit 1
fi
sleep 30
done
echo "✅ Canary monitoring completed successfully"
Các bản phát hành Canary yêu cầu giám sát và quản lý lưu lượng phức tạp nhưng chúng cung cấp cách an toàn nhất để triển khai các thay đổi có thể gây ra tác động không mong muốn khi tải người dùng thực.
Chiến lược quay lui
Không có chiến lược triển khai nào hoàn chỉnh nếu không có kế hoạch khôi phục. Khi có sự cố xảy ra, bạn cần nhanh chóng khôi phục dịch vụ.
name: Emergency Rollback
on:
workflow_dispatch:
inputs:
rollback_target:
description: 'Commit SHA to rollback to'
required: true
environment:
description: 'Environment to rollback'
required: true
type: choice
options: [staging, production]
jobs:
rollback:
runs-on: ubuntu-latest
environment: ${{ github.event.inputs.environment }}
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.rollback_target }}
- name: Download rollback artifacts
uses: actions/download-artifact@v4
with:
name: build-${{ github.event.inputs.rollback_target }}
path: dist/
- name: Execute rollback
env:
ENVIRONMENT: ${{ github.event.inputs.environment }}
ROLLBACK_TARGET: ${{ github.event.inputs.rollback_target }}
run: |
echo "🚨 EMERGENCY ROLLBACK 🚨"
echo "Rolling back $ENVIRONMENT to commit $ROLLBACK_TARGET"
# Confirm rollback (in real scenario, this might be automated)
echo "Executing rollback in 10 seconds..."
sleep 10
# Deploy the previous version
DEPLOY_HOST="${{ secrets[format('{0}_DEPLOY_HOST', env.ENVIRONMENT)] }}"
DEPLOY_KEY="${{ secrets[format('{0}_DEPLOY_KEY', env.ENVIRONMENT)] }}"
mkdir -p ~/.ssh
echo "$DEPLOY_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
rsync -avz --delete \
-e "ssh -o StrictHostKeyChecking=no" \
dist/ deployer@$DEPLOY_HOST:/var/www/app/
ssh -o StrictHostKeyChecking=no deployer@$DEPLOY_HOST \
"sudo systemctl restart app"
echo "✅ Rollback completed"
- name: Verify rollback
run: |
ENVIRONMENT="${{ github.event.inputs.environment }}"
if [ "$ENVIRONMENT" = "production" ]; then
TEST_URL="https://example.com"
else
TEST_URL="https://staging.example.com"
fi
echo "Verifying rollback..."
sleep 30
curl --fail "$TEST_URL/health" || {
echo "❌ Rollback verification failed"
exit 1
}
echo "✅ Rollback verified"
- name: Send rollback notification
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
ENVIRONMENT: ${{ github.event.inputs.environment }}
ROLLBACK_TARGET: ${{ github.event.inputs.rollback_target }}
run: |
curl -X POST "$SLACK_WEBHOOK" \
-H 'Content-type: application/json' \
--data "{
\"text\": \"🚨 Emergency rollback completed for $ENVIRONMENT to commit $ROLLBACK_TARGET\",
\"username\": \"Deployment Bot\",
\"icon_emoji\": \":warning:\"
}"
Quản lý cấu hình môi trường
Các môi trường khác nhau cần các cấu hình khác nhau nhưng việc quản lý những khác biệt này có thể trở nên phức tạp. Đây là một cách tiếp cận rõ ràng:
# Use GitHub Variables for environment-specific configuration
- name: Set environment configuration
run: |
case "${{ github.event.inputs.environment || 'staging' }}" in
"development")
echo "API_URL=${{ vars.DEV_API_URL }}" >> $GITHUB_ENV
echo "DATABASE_POOL_SIZE=5" >> $GITHUB_ENV
echo "LOG_LEVEL=debug" >> $GITHUB_ENV
;;
"staging")
echo "API_URL=${{ vars.STAGING_API_URL }}" >> $GITHUB_ENV
echo "DATABASE_POOL_SIZE=10" >> $GITHUB_ENV
echo "LOG_LEVEL=info" >> $GITHUB_ENV
;;
"production")
echo "API_URL=${{ vars.PROD_API_URL }}" >> $GITHUB_ENV
echo "DATABASE_POOL_SIZE=20" >> $GITHUB_ENV
echo "LOG_LEVEL=warn" >> $GITHUB_ENV
;;
esac
Sử dụng tính năng Biến của GitHub cho cấu hình dành riêng cho môi trường không nhạy cảm và Bí mật cho các giá trị nhạy cảm. Điều này giúp cấu hình được sắp xếp hợp lý và giúp bạn dễ dàng nhận thấy sự khác biệt giữa các môi trường.
Chìa khóa để triển khai thành công là chọn chiến lược phù hợp cho ứng dụng và cơ sở hạ tầng của bạn. Bắt đầu đơn giản với quá trình phát triển môi trường cơ bản, sau đó thêm các chiến lược phức tạp hơn khi nhu cầu của bạn tăng lên và sự tự tin của bạn trong quá trình triển khai tăng lên.
Trong phần tiếp theo, chúng ta sẽ khám phá các mẫu quy trình làm việc nâng cao và kỹ thuật tự động hóa giúp bạn xử lý các tình huống triển khai phức tạp và tối ưu hóa CI/CD pipeline của bạn.