在重新設置 Deployment 後,舊的副本集以及 Pod 是怎麼被刪除的呢?本文講述了 K8s 中的垃圾回收機制,簡單介紹了垃圾回收的概念、策略以及實現方法,希望能幫助大家進一步瞭解垃圾回收。

作者:Abhishek

翻譯:Bach(才雲)

校對: bot(才雲)、星空下的文仔(才雲)

設想這麼一個場景:我們在 K8s 上創建了一個對象,它根據需要生成副本集和 Pod。在檢查時,我們遺漏了容器某個屬性的設置,因此又重新編輯了  Deployment 。新的 Deployment 就產生了新的副本集對象和新的 Pod。這裏就出現了一個問題,舊的副本集和 Pop 去哪了?另外,如果直接刪除 Deployment,那副本集和 Pod 又會如何?事實就是,在刪除 Deployment 後,副本集和 Pod 也會一起被刪除,要不然集羣早就亂套了。

在這個場景之下,我們可以深入思考幾個問題: 在 K8s 中該如何實現級聯刪除?有幾種級聯刪除策略?在 K8s 中有沒有可能存在孤兒對象 (orphan object) 這些問題其實就是典型的垃圾回收(garbage collection,GC)問題。本文將介紹 K8s 中垃圾回收的概念以及實現方法。

K8sMeetup

什麼是垃圾回收?

一般來說, 垃圾回收(GC)就是從系統中刪除未使用的對象,並釋放分配給它們的計算資源。 GC 存在於所有的高級編程語言中,較低級的編程語言通過系統庫實現 GC。

GC 最常見的算法之一是 mark-and-sweep,這個算法會標記將刪除的對象,再進行刪除,如下圖所示:

K8sMeetup

OwnerRefernce

在面向對象的語言中,一些對象會引用其他對象或者直接由其他對象組成,k8s 也有類似形式,例如副本集管理一組 Pod,而 Deployment 又管理着副本集。

但與面嚮對象語言不同的是,在 K8s 對象的定義中,沒有明確所有者之間的關係,那麼系統要如何確定它們的關係呢?其實,在 K8s 中,每個從屬對象都具有 唯一數據字段名稱  metadata.ownerReferences  用於確定關係。

從 Kubernetes v1.8 開始,K8s 對於 ReplicaSet、StatefulSet、DaemonSet、Deployment、Job、 CronJob 等創建或管理的對象,會自動爲其設置 ownerReferences 的值。如果有需要,我們還可以手動設置 ownerReferences。

以下內容顯示了 core-dns Deployment 上  metadata.ownerReferences  的值。

k get deployment -n kube-system -o wide

NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES

coredns 2/2 2 2 44d coredns k8s.gcr.io/coredns:1.6.7



k get rs -n kube-system -o json | jq ".items[0].metadata.name, .items[0].metadata.ownerReferences"

"coredns-66bff467f8"

[

{

"apiVersion": "apps/v1",

"blockOwnerDeletion": true,

"controller": true,

"kind": "Deployment",

"name": "coredns",

"uid": "d8f29b78-439c-497e-9a45-7c33bd626a9f"

}

]


k get pods coredns-66bff467f8-rsnmg -n kube-system -o json | jq ".metadata.name, .metadata.ownerReferences"

"coredns-66bff467f8-rsnmg"

[

{

"apiVersion": "apps/v1",

"blockOwnerDeletion": true,

"controller": true,

"kind": "ReplicaSet",

"name": "coredns-66bff467f8",

"uid": "085d5398-1358-43e2-918e-2e03da18c7bd"

}

]

認真觀察上述命令的輸出,其實它和其他對象 GC 之間是有些許差別的。對象關聯參考金字塔是顛倒的:

K8sMeetup

K8s 的垃圾回收策略

