自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

九張圖帶你理解 Kubernetes Controller 工作機(jī)制

系統(tǒng) Linux
此文就是通過(guò)對(duì)代碼的簡(jiǎn)單分析,以及一些經(jīng)驗(yàn)總結(jié),來(lái)描述 k8s controller 管理資源的主要流程。

圖片

一、Introduction

起因:工作上要重構(gòu)一個(gè)現(xiàn)有的組件管理工具,要求實(shí)現(xiàn)全生命周期管理,還有上下游解耦,我心里一想這不就是 k8s controller 嘛!所以決定在動(dòng)手前先學(xué)習(xí)一下 k8s 的先進(jìn)理念。

此文就是通過(guò)對(duì)代碼的簡(jiǎn)單分析,以及一些經(jīng)驗(yàn)總結(jié),來(lái)描述 k8s controller 管理資源的主要流程。

二、Concepts

resource: 資源,k8s 中定義的每一個(gè)實(shí)例都是一個(gè)資源,比如一個(gè) rs、一個(gè) deployment。資源有不同的 kind,比如 rs、deployment。資源間存在上下游關(guān)系。

注意:下文中提到的所有“資源”,都是指 k8s 中抽象的資源聲明,而不是指 CPU、存儲(chǔ)等真實(shí)的物理資源。

高度抽象 k8s 的話其實(shí)就三大件:

  • apiserver: 負(fù)責(zé)存儲(chǔ)資源,并且提供查詢、修改資源的接口
  • controller: 負(fù)責(zé)管理本級(jí)和下級(jí)資源。比如 deploymentController 就負(fù)責(zé)管理 deployment 資源 和下一級(jí)的 rs 資源。
  • kubelet: 安裝在 node 節(jié)點(diǎn)上,負(fù)責(zé)部署資源

controller 和 kubelet 都只和 apiserver 通訊。controller 不斷監(jiān)聽本級(jí)資源,然后修改下級(jí)資源的聲明。kubelet 查詢當(dāng)前 node 所應(yīng)部署的資源,然后執(zhí)行清理和部署。

圖片

1、術(shù)語(yǔ)

  • ??metadata??: 每一個(gè)資源都有的元數(shù)據(jù),包括 label、owner、uid 等
  • ??UID??: 每一個(gè)被創(chuàng)建(提交給 apiserver)的資源都有一個(gè)全局唯一的 UUID。
  • ??label??: 每個(gè)資源都要定義的標(biāo)簽
  • ??selector??: 父資源通過(guò) labelSelector 查詢歸其管理的子資源。不允許指定空 selector(全匹配)。
  • owner: 子資源維護(hù)一個(gè) owner UID 的列表??OwnerReferences??, 指向其父級(jí)資源。列表中第一個(gè)有效的指針會(huì)被視為生效的父資源。selector 實(shí)際上只是一個(gè) adoption 的機(jī)制, 真實(shí)起作用的父子級(jí)關(guān)系是靠 owner 來(lái)維持的, 而且 owner 優(yōu)先級(jí)高于 selector。
  • replicas: 副本數(shù),pod 數(shù)
  • 父/子資源的相關(guān):
  • orphan: 沒(méi)有 owner 的資源(需要被 adopt 或 GC)
  • adopt: 將 orphan 納入某個(gè)資源的管理(成為其 owner)
  • match: 父子資源的 label/selector 匹配
  • release: 子資源的 label 不再匹配父資源的 selector,將其釋放
  • RS 相關(guān):
  • saturated: 飽和,意指某個(gè)資源的 replicas 已符合要求
  • surge: rs 的 replicas 不能超過(guò) spec.replicas + surge
  • proportion: 每輪 rolling 時(shí),rs 的變化量(小于 maxSurge)
  • fraction: scale 時(shí) rs 期望的變化量(可能大于 maxSurge)

三、Controller

  • sample-controller@a40ea2c/controller.go
  • kubernetes@59c0523b/pkg/controller/deployment/deployment_controller.go
  • kubernetes@59c0523b/pkg/controller/controller_ref_manager.go

