Kubernetes 中的垃圾回收

設想這麼一個場景:咱們在 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):在級聯刪除中,全部者被刪除,那集羣中的從屬對象也會被刪除。
  • 孤兒(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 元數據設置爲默認值。以後垃圾回收器會肯定孤兒對象並將其刪除。

垃圾回收器如何工做?

若是對象的 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://mp.weixin.qq.com/s/zhygwHbdK1h7sViHWN93hw

相關文章
相關標籤/搜索