StatefulSet金丝雀发布实战:用Partition实现安全的版本灰度
在有状态应用的更新中,直接全量替换版本风险太高——一旦新镜像有问题,可能导致整个集群不可用。金丝雀发布(Canary Deployment)通过先更新部分实例验证,再逐步扩大范围,能极大降低风险。而StatefulSet的Partition
(分区)功能,正是实现这种灰度更新的利器。本文结合我的实操过程,带你掌握这一核心技能。
一、什么是StatefulSet的金丝雀发布?
简单说,StatefulSet的金丝雀发布就是:通过配置Partition
参数,控制只有部分Pod更新到新版本,其余仍保持旧版本。验证新版本没问题后,再调整Partition
扩大更新范围,直到全量完成。
为什么必须用Partition
?
StatefulSet的Pod有严格序号(如web-0
到web-4
),而Partition
的作用是:
- 序号 ≥ Partition的Pod:会被更新到新版本
- 序号 < Partition的Pod:保持旧版本不变
这种按序号控制的方式,特别适合有状态应用(比如让序号大的从节点先更新,验证没问题再更新主节点)。
二、实战步骤:从准备到灰度更新
环境准备:先扩缩容到5个副本
首先我需要足够的实例来演示“部分更新”,所以先将副本数从2扩容到5:
# 扩容到5个副本(生成web-0到web-4)
kubectl scale sts web --replicas=5
# 查看Pod状态(确认所有实例正常运行)
kubectl get po
# 输出:
# NAME READY STATUS RESTARTS AGE
# web-0 1/1 Running 0 7h5m
# web-1 1/1 Running 0 42m
# web-2 1/1 Running 0 37s
# web-3 1/1 Running 0 36s
# web-4 1/1 Running 0 35s
此时所有Pod都使用旧版本镜像(nginx:1.7.9
),可以通过describe
命令确认:
# 查看任意Pod的镜像版本
kubectl describe po web-0 | grep "Image:"
# 输出:Image: nginx:1.7.9
核心操作:配置Partition实现金丝雀更新
我的目标是:让序号3、4的Pod先更新到新版本(nginx:1.24.1
),序号0、1、2保持旧版本。这需要通过Partition=3
实现。
步骤1:编辑StatefulSet配置,设置Partition和新镜像
# 用edit命令进入交互式编辑
kubectl edit sts web
在编辑器中找到updateStrategy
部分,添加partition: 3
,并修改镜像为nginx:1.24.1
:
spec:
updateStrategy:
rollingUpdate:
partition: 3 # 核心:只更新序号≥3的Pod
type: RollingUpdate
template:
spec:
containers:
- name: nginx
image: nginx:1.24.1 # 新版本镜像
保存退出后,Kubernetes会自动触发更新。
步骤2:观察更新结果——只有部分Pod更新
等待几分钟后查看Pod状态:
# 查看所有Pod的状态
kubectl get po
# 输出中可以看到:
# web-4 0/1 ContainerCreating 0 55s (正在拉取新镜像)
# web-3 1/1 Running 0 5m15s(可能已更新或等待)
# web-2 1/1 Running 0 5m16s(保持旧版本)
# web-1 1/1 Running 0 47m (保持旧版本)
# web-0 1/1 Running 0 7h10m (保持旧版本)
再通过describe
确认镜像版本:
# 查看序号≥3的Pod(web-4)
kubectl describe po web-4 | grep "Image:"
# 输出:Image: nginx:1.24.1 (已更新到新版本)
# 查看序号<3的Pod(web-2)
kubectl describe po web-2 | grep "Image:"
# 输出:Image: nginx:1.7.9 (保持旧版本)
关键观察:
web-3
和web-4
(序号≥3)会被更新到nginx:1.24.1
web-0
、web-1
、web-2
(序号<3)仍使用旧镜像nginx:1.7.9
这就是金丝雀发布的核心效果——只让部分实例验证新版本。
三、Partition参数的工作原理
很多初学者会疑惑:Partition=3
为什么会只更新序号≥3的Pod?这需要结合StatefulSet的更新逻辑理解:
-
StatefulSet更新时,会先创建新的控制器版本(Revision)
新镜像会生成一个新的Revision,而Partition
决定了哪些Pod会切换到这个新Revision。 -
序号与Partition的对比决定是否更新
- 当
Partition=N
时:- 序号 ≥ N 的Pod:会被更新到新Revision(新版本)
- 序号 < N 的Pod:保持在旧Revision(旧版本)
- 当
-
更新顺序仍遵循StatefulSet的规则
即使设置了Partition
,更新仍按“从高序号到低序号”进行(先更新web-4
,再更新web-3
),避免有状态应用的依赖关系被破坏。
四、常见问题与解决方案
问题1:设置Partition后,目标Pod未更新
我的web-4
一度处于ContainerCreating
状态,查看事件发现是镜像拉取中:
kubectl describe po web-4 | grep Events -A 10
# 输出:
# Normal Pulling 85s kubelet Pulling image "nginx:1.24.1"
解决:耐心等待镜像拉取完成,或确认镜像名称正确(比如是否写成1.24
而非1.24.1
)。
问题2:如何扩大更新范围?
如果验证web-3
和web-4
的新版本没问题,想继续更新web-2
,只需调整Partition
:
# 再次编辑StatefulSet,将Partition改为2
kubectl edit sts web
# 修改:partition: 2
# 此时序号≥2的Pod(web-2、3、4)都会更新到新版本
问题3:新版本有问题,如何回滚?
如果发现web-4
运行异常,需立即停止更新并回滚:
# 方法1:将镜像改回旧版本,Partition保持3(只回滚已更新的Pod)
kubectl edit sts web
# 修改image为nginx:1.7.9
# 方法2:如果需要全量回滚,将Partition设为0(所有Pod回滚)
kubectl edit sts web
# 修改partition: 0
五、实操命令总结
操作目的 | 命令 | 说明 |
---|---|---|
准备环境(扩容) | kubectl scale sts web --replicas=5 | 生成足够的Pod用于灰度 |
配置金丝雀更新 | kubectl edit sts web | 在updateStrategy 中添加partition: 3 并修改镜像 |
查看Pod状态 | kubectl get po | 观察哪些Pod在更新/运行 |
确认镜像版本 | kubectl describe po <pod名称> grep "Image:" | 验证是否按预期更新 |
扩大更新范围 | kubectl edit sts web | 减小Partition值(如从3→2) |
回滚更新 | kubectl edit sts web | 将镜像改回旧版本,或调整Partition |
六、初学者注意事项
-
Partition是“序号阈值”,不是“数量”
Partition=3
不是“更新3个Pod”,而是“更新序号≥3的Pod”,别混淆“序号”和“数量”。 -
更新顺序仍为“从高到低”
即使设置Partition=3
,更新也会先web-4
再web-3
,这是StatefulSet对有状态应用的保护。 -
镜像名称要准确
实操中nginx:1.24.1
是存在的,但nginx:1.9.1
可能拉不下来,建议先在节点上用docker pull
验证镜像是否可拉取。 -
逐步扩大范围更安全
不要一次从Partition=3
跳到Partition=0
,建议按“3→2→1→0”逐步调整,每次验证后再扩大。
总结:StatefulSet金丝雀发布的核心价值
对于有状态应用(如数据库、分布式集群),金丝雀发布不是可选而是必需——它能在更新过程中保留“回退余地”。StatefulSet的Partition
通过序号控制更新范围,完美适配有状态应用的依赖关系(比如先更新从节点,再更新主节点)。
掌握这个技能后,你可以:
- 用少量实例验证新版本稳定性
- 发现问题时快速缩小影响范围
- 按可控节奏完成全量更新
记住:有状态应用的更新核心是“稳”,而Partition
就是实现“稳”的关键工具。下次更新有状态应用时,不妨试试这种灰度方式,比直接全量更新安心多了。
StatefulSet的安全重建
一、安全删除StatefulSet的正确方法
直接删除StatefulSet会导致Pod被终止,但如果有持久化存储(PVC),数据卷不会被自动删除。为了彻底清理并重新开始,建议按以下步骤操作:
1. 先缩容到0,确保所有Pod优雅终止
# 将副本数缩容到0,让Pod优雅关闭
kubectl scale sts web --replicas=0
# 确认所有Pod已终止
kubectl get po | grep web
# 应该没有web-*的Pod显示
2. 删除StatefulSet资源,如果有service服务也要删除
# 删除StatefulSet(注意:不会删除PVC)
kubectl delete sts web
# 验证删除成功
kubectl get sts web
# 应该显示 "Error from server (NotFound)"
3. (可选)删除相关的PVC和PV(如果需要彻底清理数据)
如果你使用了持久化存储,且希望完全重置,可以删除相关的PVC:
# 查看是否有web相关的PVC
kubectl get pvc | grep web
# 如果有,逐个删除(注意:这会删除数据!)
kubectl delete pvc <pvc名称>
二、用web.yaml重新创建StatefulSet
确认所有资源已清理后,就可以用你的YAML文件重新部署:
# 检查web.yaml内容(确保镜像版本正确)
cat web.yaml
# 示例web.yaml内容(关键部分)
# spec:
# replicas: 5 # 确认副本数
# updateStrategy:
# rollingUpdate:
# partition: 3 # 按需调整Partition
# template:
# spec:
# containers:
# - name: nginx
# image: nginx:1.24.1 # 确认镜像版本
# 应用配置创建StatefulSet
kubectl apply -f web.yaml
# 查看创建状态
kubectl get sts web -w
# 等待READY显示5/5
五、总结:StatefulSet重建的最佳实践
- 先缩容后删除:避免直接删除导致Pod强制终止
- 保留PVC:如果需要保留数据,不要删除PVC
- 检查YAML配置:重建前确认镜像版本、Partition等参数正确
- 预拉取镜像:提前在节点上拉取镜像,减少部署时间
- 验证部署结果:创建后检查Pod状态、镜像版本和事件日志
- 总结
# 1. 先将StatefulSet缩容到0
kubectl scale sts web --replicas=0
kubectl get po -l app=nginx # 确认所有web Pod已终止
# 2. 删除StatefulSet和Service(按资源类型和名称删除)
kubectl delete sts,svc web nginx
# 3. 验证删除结果
kubectl get sts,svc | grep -E 'web|nginx'
# 应该没有web和nginx相关的资源显示
# 4. 重建(使用更新后的配置文件)
kubectl apply -f web.yaml
# 5. 监控创建过程
kubectl get sts,svc,po -l app=nginx -w
# 等待所有Pod变为Running状态,Service就绪
https://github.com/0voice