控制器,負(fù)責(zé)管理自己所對(duì)應(yīng)的資源(resource),并創(chuàng)建下一級(jí)資源,拿 deployment 來(lái)說(shuō):

  1. 用戶創(chuàng)建 deployment 資源
  2. deploymentController 監(jiān)聽到 deployment 資源,然后創(chuàng)建 rs 資源
  3. rsController 監(jiān)聽到 rs 資源,然后創(chuàng)建 pod 資源
  4. 調(diào)度器(scheduler)監(jiān)聽到 pod 資源,將其與 node 資源建立關(guān)聯(lián)

(node 資源是 kubelet 安裝后上報(bào)注冊(cè)的)

圖片

理想中,每一層管理器只管理本級(jí)和子兩層資源。但因?yàn)槊恳粋€(gè)資源都是被上層創(chuàng)建的, 所以實(shí)際上每一層資源都對(duì)下層資源的定義有完全的了解,即有一個(gè)由下至上的強(qiáng)耦合關(guān)系。

比如 ??A -> B -> C -> D?? 這樣的生成鏈,A 當(dāng)然是知道 D 資源的全部定義的, 所以從理論上說(shuō),A 是可以去獲取 D 的。但是需要注意的是,如果出現(xiàn)了跨級(jí)的操作,A 也只能只讀的獲取 D,而不要對(duì) D 有任何改動(dòng), 因?yàn)榭缂?jí)修改數(shù)據(jù)的話會(huì)干擾下游的控制器。

k8s 中所有的控制器都在同一個(gè)進(jìn)程(controller-manager)中啟動(dòng), 然后以 goroutine 的形式啟動(dòng)各個(gè)不同的 controller。所有的 contorller 共享同一個(gè) informer,不過(guò)可以注冊(cè)不同的 filter 和 handler,監(jiān)聽自己負(fù)責(zé)的資源的事件。

(informer 是對(duì) apiserver 的封裝,是 controller 查詢、訂閱資源消息的組件,后文有介紹)注:如果是用戶自定義 controller(CRD)的話,需要以單獨(dú)進(jìn)程的形式啟動(dòng),需要自己另行實(shí)例化一套 informer, 不過(guò)相關(guān)代碼在 client-go 這一項(xiàng)目中都做了封裝,編寫起來(lái)并不會(huì)很復(fù)雜。

控制器的核心代碼可以概括為:

for {
for {
// 從 informer 中取出訂閱的資源消息
key, empty := queue.Get()
if empty {
break
}

defer queue.Done(key)

// 處理這一消息:更新子資源的聲明,使其匹配父資源的要求。
// 所有的 controller 中,這一函數(shù)都被命名為 `syncHandler`。
syncHandler(key)
}

// 消息隊(duì)列被消費(fèi)殆盡,等待下一輪運(yùn)行
time.sleep(time.Second)
}
  1. 通過(guò) informer(indexer)監(jiān)聽資源事件,事件的格式是字符串??<namespace>/<name>??
  2. 控制器通過(guò) namespace 和 name 去查詢自己負(fù)責(zé)的資源和下級(jí)資源
  3. 比對(duì)當(dāng)前資源聲明的狀態(tài)和下級(jí)資源可用的狀態(tài)是否匹配,并通過(guò)增刪改讓下級(jí)資源匹配上級(jí)聲明。比如 deployments 控制器就查詢 deployment 資源和 rs 資源,并檢驗(yàn)其中的 replicas 副本數(shù)是否匹配。

controller 內(nèi)包含幾個(gè)核心屬性/方法:

  • informer: sharedIndexer,用于獲取資源的消息,支持注冊(cè) Add/Update/Delete 事件觸發(fā),或者調(diào)用??lister?? 遍歷。
  • clientset: apiserver 的客戶端,用來(lái)對(duì)資源進(jìn)行增刪改。
  • syncHandler: 執(zhí)行核心邏輯代碼(更新子資源的聲明,使其匹配父資源的要求)。