如前面所講,在 Kubernetes v1.8 之前,依賴對象邏輯刪除的實現是在客戶端,對於某些資源而言則是在控制器端。有時,客戶端會中途失敗,導致集羣狀態混亂,需要手動清理。後來爲了解決這個問題,K8s 社區引入並實現了 Garbage Collector Controller(垃圾回收器) ,用更好用且更簡單的方式實現 GC。在 K8s 中,有兩大類 GC:

  • 級聯(Cascading) :在級聯刪除中,所有者被刪除,那集羣中的從屬對象也會被刪除。

  • 孤兒(Orphan) :這種情況下,對所有者的進行刪除只會將其從集羣中刪除,並使所有對象處於“孤兒”狀態。

級聯刪除

在級聯刪除(cascading deletion strategy)中,從屬對象(dependent object)與所有者對象(owner object)會被一起刪除。在級聯刪除中,又有兩種模式: 前臺(foreground) 後臺(background)

前臺級聯刪除(Foreground Cascading Deletion) :在這種刪除策略中,所有者對象的刪除將會持續到其所有從屬對象都被刪除爲止。當所有者被刪除時,會進入“正在刪除”(deletion in progress)狀態,此時:

  • 對象仍然可以通過 REST API 查詢到(可通過 kubectl 或 kuboard 查詢到)

  • 對象的 deletionTimestamp 字段被設置

  • 對象的 metadata.finalizers 包含值 foregroundDeletion

一旦對象被設置爲 “正在刪除” 狀態,垃圾回收器將刪除其從屬對象。當垃圾回收器已經刪除了所有的“blocking”從屬對象(ownerReference.blockOwnerDeletion=true 的對象)以後,將刪除所有者對象。

後臺級聯刪除(Background Cascading Deletion) :這種刪除策略會簡單很多,它會立即刪除所有者的對象,並由垃圾回收器在後臺刪除其從屬對象。這種方式比前臺級聯刪除快的多,因爲不用等待時間來刪除從屬對象。

孤兒刪除

孤兒刪除策略(orphan deletion strategy) 中,會直接刪除所有者對象,並將從屬對象中的 ownerReference 元數據設置爲默認值。之後垃圾回收器會確定孤兒對象並將其刪除。

K8sMeetup

垃圾回收器如何工作?

如果對象的 OwnerReferences 元數據中沒有任何所有者對象,那麼垃圾回收器會刪除該對象。 垃圾回收器由 Scanner、Garbage Processor 和 Propagator 組成

Scanner :它會檢測 K8s 集羣中支持的所有資源,並通過控制循環週期性地檢測。它會掃描系統中的所有資源,並將每個對象添加到"髒隊列"(dirty queue)中。

Garbage Processor :它由在"髒隊列"上工作的 worker 組成。每個 worker 都會從"髒隊列"中取出對象,並檢查該對象裏的 OwnerReference 字段是否爲空。如果爲空,那就從“髒隊列”中取出下一個對象進行處理;如果不爲空,它會檢測 OwnerReference 字段內的 owner resoure object 是否存在,如果不存在,會請求 API 服務器刪除該對象。

Propagator :用於優化垃圾回收器,它包含以下三個組件:

  • EventQueue :負責存儲 k8s 中資源對象的事件

  • DAG(有向無環圖) :負責存儲 k8s 中所有資源對象的 owner-dependent 關係

  • Worker :從 EventQueue 中取出資源對象的事件,並根據事件的類型會採取操作

在有了 Propagator 的加入之後,我們完全可以僅在 GC 開始運行的時候,讓 Scanner 掃描系統中所有的對象,然後將這些信息傳遞給 Propagator 和“髒隊列”。只要 DAG 一建立起來之後,那麼 Scanner 其實就沒有再工作的必要了。

總體而言,K8s 中 GC 的實現是非常通用且非常有效,希望這篇文章可以幫助大家更加了解 K8s 中的 GC。

https://sparsecode.io/garbage-collection-in-k8s.html

推薦閱讀:

在看點一下

相關文章