Kubernetes 中的垃圾回收
在重新設置 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
推薦閱讀:
在看點一下