kubectl delete
这个命令我们几乎每天都在使用,看起来很容易理解,不就是删除 Kubernetes 的某种资源吗?但是要完全理解 Delete 操作还是很有挑战的,理解删除操作背后真正的原理,能够帮助你从容应对一些奇葩场景。这篇文章将从以下几个方面详细解释删除操作背后的故事:
- 基本的删除操作
- Finalizers 和 Owner Reference 会对删除操作产生什么影响
- 如何利用 propagation policy 改变删除的顺序
- 通过 examples 演示上述删除操作
简单起见,所有示例都将使用 ConfigMaps 和 Basic Shell 命令来演示操作过程。
The basic delete
Kubernetes 有几种不同的命令,您可以使用它允许您创建,读取,更新和删除对象。 出于本博客文章的目的,我们将专注于四个 kubectl 命令:create,
get,
patch, 和
delete。
下面是几个最简单的 kubectl delete
使用场景:
1 | kubectl create configmap mymap |
1 | kubectl get configmap/mymap |
1 | kubectl delete configmap/mymap |
1 | kubectl get configmap/mymap |
演示了一个 configMap 从创建、查询、到删除、再查询的过程,这个过程可以用下面的状态图表示:
这种 delete
操作是非常简单直观的,但当你遇到 Finalizer
和 Owner References
的时候就会出现各种难以理解的现象。
Understanding Finalizers
在理解 Kubernetes 的资源删除时,了解 Finalizers
的工作原理能够在你无法删除资源时给你一些解决问题的灵感。
Finalizers
是触发 pre-delete
操作的关键,能够控制资源的垃圾回收。Finalizers
设计的初衷是帮助 controller 在处理资源删除之前,优先处理一些 clean up 的逻辑。但是 Finalizers
并不包含代码逻辑,使用上跟 Annotation
有些相似,很容易被添加或者删除。
你可以已经见过下面的 Finalizers
:
1 | kubernetes.io/pv-protection |
这两个 Finalizer
是用来防止误删除 volume 的,类似功能的 Finalizer 还有很多。
下面是一个自定义的 configmap,只包含一个 Finalizer
:
1 | cat <<EOF | kubectl create -f - |
负责管理 configmap 的 controller 并不知道该如何处理 kubernetes
这个 Finalizer。现在我尝试去删除这个 configmap:
1 | kubectl delete configmap/mymap & |
Kubernetes 会返回一个信息告诉你这个 configmap 已经被删除了,然而并不是传统意义上的删除,而是出于一个 deleting
的状态。当我们再次执行 get
操作去获取该 configmap,会发现 configmap 资源已经被修改过了,deletionTimeStamp 设置了值。
1 | kubectl get configmap/mymap -o yaml |
换句话说,这个 configmap 资源并没有被删除而是被更新了,这是因为 Kubernetes 看到资源中有一个 Finalizer
后把它置为了 read-only 状态。这个 configmap 只有在移除了 Finalizer
后,才会真正从集群中删除。
我们使用 patch
命令移除 Finalizer
来验证一下上面的说法。
1 | kubectl patch configmap/mymap \ |
此时我们再去 get
configmap,发现已经获取不到了。
1 | kubectl get configmap/mymap -o yaml |
上面展示了当一个资源带有 Finalizer
时,执行删除操作后的状态流转图。
Owner References
Owner reference 描述了多种资源之间的关联关系。它们是关于资源的属性,可以彼此指定关联关系,因此可以做到级联删除。
Kubernetes 中最常见的具备 owner reference 关系的场景就是 pod 将 replica set 作为自己的 owner,所以当 deployment 或者 statefulSet 删除的时候,作为子资源的 replica set 和 pod 都将被删除。
下面的例子解释了 owner reference 的工作原理。我们首先创建了一个父资源,然后创建子资源的时候指定其 owner reference:
1 | cat <<EOF | kubectl create -f - |
当删除子资源时,父资源并不会被删除:
1 | kubectl get configmap |
我们再根据上面的 yaml 建立资源,然后我们删除父资源,这时发现所有的资源都被删除了:
1 | kubectl get configmap |
简而言之,当父 - 子资源之间存在 owner reference 关系的时候,我们删除父资源,子资源也会随之删除,这叫做 cascade
(级联删除)。默认情况下,cascade
的值是 true,你可以执行 kubectl delete
的时候加上参数 --cascade=false
,这样就可以只删除父资源而保留子资源。
在以下示例中,有一对父子资源,如果我使用 --cascade = false
删除父资源,但子资源仍然存在:
1 | kubectl get configmap |
——cascade
参数能够控制 API 中的删除传播策略( propagation policy),它允许控制删除对象的顺序。在下面的示例中,使用 API 访问创建一个带有 background 传播策略的自定义 delete API 调用:
1 | kubectl proxy --port=8080 & |
要注意,不能使用 kubectl 在命令行上指定传播策略。必须使用自定义 API 调用来指定。需要创建一个代理,这样就可以从客户端访问 API Server,并执行 curl 命令来执行该删除命令。
一共有三种传播策略:
Foreground
:先删除子资源再删除父资源(post-order)
Background
:先删除父资源再删除子资源(pre-order)
Orphan
:忽略 owner reference
要注意,假如一个资源中同时设置了 owner reference 和 finalizer,finalizer 的优先级是最高的。
Forcing a Deletion of a Namespace
你可以已经遇到过执行 kubectl delete ns NS
后,ns 无法删除的情况,这时候你可以通过 update 所删除 ns 的 Finalizer
字段来实行强制删除。这个操作会通知到 namespace controller 移除 finalizer :
1 | cat <<EOF | curl -X PUT \ |
这个操作要谨慎,因为它可能只删除名称空间,而将孤立资源留在现在不存在的名称空间中,会让集群处于让人很迷惑的状态。如果发生这种情况,可以手动重新创建该名称空间,有孤立的资源对象将重新出现在刚刚创建的名称空间下,可以手动清理和恢复。
Key Takeaways
上面的例子说明,Finalizer
能够阻止 Kubernetes 删除资源,但是通常在代码中添加 Finalizer
是有原因的,因此应该在手动删除它之前进行检查。Owner reference 支持级联删除资源,但 Finalizer
的优先级更高。最后,可以使用传播策略通过自定义 API 调用指定删除顺序,从而控制对象的删除方式。通过上面的例子,相信大家对 Kubernetes 中的删除工作原理有了更多的了解,现在可以使用测试集群自己尝试一下。
原文地址:https://kubernetes.io/blog/2021/05/14/using-finalizers-to-control-deletion/