本文将介绍当你执行kubectl delete语句时,K8s内部都执行了哪些操作。
以及为何有些资源’删除不掉’(具体表现为一直Terminating,删除namespace时很容易遇到这种情况)
接下来,我们聚焦讨论以下四个方面:
资源的哪些属性会对删除操作产生影响?
方便起见,以下所有示例都将使用ConfigMaps和基本shell命令来演示该过程
Kubernetes提供了几个不同的命令,您可以使用它们来创建、读取、更新和删除对象。
出于本文的目的,我们将重点讨论四个kubectl命令:create、get、patch和delete.
下面是kubectl delete命令的基本示例
创建名为mymap的configmap对象:
$ kubectl create configmap mymap
configmap/mymap created
查看名为mymap的configmap对象:
$ kubectl get configmap/mymap
NAME DATA AGE
mymap 0 12s
删除名为mymap的configmap对象:
$ kubectl delete configmap/mymap
configmap "mymap" deleted
基本delete命令的删除操作状态图非常简单:
删除操作看似简单,但是有很多因素可能会干扰删除,包括finalizers与owner references属性
上面我们提到了两个属性:finalizers与owner references可能会干扰删除操作,导致删除阻塞或失败。
那Finalizers是什么?会对删除有何影响呢?
当要理解Kubernetes中的资源删除原理时,了解finalizers(以下我们称finalizers为终结器)的工作原理是很有帮助的,
可以帮助您理解为什么有些对象无法被删除。
终结器是资源发出预删除操作信号的属性,
控制着资源的垃圾收集,并用于提示控制器在删除资源之前执行哪些清理操作。
finalizers本质是包含键的列表,不具有实际意义。与annotations(注释)类似,finalizers是可以被操作的(增删改)。
以下终结器您可能遇到过:
这两个终结器作用于卷,以防止卷被意外删除。
类似地,一些终结器可用于防止资源被删除,但不由任何控制器管理。
下面是一个自定义的configmap,它没有具体值,但包含一个终结器:
$ cat <<EOF | kubectl create -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: mymap
finalizers:
- kubernetes
EOF
终结器通常用于名称空间(namespace),而管理configmap资源的控制器不知道该如何处理finalizers字段。
下面我们尝试删除这个configmap对象:
$ kubectl delete configmap/mymap &
configmap "mymap" deleted
$ jobs
[1]+ Running kubectl delete configmap/mymap
Kubernetes返回该对象已被删除,然而它并没有真正意义上被删除,而是在删除的过程中。
当我们试图再次获取该对象时,我们发现该对象多了个deletionTimestamp(删除时间戳)字段。
$ kubectl get cm mymap -o yaml
apiVersion: v1
kind: ConfigMap
metadata:
creationTimestamp: "2021-09-29T11:04:40Z"
deletionGracePeriodSeconds: 0
deletionTimestamp: "2021-09-29T11:04:55Z"
finalizers:
- kubernetes
managedFields:
- apiVersion: v1
fieldsType: FieldsV1
fieldsV1:
f:metadata:
f:finalizers:
.: {}
v:"kubernetes": {}
manager: kubectl
operation: Update
time: "2021-09-29T11:04:40Z"
name: mymap
namespace: default
resourceVersion: "1378430"
selfLink: /api/v1/namespaces/default/configmaps/mymap
uid: 8d6ca0b1-4840-4597-8164-a63b526dbf5f
简而言之,当我们删除带有finalizers字段的对象时,该对象仅仅是被更新了,被标记为待删除状态,而不是被删除了。
这是因为Kubernetes获取到该对象包含终结器,通过添加deletionTimestamp(删除时间戳)字段将其置于只读状态(删除终结器键更新除外)。
换句话说,在删除该对象终结器之前,删除都不会完成。
接下来我们尝试通过patch命令删除终结器,并观察configmap/mymap是否会被’真正’删除。
$ kubectl patch configmap/mymap \
--type json \
--patch='[ { "op": "remove", "path": "/metadata/finalizers" } ]'
configmap/mymap patched
再次检索该对象:
$ kubectl get cm mymap
Error from server (NotFound): configmaps "mymap" not found
发现该对象已被真正删除,下图描述了带有finalizers字段的对象删除流程:
总结:当您试图删除一个带有终结器的对象,它将一直处于预删除只读状态,
直到控制器删除了终结器键或使用Kubectl删除了终结器。一旦终结器列表为空,Kubernetes就可以回收该对象,并将其放入要从注册表中删除的队列中
上面我们提到了两个属性:finalizers与owner references可能会干扰删除操作,导致删除阻塞或失败。
并介绍了Finalizers,接下来我们聊聊Owner References.
Owner References(所有者引用或所有者归属)描述了对象组之间的关系。
指定了资源彼此关联的属性,因此可以级联删除整个资源树。
当存在所有者引用时,将处理终结器规则。所有者引用由名称和UID组成
所有者引用相同名称空间内的链接资源,它还需要UID以使该引用生效(确保唯一)。
Pods通常具有对所属副本集的所有者引用。 因此,当Deloyment或有StatefulSet被删除时,子ReplicaSet和Pod将在流程中被删除。
我们通过下面的例子,来理解Owner References(所有者引用)的工作原理:
1.创建cm/mymap-parent对象
$ cat <<EOF | kubectl create -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: mymap-parent
EOF
2.获取cm/mymap-parent的UID
CM_UID=$(kubectl get configmap mymap-parent -o jsonpath="{.metadata.uid}")
3.创建cm/mymap-child对象,并设置ownerReferences字段声明所有者引用(通过kind、name、uid字段确保选择器可以匹配到)
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: mymap-child
ownerReferences:
- apiVersion: v1
kind: ConfigMap
name: mymap-parent
uid: $CM_UID
EOF
即cm/mymap-parent为cm/mymap-child的父对象,此时我们删除cm/mymap-parent对象并观察cm/mymap-child对象状态
$ kubectl get cm
NAME DATA AGE
mymap-child 0 2m44s
mymap-parent 0 3m
$ kubectl delete cm mymap-parent
configmap "mymap-parent" deleted
$ kubectl get cm
No resources found in default namespace.
即我们通过删除父对象,间接删除了父对象下的所有子对象
。 这种删除k8s中被称为级联删除。我们可不可以只删除父对象,而不删除子对象呢?
答案是: 可以的,删除时通过添加–cascade=false参数实现,我们通过下面的例子来验证:
$ cat <<EOF | kubectl create -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: mymap-parent
EOF
$ CM_UID=$(kubectl get configmap mymap-parent -o jsonpath="{.metadata.uid}")
$ cat <<EOF | kubectl create -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: mymap-child
ownerReferences:
- apiVersion: v1
kind: ConfigMap
name: mymap-parent
uid: $CM_UID
EOF
$ kubectl delete --cascade=false configmap/mymap-parent
configmap "mymap-parent" deleted
$ kubectl get cm
NAME DATA AGE
mymap-child 0 107s
–cascade=false参数实际改变了父-子资源的删除顺序,k8s中关于父-子资源删除策略有以下三种:
Foreground
: 子资源在父资源之前被删除(post-order)Background
: 父资源在子资源之前被删除 (pre-order)Orphan
: 忽略所有者引用进行删除下面这段内容比较晦涩,没太理解:
Keep in mind that when you delete an object and owner references have been specified, finalizers will be honored in the process.
This can result in trees of objects persisting, and you end up with a partial deletion.
At that point, you have to look at any existing owner references on your objects,
as well as any finalizers, to understand what’s happening
有一种情况可能需要强制删除命名空间:
如果您已经删除了一个命名空间,并删除了它下面的所有对象,但名称空间仍然存在,一般为Terminating状态。
则可以通过更新名称空间的finalize属性来强制删除该名称空间。
会话1:
$ kubectl proxy
会话2:
$ NAMESPACE_NAME=test
cat <<EOF | curl -X PUT \
127.0.0.1:8001/api/v1/namespaces/$NAMESPACE_NAME/finalize \
-H "Content-Type: application/json" \
--data-binary @-
{
"kind": "Namespace",
"apiVersion": "v1",
"metadata": {
"name": "$NAMESPACE_NAME"
},
"spec": {
"finalizers": null
}
}
EOF
我们应该谨慎思考是否强制删除命名空间,因为这样做可能只删除名称空间,命名空间下的其他资源删不完全,最终导致留下孤儿对象。
比如资源对象A存在于ddd命名空间,此时若强制删除ddd命名空间, 且对象A又未被删除,那么对象A便成了孤儿对象。
使用Finalizers控制k8s资源删除
[kubernetes]Finalizers和优雅终止解析