1、syncHandler

syncHandler 像是一個(gè)約定,所有的 controller 內(nèi)執(zhí)行核心邏輯的函數(shù)都叫這個(gè)名字。該函數(shù)負(fù)責(zé)處理收到的資源消息,比如更新子資源的聲明,使其匹配父資源的要求。

以 deploymentController 為例,當(dāng)收到一個(gè)事件消息,syncHandler 被調(diào)用后:

注:

  • ??de??: 觸發(fā)事件的某個(gè) deployment 資源
  • ??dc??: deploymentController 控制器自己
  • ??rs??: replicaset,deployment 對(duì)應(yīng)的 replicaset 子資源

注:事件是一個(gè)字符串,形如 ??namespace/name??,代表觸發(fā)事件的資源的名稱以及所在的 namespace。因?yàn)槭录皇莻€(gè)名字,所以 syncHandler 需要自己去把當(dāng)前觸發(fā)的資源及其子資源查詢出來(lái)。這里面涉及很多查詢和遍歷,不過(guò)這些查詢都不會(huì)真實(shí)的調(diào)用 apiserver,而是在 informer 的內(nèi)存存儲(chǔ)里完成的。

graph TD

A1[將 key 解析為 namespace 和 name] --> A2[查詢 de]
A2 --> A3[查詢關(guān)聯(lián)子資源 rs]
A3 --> A31{de 是否 paused}
A31 --> |yes| A32[調(diào)用 dc.sync 部署 rs]
A31 --> |no| A4{是否設(shè)置了 rollback}
A4 --> |yes| A41[按照 annotation 設(shè)置執(zhí)行 rollback]
A4 --> |no| A5[rs 是否匹配 de 聲明]
A5 --> |no| A32
A5 --> |yes| A6{de.spec.strategy.type}
A6 --> |recreate| A61[dc.rolloutRecreate]
A6 --> |rolling| A62[dc.rolloutRolling]

圖片

查詢關(guān)聯(lián)子資源

  • kubernetes@59c0523b/pkg/controller/deployment/deployment_controller.go:getReplicaSetsForDeployment

k8s 中,資源間可以有上下級(jí)(父子)關(guān)系。

理論上 每一個(gè) controller 都負(fù)責(zé)創(chuàng)建當(dāng)前資源和子資源,父資源通過(guò) labelSelector 查詢應(yīng)該匹配的子資源。

一個(gè) deployment 的定義:

apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80

上文中講到 syncHandler 的時(shí)候,提到需要“查詢關(guān)聯(lián)子資源”。其實(shí)這一步驟很復(fù)雜,不僅僅是查詢,還包含對(duì)現(xiàn)有子資源進(jìn)行同步(修改)的操作。簡(jiǎn)而言之,這一步驟實(shí)際上做的是通過(guò)對(duì) owner、label 的比對(duì),確認(rèn)并更新當(dāng)前真實(shí)的父子資源關(guān)系。

對(duì)用戶呈現(xiàn)的資源關(guān)聯(lián)是依靠 label/selector。但實(shí)際上 k8s 內(nèi)部使用的是 owner 指針。(owner 指針是資源 metadata 內(nèi)用來(lái)標(biāo)記其父資源的 OwnerReferences)。

查詢匹配子資源的方法是:

  1. 遍歷 namespace 內(nèi)所有對(duì)應(yīng)類型的子資源 (比如 deployment controller 就會(huì)遍歷所有的 rs)
  2. 匹配校驗(yàn) owner 和 label

(父是當(dāng)前操作的資源,子是查詢出的子資源)

還是用 deployment 舉例,比如此時(shí)收到了一個(gè) deployment 事件,需要查詢出該 de 匹配的所有 rs:

graph LR

A(遍歷 namespace 內(nèi)所有 rs) --> A1{子.owner == nil}
A1 --> |false| A2{子.owner == 父.uid}
A2 --> |false| A21[skip]
A2 --> |true| A3{labels matched}
A3 --> |true| A5
A3 --> |false| A31[release]
A1 --> |true| A4{labels matched}
A4 --> |false| A21
A4 --> |true| A41[adopt]
A41 --> A5[標(biāo)記為父子]

