深入理解 Kubernetes Deletion

kubectl delete 這個(gè)命令我們幾乎每天都在使用,看起來很容易理解,不就是刪除 Kubernetes 的某種資源嗎?但是要完全理解 Delete 操作還是很有挑戰(zhàn)的,理解刪除操作背后真正的原理,能夠幫助你從容應(yīng)對(duì)一些奇葩場(chǎng)景。這篇文章將從以下幾個(gè)方面詳細(xì)解釋刪除操作背后的故事:
- 基本的刪除操作
- Finalizers 和 Owner Reference 會(huì)對(duì)刪除操作產(chǎn)生什么影響
- 如何利用 ?propagation policy 改變刪除的順序
- 通過 examples 演示上述刪除操作
簡單起見,所有示例都將使用 ConfigMaps 和 Basic Shell 命令來演示操作過程。
The basic delete
Kubernetes 有幾種不同的命令,您可以使用它允許您創(chuàng)建,讀取,更新和刪除對(duì)象。出于本博客文章的目的,我們將用到四個(gè) kubectl 命令:create,get,patch,和delete。
下面是最簡單的 kubectl delete 使用場(chǎng)景:
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
演示了一個(gè) configMap 從創(chuàng)建、查詢、到刪除、再查詢的過程,這個(gè)過程可以用下面的狀態(tài)圖表示:
state diagra for delete這種 delete 操作是非常簡單直觀的,但當(dāng)你遇到 Finalizer和 Owner References 的時(shí)候就會(huì)出現(xiàn)各種難以理解的現(xiàn)象。
Understanding Finalizers
在理解 Kubernetes 的資源刪除時(shí),了解 Finalizers 的工作原理能夠在你無法刪除資源時(shí)給你一些解決問題的靈感。
Finalizers 是觸發(fā) pre-delete 操作的關(guān)鍵,能夠控制資源的垃圾回收。Finalizers 設(shè)計(jì)的初衷是幫助 controller 在處理資源刪除之前,優(yōu)先處理一些 clean up 的邏輯。但是 Finalizers 并不包含代碼邏輯,使用上跟 Annotation 有些相似,很容易被添加或者刪除。
你可以已經(jīng)見過下面的 Finalizers:
kubernetes.io/pv-protection
kubernetes.io/pvc-protection
這兩個(gè) Finalizer 是用來防止誤刪除 volume 的,類似功能的 Finalizer 還有很多。
下面是一個(gè)自定義的 configmap,只包含一個(gè) Finalizer:
cat?<<EOF?|?kubectl?create?-f?-
apiVersion:?v1
kind:?ConfigMap
metadata:
??name:?mymap
??finalizers:
??-?kubernetes
EOF
負(fù)責(zé)管理 configmap 的 controller 并不知道該如何處理 kubernetes 這個(gè) Finalizer。現(xiàn)在我嘗試去刪除這個(gè) configmap:
kubectl?delete?configmap/mymap?&
configmap?"mymap"?deleted
jobs
[1]+??Running?kubectl?delete?configmap/mymap
Kubernetes 會(huì)返回一個(gè)信息告訴你這個(gè) configmap 已經(jīng)被刪除了,然而并不是傳統(tǒng)意義上的刪除,而是處于?deleting 的狀態(tài)。當(dāng)我們?cè)俅螆?zhí)行 get 操作去獲取該 configmap,會(huì)發(fā)現(xiàn) configmap 資源已經(jīng)被修改過了,deletionTimeStamp 設(shè)置了值。
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
換句話說,這個(gè) configmap 資源并沒有被刪除而是被更新了,這是因?yàn)?Kubernetes 看到資源中有一個(gè) Finalizer 后把它置為了 read-only 狀態(tài)。這個(gè) configmap 只有在移除了 Finalizer 后,才會(huì)真正從集群中刪除。
我們使用 patch 命令移除 Finalizer 來驗(yàn)證一下上面的說法。
kubectl?patch?configmap/mymap?\
????--type?json?\
????--patch='[?{?"op":?"remove",?"path":?"/metadata/finalizers"?}?]'
configmap/mymap?patched
[1]+??Done??kubectl?delete?configmap/mymap
此時(shí)我們?cè)偃?get configmap,發(fā)現(xiàn)已經(jīng)獲取不到了。
kubectl?get?configmap/mymap?-o?yaml
Error?from?server?(NotFound):?configmaps?"mymap"?not?found
state diagram for finalize上面展示了當(dāng)一個(gè)資源帶有 Finalizer 時(shí),執(zhí)行刪除操作后的狀態(tài)流轉(zhuǎn)圖。
Owner References
Owner reference 描述了多種資源之間的關(guān)聯(lián)關(guān)系。它們是關(guān)于資源的屬性,可以彼此指定關(guān)聯(lián)關(guān)系,因此可以做到級(jí)聯(lián)刪除。
Kubernetes 中最常見的具備 owner reference 關(guān)系的場(chǎng)景就是 pod 將 replica set 作為自己的 owner,所以當(dāng) deployment 或者 statefulSet 刪除的時(shí)候,作為子資源的 replica set 和 pod 都將被刪除。
下面的例子解釋了 owner reference 的工作原理。我們首先創(chuàng)建了一個(gè)父資源,然后創(chuàng)建子資源的時(shí)候指定其 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
當(dāng)刪除子資源時(shí),父資源并不會(huì)被刪除:
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
我們?cè)俑鶕?jù)上面的 yaml 建立資源,然后我們刪除父資源,這時(shí)發(fā)現(xiàn)所有的資源都被刪除了:
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.
簡而言之,當(dāng)父 - 子資源之間存在 owner reference 關(guān)系的時(shí)候,我們刪除父資源,子資源也會(huì)隨之刪除,這叫做 cascade (級(jí)聯(lián)刪除)。默認(rèn)情況下,cascade 的值是 true,你可以執(zhí)行 kubectl delete 的時(shí)候加上參數(shù) --cascade=false ,這樣就可以只刪除父資源而保留子資源。
在以下示例中,有一對(duì)父子資源,如果我使用 --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 參數(shù)能夠控制 API 中的刪除傳播策略( propagation policy),它允許控制刪除對(duì)象的順序。在下面的示例中,使用 API 訪問創(chuàng)建一個(gè)帶有 background 傳播策略的自定義 delete API 調(diào)用:
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 調(diào)用來指定。需要?jiǎng)?chuàng)建一個(gè)代理,這樣就可以從客戶端訪問 API Server,并執(zhí)行 curl 命令來執(zhí)行該刪除命令。
一共有三種傳播策略:
Foreground:先刪除子資源再刪除父資源(post-order)
Background:先刪除父資源再刪除子資源(pre-order)
Orphan:忽略 owner reference
要注意,假如一個(gè)資源中同時(shí)設(shè)置了 owner reference 和 finalizer,finalizer 的優(yōu)先級(jí)是最高的。
Forcing a Deletion of a Namespace
你可以已經(jīng)遇到過執(zhí)行 kubectl delete ns NS 后,ns 無法刪除的情況,這時(shí)候你可以通過 update 所刪除 ns 的 Finalizer 字段來實(shí)行強(qiáng)制刪除。這個(gè)操作會(huì)通知到 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
這個(gè)操作要謹(jǐn)慎,因?yàn)樗赡苤粍h除名稱空間,而將孤立資源留在現(xiàn)在不存在的名稱空間中,會(huì)讓集群處于讓人很迷惑的狀態(tài)。如果發(fā)生這種情況,可以手動(dòng)重新創(chuàng)建該名稱空間,有孤立的資源對(duì)象將重新出現(xiàn)在剛剛創(chuàng)建的名稱空間下,可以手動(dòng)清理和恢復(fù)。
Key Takeaways
上面的例子說明,Finalizer 能夠阻止 Kubernetes 刪除資源,但是通常在代碼中添加 Finalizer 是有原因的,因此應(yīng)該在手動(dòng)刪除它之前進(jìn)行檢查。Owner reference 支持級(jí)聯(lián)刪除資源,但 Finalizer 的優(yōu)先級(jí)更高。最后,可以使用傳播策略通過自定義 API 調(diào)用指定刪除順序,從而控制對(duì)象的刪除方式。通過上面的例子,相信大家對(duì) Kubernetes 中的刪除工作原理有了更多的了解,現(xiàn)在可以使用測(cè)試集群自己嘗試一下。
原文地址:https://kubernetes.io/blog/2021/05/14/using-finalizers-to-control-deletion/
官方資訊*最新技術(shù)*獨(dú)家解讀
