🔵🟢 第12课:蓝绿部署

📚 核心概念

🎯 蓝绿部署:零停机切换策略

蓝绿部署维护两套完全相同的生产环境——蓝环境(当前线上)和绿环境(新版本)。切换只需修改路由指向,实现瞬间切换。

📋 蓝绿部署工作流

             ┌──────────────┐
             │   负载均衡器  │
             └──────┬───────┘
                    │
       ┌────────────┼────────────┐
       ▼                         ▼
┌──────────────┐         ┌──────────────┐
│ 🔵 蓝环境    │         │ 🟢 绿环境    │
│ v1.0 (线上)  │         │ v2.0 (待切)  │
│ 3 replicas   │         │ 3 replicas   │
└──────────────┘         └──────────────┘
       │                         │
       └─────────┬───────────────┘
                 ▼
         ┌──────────────┐
         │  共享数据库   │
         │ (需迁移兼容)  │
         └──────────────┘

切换:100%→蓝  ══切换══▶  100%→绿
回滚:100%→绿  ══回滚══▶  100%→蓝

📝 蓝绿部署 vs 其他策略

策略停机时间回滚速度资源成本风险暴露
蓝绿部署秒级2x全量切换瞬间
金丝雀发布分钟级1.1x渐进式暴露
滚动更新近零分钟级1x新老共存期
Recreate有停机分钟级1x全量暴露
💡 数据库迁移技巧:使用Expand-Contract模式——先加新列(兼容旧版),切换后删旧列。

⌨️ 命令实操

⌨️ 实操命令

1. Nginx蓝绿部署 ✅

Nginx蓝绿
# 创建蓝绿环境目录
mkdir -p /opt/bluegreen/{blue,green,conf}

# 蓝环境(当前线上v1)
cat > /opt/bluegreen/blue/index.html << 'EOF'

🔵 蓝环境 v1.0 - 当前线上

EOF # 绿环境(新版本v2) cat > /opt/bluegreen/green/index.html << 'EOF'

🟢 绿环境 v2.0 - 新版本

EOF # Nginx配置——指向蓝环境 cat > /opt/bluegreen/conf/nginx.conf << 'EOF' upstream active_env { server 127.0.0.1:8081; # blue } server { listen 80; location / { proxy_pass http://active_env; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } } EOF # 切换到绿环境脚本 cat > /opt/bluegreen/switch-to-green.sh << 'SCRIPT' #!/bin/bash set -euo pipefail echo "🟢 切换到绿环境..." if ! curl -sf http://127.0.0.1:8082/health > /dev/null; then echo "❌ 绿环境健康检查失败!"; exit 1 fi sed -i 's/server 127.0.0.1:8081.*# blue/server 127.0.0.1:8082; # green/' /opt/bluegreen/conf/nginx.conf nginx -t && nginx -s reload echo "✅ 已切换到绿环境" SCRIPT chmod +x /opt/bluegreen/switch-to-green.sh # 回滚到蓝环境 cat > /opt/bluegreen/switch-to-blue.sh << 'SCRIPT' #!/bin/bash sed -i 's/server 127.0.0.1:8082.*# green/server 127.0.0.1:8081; # blue/' /opt/bluegreen/conf/nginx.conf nginx -t && nginx -s reload echo "✅ 已回滚到蓝环境" SCRIPT chmod +x /opt/bluegreen/switch-to-blue.sh

2. Kubernetes蓝绿部署 ✅

K8s蓝绿
# 蓝环境Deployment
cat > blue-deployment.yaml << 'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-blue
  labels:
    app: myapp
    slot: blue
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
      slot: blue
  template:
    metadata:
      labels:
        app: myapp
        slot: blue
    spec:
      containers:
      - name: myapp
        image: myregistry/myapp:v1.0
        ports:
        - containerPort: 8080
        readinessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 10
EOF

# 绿环境Deployment(仅改slot和image)
cat > green-deployment.yaml << 'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-green
  labels:
    app: myapp
    slot: green
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
      slot: green
  template:
    metadata:
      labels:
        app: myapp
        slot: green
    spec:
      containers:
      - name: myapp
        image: myregistry/myapp:v2.0
        ports:
        - containerPort: 8080
        readinessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 10
EOF

# Service——通过selector切换
cat > service.yaml << 'EOF'
apiVersion: v1
kind: Service
metadata:
  name: myapp-svc
spec:
  selector:
    app: myapp
    slot: blue    # ← 切换时改为green
  ports:
  - port: 80
    targetPort: 8080
  type: ClusterIP