圖片

如上圖所示,其實(shí)只有兩個(gè) case 下,rs 會(huì)被視為是 de 的子資源:

  1. rs owner 指向 de,且 labels 匹配
  2. rs owner 為空,且 labels 匹配

注意:如果 rs owner 指向了其他父資源,即使 label 匹配,也不會(huì)被視為當(dāng)前 de 的子資源

dc.sync

  • kubernetes@59c0523b/pkg/controller/deployment/sync.go:sync

這是 deployment controller 中執(zhí)行“檢查和子資源,令其匹配父資源聲明”的一步。準(zhǔn)確的說(shuō):

  1. dc.sync: 檢查子資源是否符合父資源聲明
  2. dc.scale: 操作更新子資源,使其符合父資源聲明
graph TD

A1[查詢 de 下所有舊的 rs] --> A2{當(dāng)前 rs 是否符合 de}
A2 --> |no| A21[newRS = nil]
A2 --> |yes| A22[NewRS = 當(dāng)前 rs]
A22 --> A23[將 de 的 metadata 拷貝給 newRS]
A23 --> A231[newRS.revision=maxOldRevision+1]
A231 --> A3[調(diào)用 dc.scale]
A21 --> A33
A3 --> A31{是否有 active/latest rs}
A31 --> |yes| A311[dc.scaleReplicaSet 擴(kuò)縮容]
A31 --> |no| A32{newRS 是否已飽和}
A32 --> |yes|A321[把所有 oldRS 清零]
A32 --> |no|A33{de 是否允許 rolling}
A33 --> |no|A331[return]
A33 --> |yes|A34[執(zhí)行滾動(dòng)更新]

圖片

滾動(dòng)更新的流程為:

(??if deploymentutil.IsRollingUpdate(deployment) {...}?? 內(nèi)的大量代碼,實(shí)際做的事情就是按照 deployment 的要求更新 rs 的 replicas 數(shù)。不過(guò)每次變更都涉及到對(duì) rs 和 deployment 的 maxSurge 的檢查,所以代碼較為復(fù)雜。)

  1. 計(jì)算所有 RS replicas 總和??allRSsReplicas??。
  2. 計(jì)算滾動(dòng)升級(jí)過(guò)程中最多允許出現(xiàn)的副本數(shù)??allowedSize???。??allowedSize = de.Spec.Replicas + maxSurge??
  3. ??deploymentReplicasToAdd = allowedSize - allRSsReplicas??
  4. 遍歷所有當(dāng)前 rs,計(jì)算每一個(gè) rs 的 replicas 變化量(proportion), 計(jì)算的過(guò)程中需要做多次檢查,不能溢出 rs 和 deployment 的 maxSurge。
  5. 更新所有 rs 的 replicas,然后調(diào)用??dc.scaleReplicaSet?? 提交更改。

四、Object

  • apimachinery@v0.0.0-20210708014216-0dafcb48b31e/pkg/apis/meta/v1/meta.go
  • apimachinery@v0.0.0-20210708014216-0dafcb48b31e/pkg/apis/meta/v1/types.go

ObjectMeta 定義了 k8s 中資源對(duì)象的標(biāo)準(zhǔn)方法。

雖然 resource 定義里是通過(guò) labelSelector 建立從上到下的關(guān)聯(lián), 但其實(shí)內(nèi)部實(shí)現(xiàn)的引用鏈?zhǔn)菑南碌缴系?。每一個(gè)資源都會(huì)保存一個(gè) Owner UID 的 slice。

每個(gè)資源的 metadata 中都有一個(gè) ??ownerReferences?? 列表,保存了其父資源(遍歷時(shí)遇到的第一個(gè)有效的資源會(huì)被認(rèn)為是其父資源)。

