K8s GC設計原則
Warning:設計文檔的對應的 k8s 版本為1.7
Q: What is GC of Kuernetes ?
GC 是 Garbage Collector 的簡稱。從功能層面上來說,它和編程語言當中的「GC」 基本上是一樣的。它清理 Kubernetes 中「符合特定條件」的 Resource Object。(在 k8s 中,你可以認為萬物皆資源,很多邏輯的操作對象都是 Resource Object。)
Q: What are dependent mechanisms to clear needless resource objects?
Kubernetes 在不同的 Resource Objects 中維護一定的「從屬關系」。內置的 Resource Objects 一般會默認在一個 Resource Object 和它的創建者之間建立一個「從屬關系」。
當然,你也可以利用 ObjectMeta.OwnerReferences 自由的去給兩個 Resource Object 建立關系,前提是被建立關系的兩個對象必須在一個 Namespace 下。
1 // OwnerReference contains enough information to let you identify an owning 2 // object. Currently, an owning object must be in the same namespace, so there 3 // is no namespace field. 4 type OwnerReference struct { 5 // API version of the referent. 6 APIVersion string `json:"apiVersion" protobuf:"bytes,5,opt,name=apiVersion"` 7 // Kind of the referent. 8 // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds 9 Kind string `json:"kind" protobuf:"bytes,1,opt,name=kind"` 10 // Name of the referent. 11 // More info: http://kubernetes.io/docs/user-guide/identifiers#names 12 Name string `json:"name" protobuf:"bytes,3,opt,name=name"` 13 // UID of the referent. 14 // More info: http://kubernetes.io/docs/user-guide/identifiers#uids 15 UID types.UID `json:"uid" protobuf:"bytes,4,opt,name=uid,casttype=k8s.io/apimachinery/pkg/types.UID"` 16 // If true, this reference points to the managing controller. 17 // +optional 18 Controller *bool `json:"controller,omitempty" protobuf:"varint,6,opt,name=controller"` 19 // If true, AND if the owner has the "foregroundDeletion" finalizer, then 20 // the owner cannot be deleted from the key-value store until this 21 // reference is removed. 22 // Defaults to false. 23 // To set this field, a user needs "delete" permission of the owner, 24 // otherwise 422 (Unprocessable Entity) will be returned. 25 // +optional 26 BlockOwnerDeletion *bool `json:"blockOwnerDeletion,omitempty" protobuf:"varint,7,opt,name=blockOwnerDeletion"` 27 }
OwnerReference
一般存在于某一個 Resource Object 信息中的 metadata 部分。
OwnerReference 中的字段可以唯一的確定 k8s 中的一個 Resource Object。兩個 Object 可以通過這種方式建立一個 owner-dependent 的關系。
K8s 實現了一種「Cascading deletion」(級聯刪除)的機制,它利用已經建立的「從屬關系」進行資源對象的清理工作。例如,當一個 dependent 資源的 owner 已經被刪除或者不存在的時候,從某種角度就可以判定,這個 dependent 的對象已經是異常(無人管轄)的了,需要進行清理。而 「cascading deletion」則是被 k8s 中的一個 controller 組件實現的: Garbage Collector
所以,k8s 是通過 Garbage Collector 和 ownerReference 一起配合實現了「垃圾回收」的功能。
Q: What is the relationship like ?(owner-dependent)
我們可以通過一個實際的例子來了解這個「從屬關系」:
1 apiVersion: extensions/v1beta1 2 kind: ReplicaSet 3 metadata: 4 annotations: 5 deployment.kubernetes.io/desired-replicas: "2" 6 deployment.kubernetes.io/max-replicas: "3" 7 deployment.kubernetes.io/revision: "1" 8 creationTimestamp: 2018-09-07T07:11:52Z 9 generation: 1 10 labels: 11 app: coffee 12 pod-template-hash: "3866135192" 13 name: coffee-7dbb5795f6 14 namespace: default 15 ownerReferences: 16 - apiVersion: apps/v1 17 blockOwnerDeletion: true 18 controller: true 19 kind: Deployment 20 name: coffee 21 uid: 4b807ee6-b26d-11e8-b891-fa163eebca40 22 resourceVersion: "476159" 23 selfLink: /apis/extensions/v1beta1/namespaces/default/replicasets/coffee-7dbb5795f6 24 uid: 4b81e76c-b26d-11e8-b891-fa163eebca40 25 spec: 26 replicas: 2 27 ....
上面截取了一個 ReplicaSet Object 中的 metadata 的部分信息。
我們可以注意到,它的 ownerReferences 字段標識了一個 Deployment Object。我們都清楚的是,ReplicaSet 會創建一系列的 Pod。通過 spec.replicas:2 可以知道,他會創建兩個pod。
1 root@xr-service-mesh-lab:~/istio-1.0.2# kubectl get pods | grep coffee 2 coffee-7dbb5795f6-6crxz 1/1 Running 0 9d 3 coffee-7dbb5795f6-hv7tr 1/1 Running 0 5d 4 root@xr-service-mesh-lab:~/istio-1.0.2#
讓我們來觀察其中一個 Pod:
1 apiVersion: v1 2 kind: Pod 3 metadata: 4 annotations: 5 cni.projectcalico.org/podIP: 192.168.0.14/32 6 creationTimestamp: 2018-09-07T07:11:52Z 7 generateName: coffee-7dbb5795f6- 8 labels: 9 app: coffee 10 pod-template-hash: "3866135192" 11 name: coffee-7dbb5795f6-6crxz 12 namespace: default 13 ownerReferences: 14 - apiVersion: apps/v1 15 blockOwnerDeletion: true 16 controller: true 17 kind: ReplicaSet 18 name: coffee-7dbb5795f6 19 uid: 4b81e76c-b26d-11e8-b891-fa163eebca40 20 resourceVersion: "76727" 21 selfLink: /api/v1/namespaces/default/pods/coffee-7dbb5795f6-6crxz 22 uid: 4b863e4d-b26d-11e8-b891-fa163eebca40
我們可以看出,pod 中的 ownerReferences 所標識的 Object 正式我們上面看到過的 ReplicaSet。最后讓我們來檢查一下 ReplicaSet 所對應的 Deployment 的情況:
1 apiVersion: extensions/v1beta1 2 kind: Deployment 3 metadata: 4 annotations: 5 deployment.kubernetes.io/revision: "1" 6 creationTimestamp: 2018-09-07T07:11:52Z 7 generation: 1 8 labels: 9 app: coffee 10 name: coffee 11 namespace: default 12 resourceVersion: "476161" 13 selfLink: /apis/extensions/v1beta1/namespaces/default/deployments/coffee 14 uid: 4b807ee6-b26d-11e8-b891-fa163eebca40
對比一下 ReplicaSet Object 中 ownerReference 標識的 Object 可知,這個 Deployment 是 ReplicaSet 的 owner。至此,我們通過觀察三個 Object 中的 ownerReference 的信息,可以建立起如下的「從屬關系」:
- Deployment(owner)—> ReplicaSet (dependent)
- ReplicaSet (owner) —> Pod (dependent)
Q: What is the working mechanism of Garbage Collector?
一個 Garbage Collector 通常由三部分實現:
- Scanner: 它負責收集目前系統中已存在的 Resource,并且周期性的將這些資源對象放入一個隊列中,等待處理(檢測是否要對某一個Resource Object 進行 GC 操作)
- Garbage Processor: Garbage Processor 由兩部分組成
- Dirty Queue: Scanner 會將周期性掃描到的 Resource Object 放入這個隊列中等待處理
- Worker:worker 負責從這個隊列中取出元素進行處理
- 檢查 Object 的 metaData 部分,查看 ownerReference 字段是否為空
- 如果為空,則本次處理結束
- 如果不為空,檢測 ownerReference 字段內標識的 Owner Resource Object是否存在
- 存在:則本次處理結束
- 不存在:刪除這個 Object
- 檢查 Object 的 metaData 部分,查看 ownerReference 字段是否為空
其實,在有了 Scanner 和 Garbage Processor 之后,Garbage Collector 就已經能夠實現「垃圾回收」的功能了。但是有一個明顯的問題:Scanner 的掃描頻率設置多少好呢?太長了,k8s 內部就會積累過多的「廢棄資源」;太短了,尤其是在集群內部資源對象較多的時候,頻繁的拉取信息對 API-Server 也是一個不小的壓力。
k8s 作為一個分布式的服務編排系統,其內部執行任何一項邏輯或者行為,都依賴一種機制:「事件驅動」。說的簡單點,k8s 中一些看起來「自動」的行為,其實都是由一些神秘的「力量」在驅動著。而這個「力量」就是我們所說的「Event」。任意一個 Resource Object 發生變動的時候(新建,更新,刪除),都會觸發一個 k8s 的事件(Event),這個事件在 k8s 的內部是公開的,也就是說,我們可以在任意一個地方監聽這些事件。
總的來說,無論是「事件的監聽機制」還是「周期性訪問 API-Server 批量獲取 Resource Object 信息」,其目的都是為了能夠掌握 Resource Object 的最新信息。兩者是各有優勢的:
- 批量拉取:一次性拉取所有的 Resource Object,全面
- 監聽 Resource 的 Event:實時性強, 且對 API—SERVER 不會造成太大的壓力
綜上所述,在實現 Garbage Collector 的過程中,k8s 向其添加了一個「增強型」的組件:Propagator
- Propagator: Propagator 由三個部分構成
- EventQueue:負責存儲 k8s 中資源對象的事件(Eg:ADD,UPDATE,DELETE)
- DAG(有向無環圖):負責存儲 k8s 中所有資源對象的「owner-dependent」 關系
- Worker:從 EventQueue 中,取出資源對象的事件,根據事件的類型會采取以下兩種操作
- ADD/UPDATE: 將該事件對應的資源對象加入 DAG,且如果該對象有 owner 且 owner 不在 DAG 中,將它同時加入 Garbage Processor 的 Dirty Queue 中
- DELETE:將該事件對應的資源對象從 DAG 中刪除,并且將其「管轄」的對象(只向下尋找一級,如刪除 Deployment,那么只操作 ReplicaSet )加入 Garbage Processor 的 Dirty Queue 中
在有了 Propagator 的加入之后,我們完全可以僅在 GC 開始運行的時候,讓 Scanner 掃描一下系統中所有的 Object,然后將這些信息傳遞給 Propagator 和 Dirty Queue。只要 DAG 一建立起來之后,那么 Scanner 其實就沒有再工作的必要了。「事件驅動」的機制提供了一種增量的方式讓 GC 來監控 k8s 集群內部的資源對象變化情況。
Q: How can I delete the owner and reserve dependents ?
沒錯,需求就是這么奇怪,k8s 還兼容一種情況:刪除 owner,留下 dependents。剩余的 dependents 被稱為是「orphan」
你想怎么實現?
如果暫時先不看設計文檔中關于這部分的內容,根據之前對 k8s GC 的了解,讓你來實現這個功能,你會怎么做呢?這里給出一下筆者的想法:
首先,我們先來根據上面對于 GC 的了解,給出一幅大致架構圖:
在上圖中,我用三種顏色分別標記了三條較為重要的處理過程:
- 紅色:worker 從 dirtyQueue 中取出資源對象,檢查其是否帶有 owner ,如果沒帶,則不處理。否則檢測其 owner是否存在,存在,則處理下一個資源對象,不存在,刪除這個 object。
- 綠色: scanner 從 api-server 中掃描存在于 k8s 集群中的資源對象并加入至 dirtyQueue
- 粉色:propagator.worker 從 eventQueue 中取出相應的事件并且獲得對應的資源對象,根據事件的類型以及相應資源對象所屬 owner 對象的情況來進行判定,是否要進行兩個操作:
- 從 DAG 中刪除相應節點(多為響應 DELETE 事件的邏輯)
- 將有級聯關系但是 owner 不存在的對象送入 diryQueue 中
其中紅色是「數據處理」過程,而綠色和粉色是「數據收集」的過程。在「數據處理」的過程中(即我們上面分析過的 GC 的 Worker 的工作過程),worker 做的較為重要的工作有兩步:
- 檢查資源對象信息的「ownerReference」字段,判斷其是否處在一個級聯關系中
- 若資源對象有所屬 owner 且不存在,則刪除這個對象
此時,回頭看下我們的需求:「owner 刪除,dependents 留下」。如果想在「數據處理」這條鏈路上做些修改達到我們目的的話,唯一可行的辦法就是:在刪除了 dependents 對應的 owner 對象之后,同時刪除 dependents 信息中 「ownerReference」字段和對應的值。這樣一來,在檢測資源對象是否應該被刪除的過程就會因為其沒有「ownerReference」字段而放過它,最終實現了 dependents 對象的“孤立”。
k8s 是怎么實現的?
如果你了解 gRPC-intercepter 的工作機制,那么會加快你理解下面的內容
k8s 在系統內部實現了一種類似「刪除攔截器鏈」的機制:即在刪除某個資源對象的「刪除鏈路」上,執行一個或多個「攔截邏輯」。并且這種「攔截邏輯」可以自主實現,然后像插件一樣注入到這個刪除鏈路上。這種機制在 k8s 當中統稱為: Finalizers 。
Finalizers 的聲明非常簡單,就是一個 []string 。這個 Slice 的內部填充的是要執行攔截器的名稱。它存在于任何一個資源對象的 Meta 信息中: apimachinery/types.go at master · kubernetes/apimachinery · GitHub。
Finalizers
中的攔截器在其宿主資源對象觸發刪除操作之后順序執行(資源對象的deletionTimestamp不為 nil),每執行完一個,就會從 Finalizers 中移除一個,直到 Finalizers 為空的 Slice,其宿主資源對象才可以被真正的刪除。
于「刪除 owner 但是不刪除 dependents」 的需求,k8s 則是實現了一個:orphan finalizer。一般情況下,正常利用 GC 級連刪除一個資源對象是不會涉及到 orphan finalizer 的。它執行的是我們之前提到的 GC 的工作邏輯。如果你想啟用這個特性,就需要在刪除資源對象的時候,根據 K8s 版本的不同,將名為 DeleteOption.OrphanDependents 的參數賦值為 True(1.7版本以前)
或者將 DeleteOption.PropagationPolicy 參數賦值為 metav1.DeletePropagationOrphan :apimachinery/types.go at 9dc1de72c0f3996657ffc88895f89f3844d8cf01 · kubernetes/apimachinery · GitHub。
通過這個參數的注釋也可以看出:如果設置好之后,將會在它的 Finalizers 中加入 orphan finalizer。而加入 orphan finalizer 這部分的邏輯是在 api-server 的 package 中:apiserver/store.go at master · kubernetes/apiserver · GitHub
加入了 orphan finalizer 之后,在 GC 的 worker 從 dirtyQueue 中取出 owner 資源對象進行處理的時候,就會執行它的邏輯:刪除 dependents 的 OwnerReference 部分: kubernetes/garbagecollector.go at 0972ce1accf859b73abb5a68c0adf4174245d4bf · kubernetes/kubernetes · GitHub。最終,在「保留 dependents」 的邏輯完成之后,orphan finalizer 也會從相應資源對象的 Finalizers 中刪除。
一個隱含的 Race 問題
對于 Controller 來說,它會周期性的通過 Selector 來尋找它所創建的資源。
如果在篩選到了符合自己 label 的 資源,但是發現它的 Meta.OwnerReference 字段中沒有自己相關的信息的時候,就會執行一個Adoption 的操作,也就是將和自己有關的 OwnerReference 信息注入到這個 Pod 的 Meta 部分。這種邏輯雖然看起來是比較「保險」,但是實際上它和 orphan finalizer 的邏輯是有沖突的。前者是對 dependents 增加 OwnerReference 信息, 后者則是刪除它。兩個邏輯在執行的時候,如果不保證「互斥」的話,很可能就會出現一個很嚴重的競爭問題:指定了 orphan finalizer 的 對象,其 dependents 最終也會被刪除。
借鑒操作系統對于「競爭」問題的處理方式,對 OwnerReference操作的的邏輯(即臨界區),應該被「互斥」機制保護起來。而在 k8s 中,實現這種互斥保護機制的方式也很簡單:Controller 在想執行 Adoption 操作之前,會檢查一下當前資源對象的meta.DeletionTimestamp。如果這個字段的值為非 nil,那么就證明這個資源對象已經在被刪除中了。所以就不會再繼續執行 Adoption 操作。
但是仔細想一下,這種「互斥」保護機制的實現方式,看起來是借助了一個「鎖變量」(meta.DeletionTimestamp)的幫助。不過,我們并不需要擔心這個字段的「競爭」問題,因為能修改它的操作,只有「刪除」操作,而刪除操作是肯定會發生在 orphan finalizer 執行之前的。也就是說,當 orphan finalizer 執行的時候,這個值早就被設置進去了。 kubernetes/replica_set.go at 7f23a743e8c23ac6489340bbb34fa6f1d392db9d · kubernetes/kubernetes · GitHub
Q: How Kubernetes defines delete operation of resource object?
K8s 在對資源對象「刪除」操作的定義上,思考了一個較為重要的問題:「刪除」操作真正完成的標志是什么?(達到什么樣的條件才可以通知用戶「刪除」操作成功)。這個問題出現的源頭是在用戶側,當用戶在使用 k8s 提供的資源對象的「刪除」操作時,有個問題會影響到他們:
- 「刪除」操作成功多久后才可以在同一個 ns 下創建同名資源對象?
如果你了解過構建一個 k8s 集群所需要的服務組件,就可以很清楚的知道:k8s 中的資源對象的信息都是存于一個key-value 的數據庫當中的(etcd),且是以名字來做索引的。
通過命令行 kubectl get xxx 查詢的資源對象的信息都來自于那。而且,當 kubelet 組件刪除掉其所在節點上的一些資源的時候,會調用 API-Server 提供的接口刪除掉key-value 數據庫中相應的記錄。所以,在 k8s 中,給「刪除」操作下了這樣一個定義:
在沒有 orphanFinalizer 參與的前提下,直到被刪除對象及其「管轄」對象的信息在 key-value 數據庫中都被清除,才認為該對象真正的被 GC 回收。即達到了返回給用戶「刪除成功」的標準。
本質上來說,上述所表示的刪除操作是「同步」的。因為有「級聯關系」(owner-dependent) 關系的存在,刪除一個資源對象往往影響的不是他自己,還有他的 dependents。只有將因它出現的所有資源都刪除,才可以認為這個對象被刪除了。
若想指定這種同步的刪除模式,需要在兩個不同的位置設置兩個參數:
- dependents 對象 meta 信息中 OwnerReference.BlockOwnerDeletion
- 在發送刪除對象請求時,設置 DeleteOptions.PropagationPolicy
OwnerReference.BlockOwnerDeletion
參數大多數情況下在相應的 dependents 對象創建的時候就設置進去了。
如果想在 dependents 對象創建之后更新這個參數的值,可能需要使用 admission controller (1.7及以上版本)提供的一些權限相關的功能。
DeleteOptions.PropagationPolicy
一共有3個候選值:
1 // DeletionPropagation decides if a deletion will propagate to the dependents of 2 // the object, and how the garbage collector will handle the propagation. 3 type DeletionPropagation string 4 5 const ( 6 // Orphans the dependents. 7 DeletePropagationOrphan DeletionPropagation = "Orphan" 8 // Deletes the object from the key-value store, the garbage collector will 9 // delete the dependents in the background. 10 DeletePropagationBackground DeletionPropagation = "Background" 11 // The object exists in the key-value store until the garbage collector 12 // deletes all the dependents whose ownerReference.blockOwnerDeletion=true 13 // from the key-value store. API sever will put the "foregroundDeletion" 14 // finalizer on the object, and sets its deletionTimestamp. This policy is 15 // cascading, i.e., the dependents will be deleted with Foreground. 16 DeletePropagationForeground DeletionPropagation = "Foreground" 17 )
再結合 OwnerReference.BlockOwnerDeletion 參數的注釋
1 // If true, AND if the owner has the "foregroundDeletion" finalizer, then 2 // the owner cannot be deleted from the key-value store until this 3 // reference is removed. 4 // Defaults to false. 5 // To set this field, a user needs "delete" permission of the owner, 6 // otherwise 422 (Unprocessable Entity) will be returned. 7 // +optional 8 BlockOwnerDeletion *bool `json:"blockOwnerDeletion,omitempty" protobuf:"varint,7,opt,name=blockOwnerDeletion"`
我們可以了解到。同步刪除的開啟方式如下:
- DeleteOptions.PropagationPolicy = DeletePropagationForeground
- OwnerReference. BlockOwnerDeletion = True
開啟之后,在刪除身份為 owner 的資源對象的時候,就會先將 denpendents 對象中 OwnerReference.BlockOwnerDeletion 為 true 的資源對象先刪除,然后再刪除 owner 身份的對象。這里的「刪除」就指的是我們前面說過的「真正的刪除」:從 k8s 存儲資源對象信息的 key-value 數據庫中刪除所有與其相關的信息。需要注意的是, OwnerReference.BlockOwnerDeletion 為 false 的dependent 對象不會阻礙 owner 對象的刪除操作。
Foreground 是 k8s 提供的兩種級聯刪除方式其中之一,另外一種為 Background。通過上面相關的注釋可以看到 Foreground 級聯刪除也是通過 Finalizer 來實現的,查看 Finalizer 相關的定義可知,標準的 Finalizer,一個是 orphan 的,另一個就是Foreground的:
1 // These are internal finalizer values for Kubernetes-like APIs, must be qualified name unless defined here 2 const ( 3 FinalizerOrphanDependents string = "orphan" 4 FinalizerDeleteDependents string = "foregroundDeletion" 5 )
API-Server 的 Delete 函數,在接受到刪除請求的時候,會檢查 DeleteOptions.PropagationPolicy 參數,若其值為 DeletePropagationForeground , API-Server 隨即會對該資源對象進行 Update 操作:
- 插入 FinalizerDeleteDependents Finalizer
- 設置 ObjectMeta.DeletionTimestamp 為當前值
然后,在 GC 處理 owner 對象的 Update 事件的邏輯中,還會給 owner 對象打上一個「正在刪除 dependents」 對象的標簽。之后,我們會將 owner 對象管轄的 dependent 對象和他自己都加入到 dirtyQueue。dirtyQueue 的 worker 在處理 owner 對象的時候,會檢查 owner 對象 「正在刪除 dependents」的標簽是否存在,如果仍有 dependent 對象沒有被刪掉,owner 會被輪詢處理。而 dependent 對象將會被正常刪除。當 dependent 對象相應的刪除事件被 Propagator 感知到后,會將其從 DAG 和其 owner 的 dependents 信息中刪除。幾個循環之后,dependents 機會被刪光,而 owner 對象中的 finalizer 和自身也會隨之被刪掉。
Background 模式的級聯刪除不會因 dependent 對象而影響 owner 對象的刪除操作。當我們發送給 API-Server 刪除一個 owner 身份的對象的請求之后,這個資源對象會立即被刪除。它「管轄」的 dependent 對象會以「靜默」的方式刪除。
Q: What problems GC handles in k8s?
通過對 k8s GC 設計文檔的閱讀,可以大致的概括一下:GC 主要是按照用戶的需求來清理系統中「異常」的資源,用戶可以自定義「清理方式」和「清理策略」。不難發現,在 GC 中,到底是保留一個資源還是刪除一個資源都參照了資源之間的「從屬關系」。資源的「從屬關系」可以大致分為幾個形態:
- 無從屬關系:這部分資源基本不會被 GC 做處理
- 有從屬關系
- 不符合用戶預期:刪除異常資源
- 符合用戶預期:解綁異常資源之間的級聯關系
在有從屬關系的資源之間,即使被探測到關系異常,也并不代表一定要將他們都清除。如果有 Orphan Finalizer 的存在,可能某種「異常」正是用戶想要的。所以,這就回到了我們一開始所說到的「清理策略」問題。GC 有一定的默認的清理策略,但是用戶可以通過加入 Finalizer 的形式來修改「清理策略」,從而保持一個「符合用戶期望」的資源之間的從屬關系。
同時,用戶可以還可以通過參數來制定特定的「清理方式」,如 Foreground 或者 Background。總體上來說,GC 的行為會受到如下幾個因素的影響:
- 默認:
- 依據:資源之間默認的從屬關系
- 行為:刪除級聯關系異常的資源
- 方式:Foreground 或者 Background
- 可定制
- 依據:用戶定義的從屬關系(通過 Finalizer)
- 行為:刪除級聯關系異常的資源
- 方式:Foreground 或者 Background
目前看來,GC 主要是解決了「資源清理」 的問題。那么再抽象一點來看的話,GC 解決的是「資源管理」這個大問題中的一個關于「清理」的小問題。既然說到「資源管理」,那么肯定就不止「清理」一個問題需要處理:
其中,對于資源的創建部分,除了正常的新建操作之外,controller 還有定期執行一個「Adoption」 的操作,用來維護其創建的資源之間那些「本應該建立但是卻斷開的從屬關系」。而對于更新操作來說,controller_manager 需要處理用戶對于資源的擴縮容請求,如將 deployment.replicaset.replicacount 減少或者增大,相應資源對應的 controller 需要對可見資源的數量進行調整。至于「資源超賣」的問題,一定會涉及到 scheduler。因為物理資源是固定的,「超賣」本質上來說就是按照實時的需求,動態的調整服務所在的 Node,以便恰好滿足服務對于資源的需求。
如果不把資源管理問題討論的范圍局限在 k8s 中的話,那么「審計」和「復用」同樣也是「資源管理」問題中不得不考慮的兩個點。前者可以增加整個系統資源的「可控性」,后者則可以最大限度的提升資源的利用率,從而降低成本。其實「復用」和「超賣」的目的是一樣的,都是想最大限度的利用物理資源。不過這兩個功能筆者暫時還沒有去查看它們是否在 k8s 已經實現。
總結
之所以去了解 k8s 的 GC,是因為在將 k8s 集群從1.7版本升級至1.9版本的過程中,因為我錯誤的設置了資源之間的從屬關系,導致該資源被 GC 給回收掉了。問題在1.7版本沒有出現的原因是那時 k8s 還沒有支持對自定義資源(CRD)的級聯刪除。通過對 GC 設計理念的了解,我們可以初步的感受到 k8s 對于「資源管理」這個問題域中「資源清理」這個小問題的解決思路。以此為起點,我們可以順藤摸瓜,去觀察 k8s 對于「資源管理」問題域中的其他難題是如何處理的。后續,我也將會根據設計文檔中的思路,在代碼級別上去了解 GC 的實現細節,從而貢獻出更加詳細的 blog。