EOF

kubectl apply -f blue-deployment.yaml
kubectl apply -f green-deployment.yaml
kubectl apply -f service.yaml

# 切换到绿环境
kubectl patch svc myapp-svc -p '{"spec":{"selector":{"slot":"green"}}}'

# 回滚到蓝环境
kubectl patch svc myapp-svc -p '{"spec":{"selector":{"slot":"blue"}}}'

3. 自动化蓝绿部署脚本 ✅

自动化脚本
#!/bin/bash
# bluegreen-deploy.sh — 完整蓝绿部署自动化
set -euo pipefail
NAMESPACE="${1:-default}"
APP_NAME="myapp"
IMAGE_TAG="${2:?Usage: $0  }"

log() { echo "[$(date '+%H:%M:%S')] $*"; }

get_active_slot() {
    kubectl get svc myapp-svc -n "$NAMESPACE" -o jsonpath='{.spec.selector.slot}'
}

deploy_inactive() {
    local inactive="$1"
    log "部署 ${APP_NAME}-${inactive} (${IMAGE_TAG})..."
    kubectl set image deployment/"${APP_NAME}-${inactive}"         myapp="myregistry/${APP_NAME}:${IMAGE_TAG}" -n "$NAMESPACE"
    kubectl rollout status deployment/"${APP_NAME}-${inactive}"         -n "$NAMESPACE" --timeout=300s
}

health_check() {
    log "健康检查..."
    for i in $(seq 1 30); do
        if curl -sf "http://myapp-svc.$NAMESPACE.svc.cluster.local/health" > /dev/null 2>&1; then
            log "健康检查通过 ✅"; return 0
        fi
        log "等待就绪... ($i/30)"; sleep 5
    done
    log "❌ 健康检查超时"; return 1
}

switch_traffic() {
    local target="$1"
    log "切换流量到 $target..."
    kubectl patch svc myapp-svc -n "$NAMESPACE"         -p "{"spec":{"selector":{"slot":"${target}"}}}"
    log "流量切换完成 ✅"
}

main() {
    local active=$(get_active_slot)
    local inactive=$([[ "$active" == "blue" ]] && echo "green" || echo "blue")
    log "当前活跃: $active, 部署到: $inactive"
    deploy_inactive "$inactive"
    if ! health_check; then
        log "❌ 失败,保持 $active"; exit 1
    fi
    switch_traffic "$inactive"
    log "✅ 完成!回滚命令:kubectl patch svc myapp-svc -n $NAMESPACE -p '{"spec":{"selector":{"slot":"$active"}}}'"
}
main "$@" 

📐 架构图

🔵🟢 K8s蓝绿部署架构

                    ┌─────────────────────┐
                    │      Ingress         │
                    └──────────┬───────────┘
                    ┌──────────▼───────────┐
                    │    Service           │
                    │  selector: slot=blue │◄── 切换点
                    └──────┬───────────────┘
              ┌────────────┴────────────┐
              ▼                         ▼
    ┌───────────────────┐    ┌───────────────────┐
    │ 🔵 myapp-blue     │    │ 🟢 myapp-green    │
    │ 3 pods (v1.0)     │    │ 3 pods (v2.0)     │
    └───────────────────┘    └───────────────────┘
              │                         │
              └────────────┬────────────┘
                  ┌────────▼─────────┐
                  │  共享PVC/DB      │
                  └──────────────────┘

🔧 故障排查

❌ 切换后出现502错误

排查命令
# 检查Service Endpoints
kubectl get endpoints myapp-svc
# 检查Pod readiness
kubectl get pods -l app=myapp -o wide
# 查看Pod日志
kubectl logs -l slot=green --tail=50
# 检查readinessProbe
kubectl describe deploy myapp-green | grep -A5 Readiness
# 常见:readinessProbe未通过/Pod label不匹配/port不匹配

❌ 数据库迁移不兼容导致切换失败

排查命令
# 查看应用错误日志
kubectl logs -l slot=green | grep -i "error"
# Expand-Contract模式:
# Phase 1: 加新列(兼容旧版)ALTER TABLE users ADD COLUMN phone VARCHAR(20);
# Phase 2: 切换流量到绿环境
# Phase 3: 确认后删旧列 ALTER TABLE users DROP COLUMN old_phone;
# 回滚:只需切回蓝环境,旧代码兼容当前schema

🏆 成就解锁:蓝绿部署专家!

掌握Nginx/K8s蓝绿切换、自动化脚本、数据库兼容策略——零停机不是梦想,是工程实践