StatefulSet 创建必须指定 serviceName 且指向已存在的 Headless Service,否则 Pod 卡在 Pending;volumeClaimTemplates 不可更新,需删重建;滚动更新默认逆序逐个进行,暂停需用 rollout pause。
不填 serviceName 会导致 StatefulSet 创建失败,且错误提示非常隐蔽——API Server 不会直接报错,而是让 Pod 卡在 Pending 状态,kubectl describe pod 显示 FailedCreatePodSandBox 或 MissingServiceForStatefulSet 类似提示。
这是 StatefulSet 的硬性要求:它依赖 Headless Service 来提供稳定的网络标识(如 pod-0.my-sts.default.svc.cluster.local),而该 Service 必须由用户显式定义并关联。
serviceName 必须指向一个已存在的 Headless Service(clusterIP: None)selector 必须与 StatefulSet 的 matchLabels 完全一致serviceName 字段值不校验 Service 是否真实存在,但 Pod 启动时会校验 DNS 可达性apiVersion: apps/v1
kind: StatefulSet
metadata:
name: my-sts
spec:
serviceName: "my-headless-svc" # ← 必须匹配已有 Headless Service 名称
replicas: 3
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: nginx
image: nginx:1.25一旦 StatefulSet 创建完成,volumeClaimTemplates 被视为不可变字段。尝试 PATCH 或 PUT 更新(例如扩容 PVC 大小、改 storageClassName)会触发 Invalid value: "volumeClaimTemplates" 错误。
这不是 bug,是 Kubernetes 的设计约束:每个 Pod 对应的 PVC 是基于模板在创建时生成的,后续无法动态重写模板逻辑。
--cascade=orphan 保留 PVC 和 Pod)podManagementPolicy: OrderedReady(默认)以避免并发重建
resources.requests.storage 是允许的(前提是底层 StorageClass 支持扩容)Golang 中通过 clientset.AppsV1().StatefulSets(namespace) 获取列表后,直接用 ListOptions.LabelSelector 最高效。别在 Go 层做二次遍历过滤,既慢又绕过服务端索引。
LabelSelector 支持 =、!=、in、notin 等语法,注意字符串需 URL 编码(client-go 内部自动处理)。
list, err := clientset.AppsV1().StatefulSets("default").List(ctx, metav1.ListOptions{
LabelSelector: "app in (my-app,backend-app),env!=staging",
})
if err != nil {
log.Fatal(err)
}
for _, sts := range list.Items {
fmt.Printf("Found StatefulSet: %s\n", sts.Name)
}StatefulSet 默认按序号从大到小(pod-2 → pod-1 → pod-0)逐个删除重建,但你无法靠 updateStrategy.rollingUpdate.maxUnavailable 控制并发数(该字段对 StatefulSet 无效)。真正起作用的是 revisionHistoryLimit 和 podManagementPolicy。
podManagementPolicy: Parallel 可并行更新所有 Pod,但失去有序性,不适合有主从依赖的场景kubectl rollout pause statefulset/my-sts 可中途暂停滚动更新,对应 API 中 updateStrategy.rollingUpdate.pause 字段(v1.27+)scale 临时缩容再恢复,或用 operator 自行实现分批逻辑真正难处理的是更新中途出错:旧 revision 的 Pod 已被删,新 revision 卡住,此时只能回滚到上一版本(需提前保留 revisionHistoryLimit >= 2)或手动重建旧 Pod。