設想這麼一個場景:咱們在 K8s 上建立了一個對象,它根據須要生成副本集和 Pod。在檢查時,咱們遺漏了容器某個屬性的設置,所以又從新編輯了 Deployment。新的 Deployment 就產生了新的副本集對象和新的 Pod。這裏就出現了一個問題,舊的副本集和 Pop 去哪了?另外,若是直接刪除 Deployment,那副本集和 Pod 又會如何?事實就是,在刪除 Deployment 後,副本集和 Pod 也會一塊兒被刪除,要否則集羣早就亂套了。算法
在這個場景之下,咱們能夠深刻思考幾個問題:在 K8s 中該如何實現級聯刪除?有幾種級聯刪除策略?在 K8s 中有沒有可能存在孤兒對象(orphan object)?這些問題其實就是典型的垃圾回收(garbage collection,GC)問題。本文將介紹 K8s 中垃圾回收的概念以及實現方法。編程
什麼是垃圾回收?json
通常來講,垃圾回收(GC)就是從系統中刪除未使用的對象,並釋放分配給它們的計算資源。GC 存在於全部的高級編程語言中,較低級的編程語言經過系統庫實現 GC。 api
GC 最多見的算法之一是 mark-and-sweep,這個算法會標記將刪除的對象,再進行刪除,以下圖所示:服務器
OwnerRefernceapp
在面向對象的語言中,一些對象會引用其餘對象或者直接由其餘對象組成,k8s 也有相似形式,例如副本集管理一組 Pod,而 Deployment 又管理着副本集。編程語言
但與面嚮對象語言不一樣的是,在 K8s 對象的定義中,沒有明確全部者之間的關係,那麼系統要如何肯定它們的關係呢?其實,在 K8s 中,每一個從屬對象都具備 惟一數據字段名稱 metadata.ownerReferences
用於肯定關係。ide
從 Kubernetes v1.8 開始,K8s 對於 ReplicaSet、StatefulSet、DaemonSet、Deployment、Job、 CronJob 等建立或管理的對象,會自動爲其設置 ownerReferences 的值。若是有須要,咱們還能夠手動設置 ownerReferences。優化
如下內容顯示了 core-dns Deployment 上 metadata.ownerReferences
的值。ui
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 之間是有些許差異的。對象關聯參考金字塔是顛倒的:
K8s 的垃圾回收策略
如前面所講,在 Kubernetes v1.8 以前,依賴對象邏輯刪除的實現是在客戶端,對於某些資源而言則是在控制器端。有時,客戶端會中途失敗,致使集羣狀態混亂,須要手動清理。後來爲了解決這個問題,K8s 社區引入並實現了 Garbage Collector Controller(垃圾回收器),用更好用且更簡單的方式實現 GC。在 K8s 中,有兩大類 GC:
級聯刪除
在級聯刪除(cascading deletion strategy)中,從屬對象(dependent object)與全部者對象(owner object)會被一塊兒刪除。在級聯刪除中,又有兩種模式:前臺(foreground)和後臺(background)。
前臺級聯刪除(Foreground Cascading Deletion):在這種刪除策略中,全部者對象的刪除將會持續到其全部從屬對象都被刪除爲止。當全部者被刪除時,會進入「正在刪除」(deletion in progress)狀態,此時:
一旦對象被設置爲 「正在刪除」 狀態,垃圾回收器將刪除其從屬對象。當垃圾回收器已經刪除了全部的「blocking」從屬對象(ownerReference.blockOwnerDeletion=true 的對象)之後,將刪除全部者對象。
後臺級聯刪除(Background Cascading Deletion):這種刪除策略會簡單不少,它會當即刪除全部者的對象,並由垃圾回收器在後臺刪除其從屬對象。這種方式比前臺級聯刪除快的多,由於不用等待時間來刪除從屬對象。
孤兒刪除
在孤兒刪除策略(orphan deletion strategy)中,會直接刪除全部者對象,並將從屬對象中的 ownerReference 元數據設置爲默認值。以後垃圾回收器會肯定孤兒對象並將其刪除。
垃圾回收器如何工做?
若是對象的 OwnerReferences 元數據中沒有任何全部者對象,那麼垃圾回收器會刪除該對象。垃圾回收器由 Scanner、Garbage Processor 和 Propagator 組成:
Scanner:它會檢測 K8s 集羣中支持的全部資源,並經過控制循環週期性地檢測。它會掃描系統中的全部資源,並將每一個對象添加到"髒隊列"(dirty queue)中。
Garbage Processor:它由在"髒隊列"上工做的 worker 組成。每一個 worker 都會從"髒隊列"中取出對象,並檢查該對象裏的 OwnerReference 字段是否爲空。若是爲空,那就從「髒隊列」中取出下一個對象進行處理;若是不爲空,它會檢測 OwnerReference 字段內的 owner resoure object 是否存在,若是不存在,會請求 API 服務器刪除該對象。
Propagator :用於優化垃圾回收器,它包含如下三個組件:
在有了 Propagator 的加入以後,咱們徹底能夠僅在 GC 開始運行的時候,讓 Scanner 掃描系統中全部的對象,而後將這些信息傳遞給 Propagator 和「髒隊列」。只要 DAG 一創建起來以後,那麼 Scanner 其實就沒有再工做的必要了。
整體而言,K8s 中 GC 的實現是很是通用且很是有效,但願這篇文章能夠幫助你們更加了解 K8s 中的 GC。