在Kubernetes中,StatefulSet的优势不仅在于稳定的身份标识,更在于其有序的更新和回滚机制。
和Deployment不同,StatefulSet的扩缩容有严格的顺序——扩容时从低序号到高序号创建,缩容时从高序号到低序号删除。这对分布式集群(如ZooKeeper)尤为重要,避免核心节点先被删除。
# 查看初始状态(2个副本)
kubectl get sts
# 输出:NAME READY AGE
# web 2/2 5h47m
# 扩容到5个副本
kubectl scale sts web --replicas=5
# 观察扩容过程(新Pod按web-2→web-3→web-4顺序创建)
kubectl get po -w
# 输出会看到:
# web-2 0/1 Pending 0 0s
# web-2 1/1 Running 0 10s
# web-3 0/1 Pending 0 0s
# ...(依次创建)
# 缩容回2个副本(多余的web-4→web-3→web-2将被删除)
kubectl scale sts web --replicas=2
# 查看缩容结果
kubectl get sts
# 输出:NAME READY AGE
# web 2/2 5h58m
从事件日志能清晰看到这个顺序:
kubectl describe sts web | grep "SuccessfulCreate\|SuccessfulDelete"
# 扩容时:先创建web-2,再web-3,最后web-4
# 缩容时:先删除web-4,再web-3,最后web-2
这就是StatefulSet对有状态应用的保护——比如数据库集群,不会先删除主节点(通常是低序号实例)。
StatefulSet支持像Deployment一样的版本控制,每次修改Pod模板(如镜像版本)都会生成新的修订版本(Revision),方便回滚到历史状态。
最常见的更新是修改镜像版本,这里我用kubectl patch
直接修改(也可以编辑配置文件后apply
)也可以直接 kubect edit
:(文末给出三种更新方式)
# 将镜像从nginx:1.7.9更新为nginx:1.9.1
kubectl patch sts web --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"nginx:1.9.1"}]'
更新会按顺序进行:先更新web-0
,确认就绪后再更新web-1
(这就是RollingUpdate策略)。
每次更新后,Kubernetes会记录版本信息,用rollout history
查看:
# 查看所有版本
kubectl rollout history sts web
# 输出:
# REVISION CHANGE-CAUSE
# 1 # 初始版本(nginx:1.7.9)
# 2 # 第一次更新(nginx:1.9.1)
# 查看指定版本的详细配置
kubectl rollout history sts web --revision=2
# 输出会显示该版本使用的镜像、端口等信息
我更新到nginx:1.9.1
后,web-1
一直卡在ImagePullBackOff
(镜像拉取失败),这时回滚到上一个可用版本是最佳选择:
# 回滚到指定版本(这里回滚到版本1)
kubectl rollout undo sts web --to-revision=1
但回滚后可能遇到一个问题:部分Pod仍沿用旧镜像配置(比如web-1
还在尝试拉取nginx:1.9.1
)。这是因为Kubernetes有时会缓存旧配置,解决办法很简单——删除有问题的Pod,StatefulSet会自动用新配置重建:
# 删除卡住的Pod
kubectl delete pod web-1
# 查看重建后的状态(会使用版本1的镜像)
kubectl get po web-1
# 输出:web-1 1/1 Running 0 4s
现象:执行rollout undo
后,web-1
状态还是ImagePullBackOff
,仍在拉取旧镜像。
原因:kubelet节点缓存了旧的Pod配置,未及时更新。
解决:手动删除Pod触发重建(StatefulSet会自动按最新配置创建新Pod):
kubectl delete pod web-1
现象:回滚后执行kubectl rollout history sts web --revision=1
,提示“unable to find the specified revision”。
原因:每次回滚或更新都会生成新的修订版本(比如回滚到版本1后,会生成版本3),旧版本编号会被覆盖。
解决:无需纠结旧编号,直接查看当前历史:
# 查看最新历史
kubectl rollout history sts web
# 找到目标版本的新编号(比如版本3是nginx:1.7.9),再回滚
kubectl rollout undo sts web --to-revision=3
rollout status
一直显示“Waiting for pods”现象:执行kubectl rollout status sts web
后,长时间显示“Waiting for 1 pods to be ready”。
原因:部分Pod未就绪(比如镜像拉取失败、健康检查未通过)。
解决:
kubectl describe pod web-1
操作 | 命令 | 说明 |
---|---|---|
查看版本历史 | kubectl rollout history sts <名称> |
列出所有修订版本 |
查看指定版本详情 | kubectl rollout history sts <名称> --revision=<编号> |
查看某版本的配置(如镜像) |
回滚到上一版本 | kubectl rollout undo sts <名称> |
无需指定编号,回滚到最近一次更新前 |
回滚到指定版本 | kubectl rollout undo sts <名称> --to-revision=<编号> |
精确回滚到目标版本 |
查看更新/回滚状态 | kubectl rollout status sts <名称> |
确认是否所有Pod都完成更新 |
强制重建Pod | kubectl delete pod |
解决缓存导致的配置不生效问题 |
get po
或rollout status
确认状态。方式 | 适用场景 | 难度 | 特点 |
---|---|---|---|
kubectl apply -f <文件> |
修改配置文件后批量更新 | 低 | 最安全,支持重复执行,需先编辑文件 |
kubectl edit <资源类型> <名称> |
临时修改单个资源,快速验证 | 中 | 直接编辑运行中的资源,无需保存文件 |
kubectl patch <资源类型> <名称> |
脚本化、自动化更新,修改单个字段 | 高 | 使用JSON Patch语法,适合CI/CD |
kubectl edit
更新 StatefulSet这是最直观的交互式编辑方式,适合初学者:
# 编辑名为web的StatefulSet
kubectl edit sts web
# 执行后会打开默认编辑器(如vi),显示当前StatefulSet的YAML配置
# 找到并修改镜像版本(通常在 spec.template.spec.containers[0].image 路径下)
# 保存并退出编辑器,Kubernetes会自动应用修改
kubectl patch
更新 StatefulSet这是更精确的字段级修改方式,适合脚本化操作:
# 方式1:使用JSON Patch格式(官方推荐)
kubectl patch sts web --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value": "nginx:1.9.1"}]'
# 方式2:使用merge格式(更简单,但有局限性)
kubectl patch sts web -p '{"spec":{"template":{"spec":{"containers":[{"name":"nginx","image":"nginx:1.9.1"}]}}}}'
op
: 操作类型(replace
/add
/remove
)path
: JSON路径,指向要修改的字段(如 /spec/template/spec/containers/0/image
)value
: 新值kubectl apply
更新 StatefulSet这是最推荐的“声明式”更新方式:
# 1. 编辑本地配置文件
vim web.yaml
# 修改其中的镜像版本
# 2. 应用修改
kubectl apply -f web.yaml
推荐顺序:
kubectl apply
→ kubectl edit
→ kubectl patch
具体场景建议:
apply
,保持配置文件与集群状态一致edit
,快速修改验证patch
,集成到CI/CD流程避免踩坑:
edit
修改后又用 apply
),可能导致配置冲突patch
适合简单字段修改,复杂修改建议用 apply
kubectl get <资源> -o yaml
查看当前配置,避免意外覆盖kubectl get sts web -o yaml
查看完整配置kubectl get sts web -o json | jq '.spec.template.spec.containers[0].image'
patch
报错 “field is immutable” 怎么办?StatefulSet的某些字段(如 serviceName
)不可修改,需删除重建:
kubectl delete sts web
kubectl apply -f web.yaml
# 查看StatefulSet状态
kubectl get sts web
# 查看Pod使用的镜像
kubectl get po web-0 -o jsonpath='{.spec.containers[0].image}'
# 查看事件日志
kubectl describe sts web | grep Events -A 20