深入理解 Kubernetes Deletion

Go Official Blog

共 5964字,需浏览 12分钟

 · 2021-07-23

b90727cad68cd2c1d0ca23f3e3051275.webp

kubectl delete 这个命令我们几乎每天都在使用,看起来很容易理解,不就是删除 Kubernetes 的某种资源吗?但是要完全理解 Delete 操作还是很有挑战的,理解删除操作背后真正的原理,能够帮助你从容应对一些奇葩场景。这篇文章将从以下几个方面详细解释删除操作背后的故事:

  1. 基本的删除操作
  2. Finalizers 和 Owner Reference 会对删除操作产生什么影响
  3. 如何利用  propagation policy 改变删除的顺序
  4. 通过 examples 演示上述删除操作

简单起见,所有示例都将使用 ConfigMaps 和 Basic Shell 命令来演示操作过程。

The basic delete

Kubernetes 有几种不同的命令,您可以使用它允许您创建,读取,更新和删除对象。出于本博客文章的目的,我们将用到四个 kubectl 命令:create,get,patch,和delete。

下面是最简单的 kubectl delete 使用场景:

kubectl create configmap mymap
configmap/mymap created
kubectl get configmap/mymap
NAME    DATA   AGE
mymap   0      12s
kubectl delete configmap/mymap
configmap "mymap" deleted
kubectl get configmap/mymap
Error from server (NotFound): configmaps "mymap" not found

演示了一个 configMap 从创建、查询、到删除、再查询的过程,这个过程可以用下面的状态图表示:

f67cc9e4dbe0abe5aa3c8b296581ac6d.webpstate diagra for delete

这种 delete 操作是非常简单直观的,但当你遇到 FinalizerOwner References 的时候就会出现各种难以理解的现象。

Understanding Finalizers

在理解 Kubernetes 的资源删除时,了解 Finalizers 的工作原理能够在你无法删除资源时给你一些解决问题的灵感。

Finalizers 是触发 pre-delete 操作的关键,能够控制资源的垃圾回收。Finalizers 设计的初衷是帮助 controller 在处理资源删除之前,优先处理一些 clean up 的逻辑。但是 Finalizers 并不包含代码逻辑,使用上跟 Annotation 有些相似,很容易被添加或者删除。

你可以已经见过下面的 Finalizers:

kubernetes.io/pv-protection
kubernetes.io/pvc-protection

这两个 Finalizer 是用来防止误删除 volume 的,类似功能的 Finalizer 还有很多。

下面是一个自定义的 configmap,只包含一个 Finalizer:

cat <<EOF | kubectl create -f -
apiVersion: v1
kind: ConfigMap
metadata:
  name: mymap
  finalizers:
  - kubernetes
EOF

负责管理 configmap 的 controller 并不知道该如何处理 kubernetes 这个 Finalizer。现在我尝试去删除这个 configmap:

kubectl delete configmap/mymap &
configmap "mymap" deleted
jobs
[1]+  Running kubectl delete configmap/mymap

Kubernetes 会返回一个信息告诉你这个 configmap 已经被删除了,然而并不是传统意义上的删除,而是处于 deleting 的状态。当我们再次执行 get 操作去获取该 configmap,会发现 configmap 资源已经被修改过了,deletionTimeStamp 设置了值。

kubectl get configmap/mymap -o yaml
apiVersion: v1
kind: ConfigMap
metadata:
  creationTimestamp: "2020-10-22T21:30:18Z"
  deletionGracePeriodSeconds: 0
  deletionTimestamp: "2020-10-22T21:30:34Z"
  finalizers:
  - kubernetes
  name: mymap
  namespace: default
  resourceVersion: "311456"
  selfLink: /api/v1/namespaces/default/configmaps/mymap
  uid: 93a37fed-23e3-45e8-b6ee-b2521db81638

换句话说,这个 configmap 资源并没有被删除而是被更新了,这是因为 Kubernetes 看到资源中有一个 Finalizer 后把它置为了 read-only 状态。这个 configmap 只有在移除了 Finalizer 后,才会真正从集群中删除。

我们使用 patch 命令移除 Finalizer 来验证一下上面的说法。

kubectl patch configmap/mymap \
    --type json \
    --patch='[ { "op": "remove", "path": "/metadata/finalizers" } ]'
configmap/mymap patched
[1]+  Done  kubectl delete configmap/mymap