type ObjectMeta struct {
OwnerReferences []OwnerReference `json:"ownerReferences,omitempty" patchStrategy:"merge" patchMergeKey:"uid" protobuf:"bytes,13,rep,name=ownerReferences"`
}

判斷 owner 靠的是比對(duì)資源的 UID

func IsControlledBy(obj Object, owner Object) bool {
ref := GetControllerOfNoCopy(obj)
if ref == nil {
return false
}

// 猜測(cè):UID 是任何資源在 apiserver 注冊(cè)的時(shí)候,由 k8s 生成的 uuid
return ref.UID == owner.GetUID()
}

五、Informer

  • A deep dive into Kubernetes controllers[1]
  • client-go@v0.0.0-20210708094636-69e00b04ba4c/informers/factory.go

Informer 也經(jīng)歷了兩代演進(jìn),從最早各管各的 Informer,到后來(lái)統(tǒng)一監(jiān)聽,各自 filter 的 sharedInformer。

所有的 controller 都在一個(gè) controller-manager 進(jìn)程內(nèi),所以完全可以共享同一個(gè) informer, 不同的 controller 注冊(cè)不同的 filter(kind、labelSelector),來(lái)訂閱自己需要的消息。

簡(jiǎn)而言之,現(xiàn)在的 sharedIndexer,就是一個(gè)統(tǒng)一的消息訂閱器,而且內(nèi)部還維護(hù)了一個(gè)資源存儲(chǔ),對(duì)外提供可過(guò)濾的消息分發(fā)和資源查詢功能。

sharedIndexer 和 sharedInformer 的區(qū)別就是多了個(gè) threadsafe 的 map 存儲(chǔ),用來(lái)存 shared resource object。

現(xiàn)在的 informer 中由幾個(gè)主要的組件構(gòu)成:

  • reflecter:查詢器,負(fù)責(zé)從 apiserver 定期輪詢資源,更新 informer 的 store。
  • store: informer 內(nèi)部對(duì)資源的存儲(chǔ),用來(lái)提供 lister 遍歷等查詢操作。
  • queue:支持 controller 的事件訂閱。

圖片

各個(gè) controller 的訂閱和查詢絕大部分都在 sharedIndexer 的內(nèi)存內(nèi)完成,提高資源利用率和效率。

一般 controller 的消息來(lái)源就通過(guò)兩種方式:

  1. lister: controller 注冊(cè)監(jiān)聽特定類型的資源事件,事件格式是字符串,??<namespace>/<name>??
  2. handler: controller 通過(guò) informer 的??AddEventHandler??? 方法注冊(cè)??Add/Update/Delete?? 事件的處理函數(shù)。

這里有個(gè)值得注意的地方是,資源事件的格式是字符串,形如 ??<namespace>/<name>??,這其中沒(méi)有包含版本信息。

那么某個(gè)版本的 controller 拿到這個(gè)信息后,并不知道查詢出來(lái)的資源是否匹配自己的版本,也許會(huì)查出一個(gè)很舊版本的資源。

所以 controller 對(duì)于資源必須是向后兼容的,新版本的 controller 必須要能夠處理舊版資源。這樣的話,只需要保證運(yùn)行的是最新版的 controller 就行了。

圖片

1、Queue

controller 內(nèi)有大量的隊(duì)列,最重要的就是注冊(cè)到 informer 的三個(gè) add/update/delete 隊(duì)列。

RateLimitingQueue

  • client-go@v0.0.0-20210708094636-69e00b04ba4c/util/workqueue/rate_limiting_queue.go

實(shí)際使用的是隊(duì)列類型是 RateLimitingQueue,繼承于 Queue。

Queue

  • client-go@v0.0.0-20210708094636-69e00b04ba4c/util/workqueue/queue.go
type Interface interface {
// Add 增加任務(wù),可能是增加新任務(wù),可能是處理失敗了重新放入
//
// 調(diào)用 Add 時(shí),t 直接插入 dirty。然后會(huì)判斷一下 processing,
// 是否存在于 processing ? 返回 : 放入 queue
Add(item interface{})
Len() int
Get() (item interface{}, shutdown bool)
Done(item interface{})
ShutDown()
ShuttingDown() bool
}


