引言

在集群日常维护中,删除一个 Namespace 看起来是“删掉一堆资源”的简单动作,但如果控制面无法完成资源发现(discovery),或某些资源的 finalizers 无法被对应控制器清理,Namespace 就会长期卡在 Terminating

本文记录一次删除 olm Namespace 失败的排障过程:先是 NamespaceDeletionDiscoveryFailure,解决后又卡在 ClusterServiceVersion(CSV)清理阶段,最终通过移除 CSV 的 finalizer 完成删除。文末附一套可复用的排查清单与风险提示。

警告olm 相关资源通常属于 Operator Lifecycle Manager(OLM)核心组件。生产集群中不建议随意删除;本文的“强制移除 finalizer / 删除 APIService”属于应急手段,务必理解影响范围后再执行。

现象:Namespace olm 一直 Terminating

删除 olm 后一直停留在 Terminating,查看 Namespace 状态发现:

1
2
3
4
5
6
Discovery failed for some groups, 1 failing: unable to retrieve the
complete list of server APIs: packages.operators.coreos.com/v1: stale GroupVersion
discovery: packages.operators.coreos.com/v1'
reason: DiscoveryFailed
status: "True"
type: NamespaceDeletionDiscoveryFailure

这条信息的关键点是:控制面在做“列出该 Namespace 下所有资源类型并逐一清理”之前,需要先完成 API discovery;而 discovery 过程中,packages.operators.coreos.com/v1 这个聚合 API(aggregated API)无法正常返回,导致删除流程被阻塞。

第一步:从 Namespace Conditions 定位阻塞点

建议第一时间看 Namespace 的 conditions(比“盲删 finalizers”更快定位根因):

1
kubectl describe namespace olm

其中 NamespaceDeletionDiscoveryFailure=True 通常意味着:有 API group/version 在 discovery 阶段不可用,Namespace lifecycle controller 无法确认“有哪些资源类型需要被列出并清理”。

根因一:v1.packages.operators.coreos.com APIService 不可用(ServiceNotFound)

根据报错指向的 group/version,去查对应的 APIService

1
kubectl get apiservice v1.packages.operators.coreos.com

输出类似:

1
2
NAME                                SERVICE                     AVAILABLE   AGE
v1.packages.operators.coreos.com olm/packageserver-service False 163d

这里的信号非常明确:该聚合 API 通过 APIService 挂到 kube-apiserver 的 aggregation layer 上,后端需要一个真实存在且健康的 Service(这里是 olm/packageserver-service)。当它变成 False (ServiceNotFound) 时,kube-apiserver 仍会把这个 API 组暴露在 discovery 路径里,但向后端代理请求会失败,从而触发 Namespace 删除流程的 discovery failure。

你可以进一步核实 Service 是否存在:

1
kubectl -n olm get svc packageserver-service

处理一:优先恢复 packageserver(推荐),否则删除 APIService(应急)

方案A:恢复后端服务(推荐)

如果这是生产集群或你仍需要 OLM 能力,优先做“把 packageserver-service 对应的 Deployment/Pod 修复回来”,让 APIService 变为可用(AVAILABLE=True)。这样 deletion discovery 能自然恢复,后续清理也更可控。

方案B:删除不可用的 APIService(应急)

如果你明确不再需要该聚合 API(例如正在拆除 OLM / 清理残留),可以删除这个 APIService,让 kube-apiserver 的 discovery 不再包含该 group/version,从而解除 Namespace 删除的 discovery 阻塞:

1
kubectl delete apiservice v1.packages.operators.coreos.com

注意:删除后会影响依赖该 API 的能力(例如 OLM 的 packagemanifest 查询等),请在维护窗口评估影响后再做。

执行后你会观察到:很多此前卡在 Terminating 的 Namespace 会被快速清理。但这次事故里,olm 仍然没有被删掉,说明还有第二个阻塞点。

第二步:Discovery 恢复后,继续看“剩余资源 + 剩余 finalizers”

此时再次 describe Namespace,会看到 discovery 相关条件已恢复,但出现另外两条:

1
2
NamespaceContentRemaining True  SomeResourcesRemain  ... clusterserviceversions.operators.coreos.com has 1 resource instances
NamespaceFinalizersRemaining True SomeFinalizersRemain ... operators.coreos.com/csv-cleanup in 1 resource instances