此时我们再去 get configmap,发现已经获取不到了。

kubectl get configmap/mymap -o yaml
Error from server (NotFound): configmaps "mymap" not found
76b4704f1a8c505f288921d84ec3a5b4.webpstate diagram for finalize

上面展示了当一个资源带有 Finalizer 时,执行删除操作后的状态流转图。

Owner References

Owner reference 描述了多种资源之间的关联关系。它们是关于资源的属性,可以彼此指定关联关系,因此可以做到级联删除。

Kubernetes 中最常见的具备 owner reference 关系的场景就是 pod 将 replica set 作为自己的 owner,所以当 deployment 或者 statefulSet 删除的时候,作为子资源的 replica set 和 pod 都将被删除。

下面的例子解释了 owner reference 的工作原理。我们首先创建了一个父资源,然后创建子资源的时候指定其 owner reference:

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 get configmap
NAME           DATA   AGE
mymap-child    0      12m4s
mymap-parent   0      12m4s

kubectl delete configmap/mymap-child
configmap "mymap-child" deleted

kubectl get configmap
NAME           DATA   AGE
mymap-parent   0      12m10s

我们再根据上面的 yaml 建立资源,然后我们删除父资源,这时发现所有的资源都被删除了:

kubectl get configmap
NAME           DATA   AGE
mymap-child    0      10m2s
mymap-parent   0      10m2s

kubectl delete configmap/mymap-parent
configmap "mymap-parent" deleted

kubectl get configmap
No resources found in default namespace.

简而言之,当父 - 子资源之间存在 owner reference 关系的时候,我们删除父资源,子资源也会随之删除,这叫做 cascade (级联删除)。默认情况下,cascade 的值是 true,你可以执行 kubectl delete 的时候加上参数 --cascade=false ,这样就可以只删除父资源而保留子资源。

在以下示例中,有一对父子资源,如果我使用 --cascade = false 删除父资源,但子资源仍然存在:

kubectl get configmap
NAME           DATA   AGE
mymap-child    0      13m8s
mymap-parent   0      13m8s

kubectl delete --cascade=false configmap/mymap-parent
configmap "mymap-parent" deleted

kubectl get configmap
NAME          DATA   AGE
mymap-child   0      13m21s

——cascade 参数能够控制 API 中的删除传播策略( propagation policy),它允许控制删除对象的顺序。在下面的示例中,使用 API 访问创建一个带有 background 传播策略的自定义 delete API 调用:

kubectl proxy --port=8080 &
Starting to serve on 127.0.0.1:8080

curl -X DELETE \
  localhost:8080/api/v1/namespaces/default/configmaps/mymap-parent \
  -d '{ "kind":"DeleteOptions", "apiVersion":"v1", "propagationPolicy":"Background" }' \
  -H "Content-Type: application/json"
{
  "kind""Status",
  "apiVersion""v1",
  "metadata": {},
  "status""Success",
  "details": { ... }
}

要注意,不能使用 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 :

cat <<EOF | curl -X PUT \
  localhost:8080/api/v1/namespaces/test/finalize \
  -H "Content-Type: application/json" \
  --data-binary @-
{
  "kind": "Namespace",
  "apiVersion": "v1",
  "metadata": {
    "name": "test"
  },
  "spec": {
    "finalizers": null
  }
}
EOF

这个操作要谨慎,因为它可能只删除名称空间,而将孤立资源留在现在不存在的名称空间中,会让集群处于让人很迷惑的状态。如果发生这种情况,可以手动重新创建该名称空间,有孤立的资源对象将重新出现在刚刚创建的名称空间下,可以手动清理和恢复。

Key Takeaways

上面的例子说明,Finalizer 能够阻止 Kubernetes 删除资源,但是通常在代码中添加 Finalizer 是有原因的,因此应该在手动删除它之前进行检查。Owner reference 支持级联删除资源,但 Finalizer 的优先级更高。最后,可以使用传播策略通过自定义 API 调用指定删除顺序,从而控制对象的删除方式。通过上面的例子,相信大家对 Kubernetes 中的删除工作原理有了更多的了解,现在可以使用测试集群自己尝试一下。

原文地址:https://kubernetes.io/blog/2021/05/14/using-finalizers-to-control-deletion/



官方资讯*最新技术*独家解读


浏览 44
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报