12 KiB
12 KiB
trigger
| trigger |
|---|
| always_on |
CI/CD Advanced Patterns
When to Use This Skill
Use this skill when:
- Implementing blue-green deployments
- Setting up canary releases
- Implementing automated rollback mechanisms
- Creating deployment verification pipelines
- Implementing progressive delivery
- Setting up deployment gates
- Implementing smoke tests
- Managing deployment strategies in Kubernetes
Core Concepts
Deployment Strategies
- Rolling Update: Gradual replacement (default K8s)
- Blue-Green: Two identical environments, switch traffic
- Canary: Gradual rollout to subset of users
- Recreate: Stop old, start new (downtime)
Deployment Verification
- Smoke tests
- Health checks
- Performance tests
- Rollback triggers
Blue-Green Deployment
Kubernetes Implementation
# deployments/production/kubernetes/user-service-blue.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service-blue
labels:
app: user-service
version: blue
spec:
replicas: 3
selector:
matchLabels:
app: user-service
version: blue
template:
metadata:
labels:
app: user-service
version: blue
spec:
containers:
- name: user-service
image: goodgo/user-service:v1.0.0
ports:
- containerPort: 5000
---
# deployments/production/kubernetes/user-service-green.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service-green
labels:
app: user-service
version: green
spec:
replicas: 3
selector:
matchLabels:
app: user-service
version: green
template:
metadata:
labels:
app: user-service
version: green
spec:
containers:
- name: user-service
image: goodgo/user-service:v1.1.0
ports:
- containerPort: 5000
---
# Service selector switches between blue/green
apiVersion: v1
kind: Service
metadata:
name: user-service
spec:
selector:
app: user-service
version: blue # EN: Switch to green after verification / VI: Chuyển sang green sau khi xác minh
ports:
- port: 80
targetPort: 5000
Blue-Green Switch Script
#!/bin/bash
# scripts/deployment/blue-green-switch.sh
# EN: Switch between blue and green deployments
# VI: Chuyển đổi giữa blue và green deployments
SERVICE_NAME=$1
CURRENT_VERSION=$(kubectl get service $SERVICE_NAME -o jsonpath='{.spec.selector.version}')
NEW_VERSION=$([ "$CURRENT_VERSION" = "blue" ] && echo "green" || echo "blue")
echo "Switching from $CURRENT_VERSION to $NEW_VERSION"
# EN: Update service selector
# VI: Cập nhật service selector
kubectl patch service $SERVICE_NAME -p "{\"spec\":{\"selector\":{\"version\":\"$NEW_VERSION\"}}}"
# EN: Wait for rollout
# VI: Đợi rollout
kubectl rollout status deployment/$SERVICE_NAME-$NEW_VERSION
# EN: Run smoke tests
# VI: Chạy smoke tests
./scripts/deployment/smoke-tests.sh $SERVICE_NAME
if [ $? -ne 0 ]; then
echo "Smoke tests failed, rolling back"
kubectl patch service $SERVICE_NAME -p "{\"spec\":{\"selector\":{\"version\":\"$CURRENT_VERSION\"}}}"
exit 1
fi
echo "Successfully switched to $NEW_VERSION"
Canary Deployment
Kubernetes Canary with Service Mesh
# deployments/production/kubernetes/user-service-canary.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service-canary
labels:
app: user-service
version: canary
spec:
replicas: 1 # EN: Start with 1 replica (10% traffic) / VI: Bắt đầu với 1 replica (10% traffic)
selector:
matchLabels:
app: user-service
version: canary
template:
metadata:
labels:
app: user-service
version: canary
spec:
containers:
- name: user-service
image: goodgo/user-service:v1.1.0
---
# VirtualService splits traffic
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: user-service
spec:
hosts:
- user-service
http:
- match:
- headers:
canary:
exact: "true"
route:
- destination:
host: user-service
subset: canary
weight: 100
- route:
- destination:
host: user-service
subset: stable
weight: 90
- destination:
host: user-service
subset: canary
weight: 10 # EN: 10% traffic to canary / VI: 10% traffic tới canary
Progressive Canary Rollout
#!/bin/bash
# scripts/deployment/canary-rollout.sh
# EN: Progressive canary rollout
# VI: Progressive canary rollout
SERVICE_NAME=$1
CANARY_PERCENTAGES=(10 25 50 75 100)
for PERCENTAGE in "${CANARY_PERCENTAGES[@]}"; do
echo "Rolling out to $PERCENTAGE%"
# EN: Update VirtualService weight
# VI: Cập nhật VirtualService weight
kubectl patch virtualservice $SERVICE_NAME -p "{\"spec\":{\"http\":[{\"route\":[{\"destination\":{\"subset\":\"canary\"},\"weight\":$PERCENTAGE},{\"destination\":{\"subset\":\"stable\"},\"weight\":$((100-PERCENTAGE))}]}]}}"
# EN: Wait for traffic to stabilize
# VI: Đợi traffic ổn định
sleep 60
# EN: Run health checks
# VI: Chạy health checks
./scripts/deployment/health-checks.sh $SERVICE_NAME
if [ $? -ne 0 ]; then
echo "Health checks failed at $PERCENTAGE%, rolling back"
kubectl patch virtualservice $SERVICE_NAME -p "{\"spec\":{\"http\":[{\"route\":[{\"destination\":{\"subset\":\"canary\"},\"weight\":0},{\"destination\":{\"subset\":\"stable\"},\"weight\":100}]}]}}"
exit 1
fi
echo "Successfully rolled out to $PERCENTAGE%"
done
echo "Canary rollout complete"
Automated Rollback
Rollback Script
#!/bin/bash
# scripts/deployment/rollback.sh
# EN: Automated rollback to previous version
# VI: Rollback tự động về version trước
SERVICE_NAME=$1
NAMESPACE=${2:-production}
# EN: Get previous deployment revision
# VI: Lấy revision deployment trước
PREVIOUS_REVISION=$(kubectl rollout history deployment/$SERVICE_NAME -n $NAMESPACE --no-headers | tail -1 | awk '{print $1}')
if [ -z "$PREVIOUS_REVISION" ]; then
echo "No previous revision found"
exit 1
fi
echo "Rolling back to revision $PREVIOUS_REVISION"
# EN: Rollback deployment
# VI: Rollback deployment
kubectl rollout undo deployment/$SERVICE_NAME -n $NAMESPACE --to-revision=$PREVIOUS_REVISION
# EN: Wait for rollout
# VI: Đợi rollout
kubectl rollout status deployment/$SERVICE_NAME -n $NAMESPACE
echo "Rollback complete"
Automated Rollback on Failure
# .github/workflows/deploy-production.yml
name: Deploy Production
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Deploy to Kubernetes
run: |
kubectl apply -f deployments/production/kubernetes/
kubectl rollout status deployment/user-service
- name: Run Smoke Tests
run: ./scripts/deployment/smoke-tests.sh user-service
- name: Rollback on Failure
if: failure()
run: ./scripts/deployment/rollback.sh user-service production
Deployment Verification
Smoke Tests
// scripts/deployment/smoke-tests.ts
// EN: Smoke tests for deployment verification
// VI: Smoke tests để xác minh deployment
import axios from 'axios';
const SERVICE_URL = process.env.SERVICE_URL || 'http://localhost';
async function runSmokeTests(): Promise<boolean> {
try {
// EN: Health check
// VI: Health check
const healthResponse = await axios.get(`${SERVICE_URL}/health`);
if (healthResponse.status !== 200) {
console.error('Health check failed');
return false;
}
// EN: Basic functionality test
// VI: Test chức năng cơ bản
const testResponse = await axios.get(`${SERVICE_URL}/api/v1/users`, {
timeout: 5000,
});
if (testResponse.status !== 200) {
console.error('Functionality test failed');
return false;
}
console.log('Smoke tests passed');
return true;
} catch (error) {
console.error('Smoke tests failed', error);
return false;
}
}
runSmokeTests().then((success) => {
process.exit(success ? 0 : 1);
});
Health Check Script
#!/bin/bash
# scripts/deployment/health-checks.sh
# EN: Comprehensive health checks
# VI: Health checks toàn diện
SERVICE_NAME=$1
NAMESPACE=${2:-production}
echo "Running health checks for $SERVICE_NAME"
# EN: Check pods are ready
# VI: Kiểm tra pods đã ready
READY_PODS=$(kubectl get pods -n $NAMESPACE -l app=$SERVICE_NAME --field-selector=status.phase=Running --no-headers | wc -l)
if [ $READY_PODS -eq 0 ]; then
echo "No ready pods found"
exit 1
fi
# EN: Check service endpoints
# VI: Kiểm tra service endpoints
ENDPOINTS=$(kubectl get endpoints $SERVICE_NAME -n $NAMESPACE -o jsonpath='{.subsets[0].addresses[*].ip}' | wc -w)
if [ $ENDPOINTS -eq 0 ]; then
echo "No service endpoints found"
exit 1
fi
# EN: Check health endpoint
# VI: Kiểm tra health endpoint
SERVICE_URL=$(kubectl get service $SERVICE_NAME -n $NAMESPACE -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')
if [ -z "$SERVICE_URL" ]; then
SERVICE_URL="http://$SERVICE_NAME.$NAMESPACE.svc.cluster.local"
fi
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" $SERVICE_URL/health)
if [ $HTTP_CODE -ne 200 ]; then
echo "Health endpoint returned $HTTP_CODE"
exit 1
fi
echo "Health checks passed"
Deployment Gates
# .github/workflows/deploy-with-gates.yml
name: Deploy with Gates
jobs:
deploy:
steps:
- name: Deploy
run: kubectl apply -f deployments/
- name: Wait for Rollout
run: kubectl rollout status deployment/service
- name: Smoke Tests Gate
id: smoke-tests
run: ./scripts/deployment/smoke-tests.sh
- name: Performance Tests Gate
if: steps.smoke-tests.outcome == 'success'
run: ./scripts/deployment/performance-tests.sh
- name: Manual Approval Gate
if: steps.smoke-tests.outcome == 'success'
uses: trstringer/manual-approval@v1
with:
secret: ${{ secrets.GITHUB_TOKEN }}
approvers: team-leads
minimum-approvals: 1
issue-title: "Approve deployment"
Best Practices
- Blue-Green: Use for zero-downtime deployments
- Canary: Use for gradual rollouts with monitoring
- Automated Rollback: Always have rollback plan
- Smoke Tests: Run immediately after deployment
- Health Checks: Monitor health continuously
- Gates: Use deployment gates for critical deployments
Common Mistakes
-
No Rollback Plan: Can't recover from failed deployment
# ✅ Always have rollback command ready kubectl rollout undo deployment/service -
Skipping Smoke Tests: Catching issues too late
# ✅ Run smoke tests immediately after deploy - name: Smoke Tests run: ./scripts/smoke-tests.sh -
100% Traffic Switch: All-or-nothing failures
# ❌ BAD: Immediate full switch # ✅ GOOD: Gradual rollout (10% → 50% → 100%) -
No Health Monitoring: Missing deployment issues
# ✅ Monitor health after deployment - name: Monitor Health run: kubectl rollout status deployment/service --timeout=5m
Quick Reference
| Strategy | Risk | Downtime | Resource Cost |
|---|---|---|---|
| Blue-Green | Low | Zero | 2x (temporary) |
| Canary | Low | Zero | +10-20% |
| Rolling | Medium | Zero | 1x |
| Recreate | High | Yes | 1x |
Deployment Commands:
# Apply deployment
kubectl apply -f kubernetes/
# Check rollout status
kubectl rollout status deployment/servi