本文主要记录 Namespace 长期处于 Terminating、常规 kubectl delete / patch finalizer 均失败时的排查过程;案例中根因是 KubeVirt 相关 ValidatingWebhook 指向的 Service 已不存在,形成删除死锁。文末补充在 API 路径走不通时通过 etcd 清理的高危操作说明。
需要注意的是:直接操作 etcd 属于破坏性手段,仅适用于确认业务可丢弃、且 API 与 webhook 已无法自愈的实验/可重建环境。生产集群应优先修复 webhook / 卸载 Operator,而不是一上来 etcdctl del。
问题现象
查看集群 Namespace:
kubectl get ns -o wide
示例输出(主机名与内网信息已脱敏):
NAME STATUS AGE
argocd Active 398d
default Active 491d
kube-system Active 491d
kubevirt Terminating 433d
nginx-gateway Terminating 190d
purelb Active 491d
...
kubevirt、nginx-gateway 等长期停在 Terminating,常规删除无进展。
环境背景
| 项目 | 说明 |
|---|---|
| 场景 | 自建 Kubernetes 集群 |
| 问题 NS | 示例为 kubevirt(曾部署 KubeVirt) |
| 症状特征 | deletionTimestamp 已写入;spec.finalizers 仍含 kubernetes |
| 关联组件 | ValidatingWebhook / Operator Service 缺失 |
具体发行版与 K8s 小版本以现场为准(待确认)。
排查过程
查看 Namespace 详情
kubectl get ns kubevirt -o yaml
关键字段示意(已裁剪):
metadata:
name: kubevirt
deletionTimestamp: "2023-11-13T09:04:18Z"
spec:
finalizers:
- kubernetes
status:
phase: Terminating
conditions:
- type: NamespaceDeletionDiscoveryFailure
status: "True"
reason: DiscoveryFailed
message: >-
Discovery failed for some groups ... subresources.kubevirt.io/v1: stale GroupVersion discovery
- type: NamespaceDeletionContentFailure
status: "True"
reason: ContentDeletionFailed
message: >-
Failed to delete all resource types ... failed calling webhook "kubevirt-validator.kubevirt.io":
Post "https://kubevirt-operator-webhook.kubevirt.svc:443/kubevirt-validate-delete?timeout=10s":
service "kubevirt-operator-webhook" not found
- type: NamespaceContentRemaining
status: "False"
reason: ContentRemoved
解读:
- 删除已发起(有
deletionTimestamp),kubernetesfinalizer 仍在,说明控制面认为清理未完成。 NamespaceDeletionContentFailure=True是关键信号:删除内容时调用 webhook 失败。- 错误核心:
service "kubevirt-operator-webhook" not found—— 校验/删除 webhook 还在,后端 Service 已没了。
Namespace 卡在 Terminating 的常见原因
在深入案例前,先对齐一般原因(便于对照 describe / conditions):
- 资源删不干净:对象带 finalizer;PVC/PV 绑定;CRD 与自定义资源依赖。
- Webhook 阻断:Validating/MutatingWebhook 指向的 Service/Pod 不可用,更新与删除被 API 拒绝。
- 控制面异常:namespace-controller、apiserver 异常(相对少见,先查前面两类)。
优先命令:
kubectl describe ns <namespace>
kubectl get all,crd -n <namespace>
kubectl get validatingwebhookconfiguration,mutatingwebhookconfiguration
检查 NS 内残留
kubectl get all -n kubevirt
示例仍可见 KubeVirt 聚合资源:
NAME AGE PHASE
kubevirt.kubevirt.io/kubevirt 433d Deployed
尝试编辑 / 删除 CR 失败
试图去掉 CR 上的 finalizer 或直接删除:
kubectl edit kubevirt.kubevirt.io/kubevirt -n kubevirt
# 拟删除 finalizers: foregroundDeleteKubeVirt
报错示意:
error: ... failed calling webhook "kubevirt-update-validator.kubevirt.io":
Post "https://kubevirt-operator-webhook.kubevirt.svc:443/kubevirt-validate-update?timeout=10s":
service "kubevirt-operator-webhook" not found
kubectl delete kubevirt.kubevirt.io/kubevirt -n kubevirt
同样因 delete 校验 webhook 找不到 Service 而失败。
强制删 Namespace 仍不够
kubectl delete ns kubevirt --grace-period=0 --force
可能提示 force deleted,但 kubectl get ns 仍显示 Terminating。原因是 finalizer / 内容删除逻辑仍被 webhook 与 API 对象卡住,--force 并不能绕过所有准入与 etcd 中的残留关系。
原因分析
删除链路形成死锁:
- 删除 Namespace / CR 需要调用 KubeVirt 的 ValidatingWebhook。
- Webhook 配置仍指向
kubevirt-operator-webhook.kubevirt.svc。 - 该 Service(及可能的 Operator 负载)已不存在。
- 准入失败 → 资源删不掉 →
kubernetesfinalizer 不移除 → Namespace 永久Terminating。
NamespaceDeletionDiscoveryFailure(stale GroupVersion)说明 KubeVirt 相关 APIService / 发现也处于不健康状态,进一步拖累清理。
优先修复路径(推荐,本文案例未完整走通时再考虑 etcd):
- 重新安装/恢复 KubeVirt Operator,使 webhook Service 可用,再按官方流程卸载。
- 或删除/修改相关
ValidatingWebhookConfiguration/MutatingWebhookConfiguration(确认无其他业务依赖后),再删 CR 与 Namespace。 - 清理失效
APIService,消除 discovery 失败。
仅在上述路径不可用、且接受数据丢失时,才考虑下一节的 etcd 强清。
解决方法
常规手段(优先)
移除 Namespace finalizer(仅当确认内容已空或可丢弃):
kubectl patch ns <namespace-name> -p '{"metadata":{"finalizers":null}}' --type=merge
若 webhook 仍拦截对子资源的操作,这一步也可能不够;需先处理 webhook 配置。
删除失效 webhook 配置示例(名称以现场 kubectl get 为准):
kubectl get validatingwebhookconfiguration | grep -i kubevirt
kubectl delete validatingwebhookconfiguration virt-operator-validator virt-api-validator
# 视情况处理 mutatingwebhookconfiguration
然后再删 CR、CRD 与 Namespace。
高危:etcd 直接删除残留键
以下操作会绕过 API 语义,可能导致集群状态不一致。执行前:
- 备份 etcd
- 确认目标键仅属于待废弃组件
- 在维护窗口操作,并准备好从备份恢复
列出相关键(示例,需已配置 etcdctl 与证书,路径按发行版调整):
etcdctl get / --prefix --keys-only=true | grep virt
可见 CRD、APIService、ClusterRole(Binding)、Webhook 配置、Namespace、CR 实例等大量前缀。
按键删除(示意;务必人工审阅列表,禁止无过滤整库删除):
# 示例:对审阅后的键逐条或可控地批删
etcdctl del /registry/namespaces/kubevirt
# 以及对应 CRD、webhook、RBAC、kubevirt CR 等键
案例中曾用类似:
etcdctl get / --prefix --keys-only=true | grep virt | xargs -t -I{} etcdctl del {}
del 返回 1 表示删到键,0 表示当时已不存在。该写法风险极高:grep virt 可能误伤无关键,不建议在生产照搬;应导出列表、人工确认后再删。
验证
kubectl get ns
kubectl get crd | grep -i kubevirt || true
期望:kubevirt Namespace 消失;KubeVirt 相关 CRD 不再列出(若已删 CRD 键)。案例中清理后 kubectl get ns 不再出现 kubevirt,kubectl get crd 中 KubeVirt 系列 CRD 消失,仅保留集群其它组件 CRD。
同集群上若还有 nginx-gateway 等 Terminating,需单独按同样方法看 conditions,不要假设同一根因。
验证结果
- 在移除 etcd 中 KubeVirt 相关对象后,API 侧 Namespace 与 CRD 列表恢复干净(以当时
kubectl get为准)。 - 强制
kubectl delete ns --force单独使用未能结束 Terminating。 - 根因确认:webhook Service 缺失导致 ContentDeletion 失败,而非单纯“忘了 patch finalizer”。
注意事项
- 先修 webhook / 官方卸载,再考虑 etcd。把 etcd 当默认清 NS 手段会埋下更难查的脑裂。
kubectl patch finalizers null在仍有保护性 webhook 或未删净资源时可能无效或有副作用。- 删除 CRD 会级联影响该类型全部自定义资源;操作前确认无残留业务 VM / CR。
- etcd 前缀因发行版、k8s 版本可能不同;以本集群
etcdctl get为准。 - 本文命令输出为历史排障记录的脱敏整理,不保证适用于当前 KubeVirt 版本的卸载流程。