type Type struct {
// queue 所有未被處理的任務(wù)
queue []t

// dirty 所有待處理的任務(wù)
//
// 從定義上看和 queue 有點(diǎn)相似,可以理解為 queue 的緩沖區(qū)。
// 比如調(diào)用 Add 時(shí),如果 t 存在于 processing,就只會(huì)插入 dirty,不會(huì)插入 queue,
// 這種情況表明外部程序處理失敗了,所以再次插入了 t。
dirty set

// processing 正在被處理的任務(wù)
//
// 一個(gè)正在被處理的 t 應(yīng)該從 queue 移除,然后添加到 processing。
//
// 如果 t 處理失敗需要重新處理,那么這個(gè) t 會(huì)被再次放入 dirty。
// 所以調(diào)用 Done 從 processing 移除 t 的時(shí)候需要同步檢查一下 dirty,
// 如果 t 存在于 dirty,則將其再次放入 queue。
processing set

cond *sync.Cond

shuttingDown bool

metrics queueMetrics

unfinishedWorkUpdatePeriod time.Duration
clock clock.Clock
}

隊(duì)列傳遞的資源事件是以字符串來(lái)表示的,格式形如 ??namespace/name??。

正因?yàn)橘Y源是字符串來(lái)表示,這導(dǎo)致了很多問(wèn)題。其中對(duì)于隊(duì)列的一個(gè)問(wèn)題就是:沒(méi)法為事件設(shè)置狀態(tài),標(biāo)記其是否已完成。為了實(shí)現(xiàn)這個(gè)狀態(tài),queue 中通過(guò) queue、dirty、processing 三個(gè)集合來(lái)表示。具體實(shí)現(xiàn)可以參考上面的注釋和代碼。

另一個(gè)問(wèn)題就是資源中沒(méi)有包含版本信息。

那么某個(gè)版本的 controller 拿到這個(gè)信息后,并不知道查詢出來(lái)的資源是否匹配自己的版本,也許會(huì)查出一個(gè)很舊版本的資源。

所以 controller 對(duì)于資源必須是向后兼容的,新版本的 controller 必須要能夠處理舊版資源。這樣的話,只需要保證運(yùn)行的是最新版的 controller 就行了。

六、GC

  • Garbage Collection[2]
  • Using Finalizers to Control Deletion[3]
  • kubernetes@59c0523b/pkg/controller/garbagecollector/garbagecollector.go

1、Concepts

我看到

GC 的第一印象是一個(gè)像語(yǔ)言 runtime 里的回收資源的自動(dòng)垃圾收集器。但其實(shí) k8s 里的 GC 的工作相對(duì)比較簡(jiǎn)單,更像是只是一個(gè)被動(dòng)的函數(shù)調(diào)用,當(dāng)用戶試圖刪除一個(gè)資源的時(shí)候, 就會(huì)把這個(gè)資源提交給 GC,然后 GC 執(zhí)行一系列既定的刪除流程,一般來(lái)說(shuō)包括:

  1. 刪除子資源
  2. 執(zhí)行刪除前清理工作(finalizer)
  3. 刪除資源

k8s 的資源間存在上下游依賴,當(dāng)你刪除一個(gè)上游資源時(shí),其下游資源也需要被刪除,這被稱為??級(jí)聯(lián)刪除 cascading deletion??。

刪除一個(gè)資源有三種策略(??propagationPolicy/DeletionPropagation??):

  • ??Foreground??(default): Children are deleted before the parent (post-order)
  • ??Background??: Parent is deleted before the children (pre-order)
  • ??Orphan??: 忽略 owner references

可以在運(yùn)行 ??kubectl delete --cascade=?????? 的時(shí)候指定刪除的策略,默認(rèn)為 ??foreground??。

2、Deletion

