helm upgrade时报错提示修改了 StatefulSet 中不允许直接更新的字段
·
错误信息如下:
Error: UPGRADE FAILED: cannot patch "kb-cloud-vms-server" with kind StatefulSet: StatefulSet.apps "kb-cloud-vms-server" is invalid: spec: Forbidden: updates to statefulset spec for fields other than 'replicas', 'ordinals', 'template', 'updateStrategy', 'persistentVolumeClaimRetentionPolicy' and 'minReadySeconds' are forbidden
查看sts配置信息未看到不能更新的字段
apiVersion: apps/v1
kind: StatefulSet
metadata:
annotations:
meta.helm.sh/release-name: kb-cloud
meta.helm.sh/release-namespace: kb-cloud
creationTimestamp: "2026-03-26T09:35:08Z"
generation: 1
labels:
app: server
app.kubernetes.io/instance: kb-cloud
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: vms
app.kubernetes.io/version: v1.133.0
helm.sh/chart: victoria-metrics-single-0.28.0
name: kb-cloud-vms-server
namespace: kb-cloud
resourceVersion: "12482"
uid: 5461ca09-780f-4500-8d93-83abcda15fa2
spec:
persistentVolumeClaimRetentionPolicy:
whenDeleted: Retain
whenScaled: Retain
podManagementPolicy: OrderedReady
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
app: server
app.kubernetes.io/instance: kb-cloud
app.kubernetes.io/name: vms
serviceName: kb-cloud-vms-server
template:
metadata:
annotations:
prometheus.io/port: "8428"
prometheus.io/scrape: "true"
creationTimestamp: null
labels:
app: server
app.kubernetes.io/instance: kb-cloud
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: vms
app.kubernetes.io/version: v1.133.0
helm.sh/chart: victoria-metrics-single-0.28.0
spec:
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- preference:
matchExpressions:
- key: node-role.cloud.kubeblocks.io/control-plane
operator: Exists
weight: 100
automountServiceAccountToken: true
containers:
- args:
- --dedup.minScrapeInterval=1s
- --envflag.enable
- --envflag.prefix=VM_
- --httpListenAddr=:8428
- --loggerFormat=json
- --retentionPeriod=48h
- --storageDataPath=/storage
image: sealos.hub:5000/apecloud/victoria-metrics:v1.133.0
imagePullPolicy: IfNotPresent
livenessProbe:
failureThreshold: 10
initialDelaySeconds: 30
periodSeconds: 30
successThreshold: 1
tcpSocket:
port: http
timeoutSeconds: 5
name: vmsingle
ports:
- containerPort: 8428
name: http
protocol: TCP
readinessProbe:
failureThreshold: 3
httpGet:
path: /health
port: http
scheme: HTTP
initialDelaySeconds: 5
periodSeconds: 15
successThreshold: 1
timeoutSeconds: 5
resources:
limits:
cpu: "1"
memory: 1Gi
requests:
cpu: 10m
memory: 10Mi
securityContext: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /storage
name: server-volume
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
serviceAccount: kb-cloud-vms-server
serviceAccountName: kb-cloud-vms-server
terminationGracePeriodSeconds: 60
tolerations:
- effect: NoSchedule
key: node-role.cloud.kubeblocks.io/control-plane
operator: Exists
- effect: NoSchedule
key: node-role.cloud.kubeblocks.io/data-plane
operator: Exists
- effect: NoSchedule
key: node.cloud.kubeblocks.io
operator: Exists
- effect: NoSchedule
key: cloud.kubeblocks.io/node-group
operator: Exists
- effect: NoSchedule
key: kb-controller
operator: Equal
value: "true"
- effect: NoExecute
key: node.kubernetes.io/not-ready
operator: Exists
tolerationSeconds: 10
- effect: NoExecute
key: node.kubernetes.io/unreachable
operator: Exists
tolerationSeconds: 10
updateStrategy:
rollingUpdate:
partition: 0
type: RollingUpdate
volumeClaimTemplates:
- apiVersion: v1
kind: PersistentVolumeClaim
metadata:
creationTimestamp: null
name: server-volume
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 50Gi
volumeMode: Filesystem
status:
phase: Pending
status:
availableReplicas: 1
collisionCount: 0
currentReplicas: 1
currentRevision: kb-cloud-vms-server-645977f45d
observedGeneration: 1
readyReplicas: 1
replicas: 1
updateRevision: kb-cloud-vms-server-645977f45d
updatedReplicas: 1
尝试删除sts再upgrade的方式解决问题
# 1. 备份当前配置(已完成)
kubectl -n kb-cloud get statefulset kb-cloud-vms-server -o yaml > current-statefulset.yaml
# 2. 删除 StatefulSet,但保留 Pod
kubectl -n kb-cloud delete statefulset kb-cloud-vms-server --cascade=orphan
# 3. 验证 Pod 仍在运行
kubectl -n kb-cloud get pods | grep vms-server
# 4. 执行 Helm 升级
helm -n kb-cloud upgrade kb-cloud kb-charts/kubeblocks-cloud-v2.1.169.tgz -f kb-cloud-values.yaml
# 5. 检查新的 StatefulSet 是否创建成功
kubectl -n kb-cloud get statefulset kb-cloud-vms-server
# 6. 查看 Pod 状态(可能需要滚动更新)
kubectl -n kb-cloud get pods -w
为什么先删除sts再upgrade就可以?
核心原理:Kubernetes API 的更新限制 vs 创建时的灵活性
1. StatefulSet 的更新限制
Kubernetes 对 StatefulSet 的 spec 字段有严格的更新限制:
- 通过
kubectl edit或kubectl patch更新现有 StatefulSet 时,只能修改特定字段 - 尝试修改其他字段(如
selector、serviceName、volumeClaimTemplates的结构等)会被 API Server 拒绝
这是 Kubernetes 的设计决策,目的是保证 StatefulSet 管理的 Pod 的稳定性和有序性。
2. Helm upgrade 的工作机制
当执行 helm upgrade 时,Helm 会:
- 生成新的 Kubernetes 资源清单
- 对集群中已存在的资源执行
kubectl apply或kubectl patch - 对于已存在的 StatefulSet,Helm 尝试更新它
如果新版本的 Chart 中,StatefulSet 的 spec 包含了不允许更新的字段变更,API Server 就会拒绝,导致发生看到的错误。
3. 如何绕过这个限制
kubectl -n kb-cloud delete statefulset kb-cloud-vms-server --cascade=orphan
helm upgrade ...
这个方案的关键在于:
| 步骤 | 做了什么 | 效果 |
|---|---|---|
delete --cascade=orphan |
删除 StatefulSet 对象,但保留其管理的 Pod 和 PVC | StatefulSet 对象从 etcd 中移除 |
helm upgrade |
Helm 发现 StatefulSet 不存在,于是执行 kubectl create |
创建全新的 StatefulSet,而不是更新旧的 |
4. 创建 vs 更新的区别
| 操作 | 限制 | 适用场景 |
|---|---|---|
| 创建 StatefulSet | 可以设置所有 spec 字段 |
首次部署,或删除后重建 |
| 更新 StatefulSet | 只能修改少数几个字段 | 滚动更新镜像、副本数等 |
关键点:创建操作不受更新限制的约束!这就是为什么先删除再创建可以解决问题。
5. 为什么 --cascade=orphan 很重要
--cascade=orphan # 孤立模式
- 没有这个参数:删除 StatefulSet 时会同时删除 Pod(级联删除)
- 使用这个参数:只删除 StatefulSet 控制器,Pod 继续运行
这样做的优势:
- ✅ Pod 继续提供服务,业务不中断
- ✅ PVC 保留,数据不丢失
- ✅ 新 StatefulSet 创建后,会接管现有的 Pod 和 PVC
6. 示意图解
升级前状态:
etcd 中: [StatefulSet 对象 A] → 管理 → [Pod-0, PVC-0]
执行过程:
1. delete --cascade=orphan
etcd 中: [StatefulSet 对象 A] 被删除
集群中: [Pod-0, PVC-0] 继续运行 (孤儿状态)
2. helm upgrade (创建新 StatefulSet)
etcd 中: [StatefulSet 对象 B] 被创建
StatefulSet B 发现已有 Pod-0 和 PVC-0 → 接管它们
最终: [StatefulSet 对象 B] → 接管 → [Pod-0, PVC-0]
7. 什么时候不需要此方案?
如果新版本只修改了允许的字段(如 replicas、template 中的镜像),直接 helm upgrade 就能成功:
# 这些字段可以直接更新
- replicas: 1 → 3
- template.spec.containers[0].image: v1.133.0 → v1.134.0
- minReadySeconds
- updateStrategy
8. 验证原理
可以通过以下实验验证:
# 1. 查看当前 StatefulSet 的 resourceVersion
kubectl -n kb-cloud get statefulset kb-cloud-vms-server -o jsonpath='{.metadata.resourceVersion}'
# 2. 使用方案后,查看新的 resourceVersion
# 会发现 resourceVersion 变化了,说明是一个全新的对象
总结
此方案有效的原因:
- 删除操作移除了有更新限制的旧对象
- 创建操作允许设置任意字段
--cascade=orphan保证了业务连续性和数据安全
这就像换房子:不能直接修改旧房子的承重墙(更新限制),但可以拆掉旧房子,在原址建一栋新房子(删除+创建),而里面的家具(Pod 和数据)可以继续使用。
更多推荐
所有评论(0)