一、什么是蓝绿发布?
蓝绿发布(Blue-Green Deployment)是一种旨在实现零宕机的发布策略。其核心思想是同时维护两个完全相同的生产环境,然后通过切换流量来实现平滑升级。
简单来说:
- 🟦 蓝色环境:当前正在对外服务的线上版本。
- 🟩 绿色环境:包含新版本、等待上线的环境。
- 🔁 流量切换:通过一个“开关”将用户流量从蓝色环境瞬时切换到绿色环境。
- ⚡ 瞬间完成:切换动作通常能在毫秒级内完成。
- 🔙 秒级回滚:如果新版本出现问题,可以立即将流量切回蓝色环境。
在 Kubernetes 中实现蓝绿发布,其本质就是:
利用 Service 的 selector 标签选择器 作为一个高效的“流量开关”。
二、生产级架构设计
📌 典型生产拓扑
┌─────────────┐
│ Ingress │
└──────┬──────┘
│
┌──────────────┐
│ prod-svc │ ← 只修改 selector
└──────┬───────┘
│
┌──────────────┴──────────────┐
│ │
┌─────────────┐ ┌─────────────┐
│ app-blue │ │ app-green │
│ Deployment │ │ Deployment │
└──────┬──────┘ └──────┬──────┘
│ │
Pod x3 Pod x3
在生产环境中,我们通常采用以下设计原则:
- 为每个版本(蓝、绿)创建独立的
Deployment。
- 可以(可选地)为每个版本创建独立的
Service,用于内部调试。
- 创建一个统一的、面向用户的
prod-svc(生产服务)。
- 入口(
Ingress)只指向这个统一的 prod-svc。
- 发布时,仅需修改
prod-svc 的 selector,即可将流量导向目标版本。
三、完整生产级 YAML 示例
1️⃣ 蓝版本 Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-blue
namespace: blue-green-demo
spec:
replicas: 3
selector:
matchLabels:
app: myapp
version: blue
template:
metadata:
labels:
app: myapp
version: blue
spec:
containers:
- name: app
image: yourusername/demo:v1
ports:
- containerPort: 80
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "300m"
memory: "256Mi"
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 5
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 10
periodSeconds: 10
2️⃣ 绿版本 Deployment
创建绿版本 Deployment 时,核心修改只有两处:
version: green
image: yourusername/demo:v2
其他配置,如资源限制、探针等,应与蓝版本保持一致以确保环境一致性。
3️⃣ 统一生产 Service
这个服务是流量切换的关键。
apiVersion: v1
kind: Service
metadata:
name: prod-svc
namespace: blue-green-demo
spec:
selector:
app: myapp
version: blue # 初始指向蓝版本
ports:
- port: 80
targetPort: 80
type: ClusterIP
四、流量切换(核心步骤)
切换到绿版本
执行一条 kubectl patch 命令即可完成切换:
kubectl patch svc prod-svc -n blue-green-demo \
-p '{"spec":{"selector":{"version":"green"}}}'
- ⚡ 切换时间:毫秒级。Kubernetes 会立即更新 Service 的端点(Endpoints)列表。
- 🟢 无需重启:不会影响任何正在运行的 Pod。
- 🟢 无需滚动更新:瞬间完成全量切换。
回滚到蓝版本
如果绿版本出现问题,回滚操作同样简单直接:
kubectl patch svc prod-svc -n blue-green-demo \
-p '{"spec":{"selector":{"version":"blue"}}}'
这就是蓝绿发布的最大价值之一:
回滚不需要重新部署旧版本应用。旧版本一直以完整的副本数在运行,随时待命。
五、数据库升级的真正难点(90% 团队踩坑)
蓝绿发布最大的风险往往不在于应用本身,而在于数据库的结构变更。
试想一下,如果绿版本的应用代码依赖一个修改过的数据库 Schema(例如删除了某个字段),而你将流量切到绿版本后发现了问题。当你试图切回蓝版本时,蓝版本的应用会因为找不到那个已被删除的字段而崩溃,导致回滚失效。
常见的破坏性变更包括:
✅ 正确做法:Expand → Migrate → Contract 模式
-
Expand(扩展阶段)
- 在发布新版本之前,先对数据库进行“扩展”操作。
- 只新增字段或表,绝对不删除、不修改现有的字段。
- 绿版本和蓝版本的代码此时都能兼容新旧两套 Schema。
-
Migrate(迁移阶段)
- 部署绿版本应用,此时它开始写入新字段,但可能仍能读取旧字段。
- 运行数据迁移脚本,将旧数据填充到新结构中(异步进行)。
- 切换流量到绿版本。由于 Schema 是兼容的,即使此时回滚,蓝版本也能正常运行。
-
Contract(收缩阶段)
- 确认绿版本稳定运行足够长的时间(例如一周)。
- 在下一个发布周期中,安全地删除那些不再被任何版本使用的旧字段或表。
六、真实生产事故案例
🚨 事故一:未配置 ReadinessProbe
现象:
- 绿版本
Deployment 刚刚创建,Pod 处于 ContainerCreating 或 Running 但应用未完成初始化。
- 运维人员执行了流量切换。
- Service 将请求转发到了尚未就绪的绿版本 Pod。
- 结果:全站出现大量
502 Bad Gateway 或 503 Service Unavailable 错误。
原因:
Kubernetes 默认只检查 Pod 是否 Running,而不会判断 Pod 内的应用是否已经准备好接收流量。如果没有 readinessProbe,Service 会立即将新 Pod 纳入负载均衡池。
解决:
如前面 YAML 所示,必须为你的应用配置有效的 readinessProbe。只有当探针通过后,Pod 才会被标记为就绪,Service 才会向其转发流量。
🚨 事故二:Session 存储在本地内存
现象:
- 流量从蓝版本切换到绿版本后。
- 用户会话(Session)全部丢失,所有用户需要重新登录。
- 部分正在进行中的操作因会话失效而报错。
原因:
许多应用的默认 Session 存储在本地进程内存中。当流量从一个环境的 Pod 切换到另一个环境的 Pod 时,新的 Pod 无法访问旧 Pod 内存中的 Session 数据。
解决:
使用外部集中式存储来管理 Session,例如 Redis 或数据库。确保蓝绿两个环境的应用都能访问同一份 Session 存储,这样切换时用户状态不会丢失。
🚨 事故三:定时任务重复执行
现象:
- 蓝绿两个环境的
Deployment 同时运行。
- 两个环境内的应用实例都激活了各自的定时任务(如每天凌晨的数据报表生成)。
- 导致任务被重复执行两次,造成数据错乱或重复计算。
解决方案:
- 使用分布式锁:在任务执行前,尝试从一个共享存储(如 Redis、ZooKeeper)获取全局锁,确保同一时间只有一个实例能执行任务。
- 环境区分:在定时任务逻辑中判断当前 Pod 的标签(如
version),只允许当前流量所指版本(即 prod-svc 选中的版本)执行任务。非活动版本的任务自动跳过。
七、与滚动更新对比
| 维度 |
滚动更新 |
蓝绿发布 |
| 是否双环境 |
❌ 同一套 Deployment 逐渐替换 |
✅ 两套独立的 Deployment |
| 是否可秒级回滚 |
❌ 回滚需重新滚动部署旧镜像 |
✅ 只需切换 Service selector |
| 是否支持完整预验证 |
❌ 无法用真实流量测试完整新版本 |
✅ 可在隔离环境中完整测试新版本 |
| 资源消耗 |
低(副本数小幅波动) |
高(长期双倍资源) |
| 适合场景 |
普通服务、无状态应用、迭代迅速 |
核心系统、关键业务、追求高可用 |
八、进阶:结合 Service Mesh
如果你在集群中已经部署了 Service Mesh(如 Istio 或 Linkerd),蓝绿发布可以变得更加智能和强大,轻松升级为金丝雀发布:
- 精细化流量控制:可以实现
10% -> 30% -> 50% -> 100% 的渐进式流量切换,而非“一刀切”。
- 基于指标的自动决策:监控新版本(绿版本)的请求错误率、延迟等指标。如果指标超出阈值,Mesh 可以自动执行回滚,将流量切回蓝版本。
- 多维条件路由:可以根据请求头(如
内部用户)、用户标识等条件,将特定流量导向新版本进行测试。
九、CI/CD 自动化切流脚本
将切换操作脚本化并集成到你的 DevOps 流水线中是生产级实践的关键。下面是一个简单的 Bash 脚本示例:
#!/bin/bash
NAMESPACE=blue-green-demo
SERVICE=prod-svc
TARGET=$1
if [[ "$TARGET" != "blue" && "$TARGET" != "green" ]]; then
echo "Usage: ./switch.sh [blue|green]"
exit 1
fi
kubectl patch svc $SERVICE -n $NAMESPACE \
-p "{\"spec\":{\"selector\":{\"version\":\"$TARGET\"}}}"
echo "Switched to $TARGET"
你可以将这个脚本轻松集成到:
- Jenkins Pipeline
- GitLab CI/CD
- GitHub Actions
- ArgoCD 的钩子(Hook)中
十、生产级最佳实践 Checklist
上线前,请逐一核对以下清单:
- ✅ 必须配置
readinessProbe:确保流量只被导向已就绪的 Pod。
- ✅ 必须设置资源限制(
requests/limits):防止单个版本异常消耗所有集群资源。
- ✅ 数据库 Schema 向前兼容:严格遵守 Expand → Migrate → Contract 模式。
- ✅ Session 外部化:使用 Redis 等外部存储,避免会话丢失。
- ✅ 定时任务加分布式锁:或设计为仅活动版本执行。
- ✅ 保留旧版本至少 24-48 小时:为彻底的回滚和问题排查留出足够时间。
- ✅ 自动化切流:将切换命令封装为脚本或流水线步骤,禁止在生产环境使用
kubectl edit 手工修改。
十一、蓝绿发布的成本
采用蓝绿发布策略,最直观的成本增加在于资源:
- 在发布和观察期间,你需要同时运行两套完整的应用实例,这意味着临时的资源使用量接近翻倍。
- 会对集群的 CPU、内存配额产生额外压力。
然而,这笔成本换来的是:
零宕机时间、秒级回滚能力、以及对发布风险极强的控制力。
对于电商交易、支付网关、核心后台管理等关键业务系统而言,这种用资源换取稳定性和可控性的权衡,通常是绝对值得的。
十二、总结
蓝绿发布在 Kubernetes 中的实现,其核心难点并不在于编写 YAML 文件,而在于背后的系统性设计与规范。
它考验的是团队的以下能力:
- 流量控制能力:能否精确、瞬时地操纵流量指向。
- 数据兼容设计:能否处理好最棘手的数据库变更问题。
- 快速回滚能力:预案是否完善,流程是否顺畅。
- 自动化与流程化能力:发布是脚本化的标准操作,还是依赖人工的“骚操作”。
正如很多在 云栈社区 交流的一线工程师所共识的:真正的生产级蓝绿发布,
技术实现只占 30%,而规范、流程与团队协作占了剩下的 70%。
希望这份从架构到踩坑指南的完整梳理,能帮助你不仅仅“跑通 Demo”,更能有信心将蓝绿发布应用于真实的生产环境之中。