起因

在我们日常使用 k8s 中,可能会遇到这样的情况:在删除 namespace 时,往往会遇到资源没有被删除的情况,资源处于 terminating 的状态,这时我们该如何解决了,寻找到的解决方法往往是如下:

1 运行以下命令查看处于 terminating 状态的资源(这里以namespace 为例):

kubectl get namespaces

2 选择一个Terminating namespace,并查看namespace 中的finalizer。运行以下命令:

kubectl get namespace <terminating-namespace> -o yaml

得到类似这样的信息:

apiVersion: v1
kind: Namespace
metadata:
  creationTimestamp: "2021-01-20T15:18:06Z"
  deletionTimestamp: "2021-01-21T02:50:02Z"
  name: <terminating-namespace>
  resourceVersion: "3249493"
  selfLink: /api/v1/namespaces/knative-eventing
  uid: f300ea38-c8c2-4653-b432-b66103e412db
spec:
  finalizers:
  - kubernetes
status:
  phase: Terminating

3 导出json格式到tmp.json:

kubectl get namespace <terminating-namespace> -o json > tmp.json

4 编辑 tmp.json 删除 finalizers 字段的值

5 开启 proxy: kubectl proxy

6 打开新终端,输入以下命令:

curl -k -H "Content-Type: application/json" -X PUT --data-binary @tmp.json http://127.0.0.1:8001/api/v1/namespaces/<terminating-namespace>/finalize

7 检查该namespace 是否被删除:

kubectl get namespace <terminating-namespace>

可以看到 namespace 已经被删除了

Finalizers 是什么

从上面可以得知删除 finalizers 字段就能顺利删除 terminating 状态下的资源,这是为什么呢? 接下来由我来简单介绍一下。

Finalizer 是带有命名空间的键,告诉 Kubernetes 等到特定的条件被满足后, 再完全删除被标记为删除的资源。 Finalizer 提醒控制器清理被删除的对象拥有的资源。

当你告诉 Kubernetes 删除一个指定了 Finalizer 的对象时, Kubernetes API 会将该对象标记为删除,使其进入只读状态。 此时控制平面或其他组件会采取 Finalizer 所定义的行动, 而目标对象仍然处于终止中(Terminating)的状态。 这些行动完成后,控制器会删除目标对象相关的 Finalizer。 当 metadata.finalizers 字段为空时,Kubernetes 认为删除已完成。

你可以使用 Finalizer 控制资源的垃圾收集。 例如,你可以定义一个 Finalizer,在删除目标资源前清理相关资源或基础设施。

Finalizers 通常不指定要执行的代码。 相反,它们通常是特定资源上的键的列表,类似于注解。 Kubernetes 自动指定了一些 Finalizers,但你也可以指定你自己的。

Finalizers 是如何工作的

在你使用资源清单创建资源时,可以在 metadata.finalizers 指定 Finalizers 。当年删除资源时,k8s 相应的控制器会 修改对象,将你开始执行删除的时间添加到 metadata.deletionTimestamp 字段,将该对象标记为只读,直到其 metadata.finalizers 字段为空。 然后,控制器试图满足资源的 Finalizers 的条件。 每当一个 Finalizer 的条件被满足时,控制器就会从资源的 finalizers 字段中删除该键。 当该字段为空时,垃圾收集继续进行。 你也可以使用 Finalizers 来阻止删除未被管理的资源。

参考