k8s 中,資源的 metadata 中有幾個(gè)對(duì)刪除比較重要的屬性:

  • ??ownerRerences??: 指向父資源的 UID
  • ??deletionTimestamp??: 如果不為空,表明該資源正在被刪除中
  • ??finalizers??: 一個(gè)字符串?dāng)?shù)組,列舉刪除前必須執(zhí)行的操作
  • ??blockOwnerDeletion??: 布爾,當(dāng)前資源是否會(huì)阻塞父資源的刪除流程

每一個(gè)資源都有 ??metadata.finalizers???,這是一個(gè) ??[]string??, 內(nèi)含一些預(yù)定義的字符串,表明了在刪除資源前必須要做的操作。每執(zhí)行完一個(gè)操作,就從 finalizers 中移除這個(gè)字符串。

無(wú)論是什么刪除策略,都需要先把所有的 finalizer 逐一執(zhí)行完,每完成一個(gè),就從 finalizers 中移除一個(gè)。在 finalizers 為空后,才能正式的刪除資源。

foreground、orphan 刪除就是通過(guò) finalizer 來(lái)實(shí)現(xiàn)的。

const (
FinalizerOrphanDependents = "orphan"
FinalizerDeleteDependents = "foregroundDeletion"
)

圖片

注:有一種讓資源永不刪除的黑魔法,就是為資源注入一個(gè)不存在的 finalizer。因?yàn)?GC 無(wú)法找到該 finalizer 匹配的函數(shù)來(lái)執(zhí)行,就導(dǎo)致這個(gè) finalizer 始終無(wú)法被移除, 而 finalizers 為空清空的資源是不允許被刪除的。

3、Foreground cascading deletion

  1. 設(shè)置資源的??deletionTimestamp???,表明該資源的狀態(tài)為正在刪除中(??"deletion in progress"??)。
  2. 設(shè)置資源的??metadata.finalizers??? 為??"foregroundDeletion"??。
  3. 刪除所有??ownerReference.blockOwnerDeletion=true?? 的子資源
  4. 刪除當(dāng)前資源

每一個(gè)子資源的 owner 列表的元素里,都有一個(gè)屬性 ??ownerReference.blockOwnerDeletion???,這是一個(gè) ??bool??, 表明當(dāng)前資源是否會(huì)阻塞父資源的刪除流程。刪除父資源前,應(yīng)該把所有標(biāo)記為阻塞的子資源都刪光。

在當(dāng)前資源被刪除以前,該資源都通過(guò) apiserver 持續(xù)可見。

4、Orphan deletion

觸發(fā) ??FinalizerOrphanDependents??,將所有子資源的 owner 清空,也就是令其成為 orphan。然后再刪除當(dāng)前資源。

5、Background cascading deletion

立刻刪除當(dāng)前資源,然后在后臺(tái)任務(wù)中刪除子資源。

graph LR

A1{是否有 finalizers} --> |Yes: pop, execute| A1
A1 --> |No| A2[刪除自己]
A2 --> A3{父資源是否在等待刪除}
A3 --> |No| A4[刪除所有子資源]
A3 --> |Yes| A31[在刪除隊(duì)列里提交父資源]
A31 --> A4

圖片

foreground 和 orphan 刪除策略是通過(guò) finalizer 實(shí)現(xiàn)的 因?yàn)檫@兩個(gè)策略有一些刪除前必須要做的事情:

  • foreground finalizer: 將所有的子資源放入刪除事件隊(duì)列
  • orphan finalizer: 將所有的子資源的 owner 設(shè)為空

而 background 則就是走標(biāo)準(zhǔn)刪除流程:刪自己 -> 刪依賴。

這個(gè)流程里有一些很有趣(繞)的設(shè)計(jì)。比如 foreground 刪除,finalizer 里把所有的子資源都放入了刪除隊(duì)列, 然后下一步在刪除當(dāng)前資源的時(shí)候,會(huì)發(fā)現(xiàn)子資源依然存在,導(dǎo)致當(dāng)前資源無(wú)法刪除。實(shí)際上真正刪除當(dāng)前資源(父資源),j是在刪除最后一個(gè)子資源的時(shí)候,每次都會(huì)去檢查下父資源的狀態(tài)是否是刪除中, 如果是,就把父資源放入刪除隊(duì)列,此時(shí),父資源才會(huì)被真正刪除。