这两条意味着:

  • Namespace 里仍有资源实例没删干净(这里是 ClusterServiceVersion
  • 且该资源实例上仍挂着 finalizer(这里是 operators.coreos.com/csv-cleanup),控制器没有完成清理并移除 finalizer

根因二:CSV packageserver 残留,csv-cleanup finalizer 无法被清理

列出 olm 下的 CSV:

1
kubectl get csv -n olm

输出示例:

1
2
NAMESPACE   NAME          DISPLAY          VERSION   REPLACES   PHASE
olm packageserver Package Server 0.32.0 Pending

此时典型情况是:OLM 相关控制器(负责执行 operators.coreos.com/csv-cleanup)已经不可用或不再工作,导致 finalizer 永远不会被自动移除,进而阻塞 CSV 删除,也阻塞 Namespace 最终删除。

处理二:手动清理 CSV(最后手段:移除 finalizers)

优先尝试正常删除

如果控制器仍然健康,直接删除 CSV 即可:

1
kubectl delete csv packageserver -n olm

控制器失效时:移除 CSV finalizers

当 CSV 一直删不掉(或你已确认需要强制清理),可通过 patch 移除 finalizers:

1
2
kubectl patch csv packageserver -n olm \
-p '{"metadata":{"finalizers":null}}' --type=merge

执行后,CSV 会被立即释放,随后 olm Namespace 也会自动完成终止并消失。

为什么“删 namespace 的 spec.finalizers”不一定有用?

很多人第一反应是把 Namespace 的 spec.finalizers 清空,但这在本案里无效,原因有两类:

  1. Discovery 阶段就失败:控制器连“这个 Namespace 里有哪些资源类型要清理”都无法完整发现,删除流程会直接卡住。
  2. 资源级 finalizer 才是硬阻塞:即使 Namespace 自身 finalizer 被清空,只要 Namespace 内仍有带 finalizer 的资源(例如 CSV),最终删除仍会被阻塞。

换句话说:Namespace 是“结果”,资源与控制器的健康才是“原因”

一套可复用的排查清单(推荐按顺序执行)

1. 看清楚卡在哪一类 condition

1
kubectl describe namespace <ns>
  • NamespaceDeletionDiscoveryFailure=True:优先查 APIService/聚合 API/网络/后端服务
  • NamespaceContentRemaining=True:说明仍有资源残留
  • NamespaceFinalizersRemaining=True:说明仍有资源 finalizer 没被清理

2. 找出所有不可用的 APIService

1
2
kubectl get apiservice | grep False
kubectl describe apiservice <apiservice-name>

3. 枚举 Namespace 内仍然存在的资源(通用方法)

1
2
kubectl api-resources --verbs=list --namespaced -o name | \
xargs -n 1 kubectl get -n <ns>

4. 针对“有 finalizer 的残留资源”做精准清理

以本案为例:

1
2
kubectl get csv -n <ns>
kubectl get csv <name> -n <ns> -o yaml

确认 finalizers 后,再决定是否移除(强制手段)。

5. 最后手段:强制完成 Namespace finalize(谨慎)

如果你已经:

  • 修复/移除了不可用的 APIService(不再触发 discovery failure)
  • 手工清理了 Namespace 内的残留资源(或你已接受残留风险)

仍然无法删除 Namespace,可以考虑通过 finalize 端点强制移除 spec.finalizers(参考 GKE 文档的流程)。这是高风险操作,可能导致集群中遗留“看不见但还存在”的资源。

1
2
3
4
5
kubectl get ns <ns> -o yaml > ns-terminating.yml
kubectl proxy
curl -H "Content-Type: application/yaml" -X PUT \
--data-binary @ns-terminating.yml \
http://127.0.0.1:8001/api/v1/namespaces/<ns>/finalize

风险提示与建议

  • 能修复控制器就别强拆 finalizer:finalizer 本意是保证清理动作被执行;直接移除意味着你承诺“后果自负”,后续可能留下孤儿资源或脏数据。
  • 删除 APIService 是在“切断聚合 API 的入口”:它能让 discovery 变干净,但也会让对应 API 彻底不可用。更推荐先恢复后端服务。
  • 系统组件 Namespace 要谨慎:如果是 OpenShift 集群内置的 OLM(例如 openshift-operator-lifecycle-manager 等),不要照搬本文对系统命名空间的删除操作。

参考链接

  • Kubernetes Finalizers:https://kubernetes.io/docs/concepts/overview/working-with-objects/finalizers/
  • Kubernetes API Aggregation Layer:https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/apiserver-aggregation/
  • Configure the Aggregation Layer:https://kubernetes.io/docs/tasks/extend-kubernetes/configure-aggregation-layer/
  • GKE:Troubleshoot namespace stuck in the Terminating state:https://cloud.google.com/kubernetes-engine/docs/troubleshooting/terminating-namespaces
  • OLM:ClusterServiceVersion(概念)https://olm.operatorframework.io/docs/concepts/crds/clusterserviceversion/
  • OLM:PackageManifest API(列出可安装 Operator)https://olm.operatorframework.io/docs/tasks/list-operators-available-to-install/

本文由 AI 辅助生成,如有错误或建议,欢迎指出。