6、Owner References

每個(gè)資源的 metadata 中都有一個(gè) ??ownerReferences?? 列表,保存了其父資源(遍歷時(shí)遇到的第一個(gè)有效的資源會(huì)被認(rèn)為是其父資源)。

owner 決定了資源會(huì)如何被刪除。刪除子資源不會(huì)影響到父資源。刪除父資源會(huì)導(dǎo)致子資源被聯(lián)動(dòng)刪除。(默認(rèn) ??kubectl delete --cascade=foreground??)

七、參考資料

關(guān)于本主題的內(nèi)容,我制作了一個(gè) slides,可用于內(nèi)部分享:https://s3.laisky.com/public/slides/k8s-controller.slides.html#/

1、如何閱讀源碼

核心代碼:https://github.com/kubernetes/kubernetes,所有的 controller 代碼都在 ??pkg/controller/?? 中。

所有的 clientset、informer 都被抽象出來(lái)在 https://github.com/kubernetes/client-go 庫(kù)中,供各個(gè)組件復(fù)用。

學(xué)習(xí)用示例項(xiàng)目:https://github.com/kubernetes/sample-controller

2、參考文章

  • Garbage Collection[4]
  • Using Finalizers to Control Deletion[5]
  • A deep dive into Kubernetes controllers[6]
  • kube-controller-manager[7]

引用鏈接

[1]

A deep dive into Kubernetes controllers: https://app.yinxiang.com/shard/s17/nl/2006464/674c3d83-f011-49b8-9135-413588c22c0f/

[2]

Garbage Collection: https://kubernetes.io/docs/concepts/workloads/controllers/garbage-collection/

[3]

Using Finalizers to Control Deletion: https://kubernetes.io/blog/2021/05/14/using-finalizers-to-control-deletion/

[4]

Garbage Collection: https://kubernetes.io/docs/concepts/workloads/controllers/garbage-collection/

[5]

Using Finalizers to Control Deletion: https://kubernetes.io/blog/2021/05/14/using-finalizers-to-control-deletion/

[6]

A deep dive into Kubernetes controllers: https://engineering.bitnami.com/articles/a-deep-dive-into-kubernetes-controllers.html

[7]

kube-controller-manager: https://kubernetes.io/docs/reference/command-line-tools-reference/kube-controller-manager/

責(zé)任編輯:龐桂玉 來(lái)源: 奇妙的Linux世界
相關(guān)推薦

2024-07-03 08:28:44

HWKafkaLEO

2022-06-13 11:05:35

RocketMQ消費(fèi)者線程

2020-06-28 07:39:44

Kafka分布式消息

2022-06-27 11:04:24

RocketMQ順序消息

2020-11-16 10:50:27

KubernetesIngressLinux

2022-02-28 11:10:42

ZGCG1收集器

2023-04-11 08:35:22

RocketMQ云原生

2022-07-11 11:06:11

RocketMQ函數(shù).消費(fèi)端

2021-04-25 10:45:59

Docker架構(gòu)Job

2022-07-04 11:06:02

RocketMQ事務(wù)消息實(shí)現(xiàn)

2021-05-18 06:55:07

Java AQS源碼

2022-01-05 14:30:44

容器Linux網(wǎng)絡(luò)

2019-07-24 08:49:36

Docker容器鏡像

2021-11-12 08:38:26

一致性哈希算法數(shù)據(jù)結(jié)構(gòu)

2021-01-28 10:55:47

Kubernetes IPLinux

2021-12-06 07:15:47

Pulsar地域復(fù)制

2022-05-09 11:15:05

RocketMQPULL 模式PUSH 模式

2020-06-03 08:19:00

Kubernetes

2015-07-13 10:23:23

Java圖解

2023-09-28 21:37:41

HashMap多線程
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)