OpenKruise v1.1:功能增强与上游对齐,大规模场景性能优化
作者:酒祝(王思宇)
云原生應(yīng)用自動(dòng)化管理套件、CNCF Sandbox 項(xiàng)目 – OpenKruise,近期發(fā)布了 v1.1 版本。
OpenKruise [1] ?是針對 Kubernetes 的增強(qiáng)能力套件,聚焦于云原生應(yīng)用的部署、升級、運(yùn)維、穩(wěn)定性防護(hù)等領(lǐng)域。所有的功能都通過 CRD 等標(biāo)準(zhǔn)方式擴(kuò)展,可以適用于 1.16 以上版本的任意 Kubernetes 集群。單條 helm 命令即可完成 Kruise 的一鍵部署,無需更多配置。******
版本解析
在 v1.1 版本中,OpenKruise 對不少已有功能做了擴(kuò)展與增強(qiáng),并且優(yōu)化了在大規(guī)模集群中的運(yùn)行性能。以下對 v1.1 的部分功能做簡要介紹。
值得注意的是,OpenKruise v1.1 已經(jīng)將 Kubernetes 代碼依賴版本升級到 v1.22,這意味著用戶可以在 CloneSet 等工作負(fù)載的 pod template 模板中使用 up to v1.22 的新字段等, 但用戶安裝使用 OpenKruise 所兼容的 Kubernetes 集群版本仍然保持在 >= v1.16。
原地升級支持容器順序優(yōu)先級
去年底發(fā)布的 v1.0 版本,OpenKruise 引入了容器啟動(dòng)順序控制 [2] 功能, 它支持為一個(gè) Pod 中的多個(gè)容器定義不同的權(quán)重關(guān)系,并在 Pod 創(chuàng)建時(shí)按照權(quán)重來控制不同容器的啟動(dòng)順序。
在 v1.0 中,這個(gè)功能僅僅能夠作用于每個(gè) Pod 的創(chuàng)建階段。當(dāng)創(chuàng)建完成后,如果對 Pod 中多個(gè)容器做原地升級,則這些容器都會(huì)被同時(shí)執(zhí)行升級操作。
最近一段時(shí)間,社區(qū)與 LinkedIn 等公司做過一些交流,獲得了更多用戶使用場景的輸入。在一些場景下,Pod 中多個(gè)容器存在關(guān)聯(lián)關(guān)系,例如業(yè)務(wù)容器升級的同時(shí),Pod 中其他一些容器也需要升級配置從而關(guān)聯(lián)到這個(gè)新版本;或是多個(gè)容器避免并行升級,從而保證如日志采集類的 sidecar 容器不會(huì)丟失業(yè)務(wù)容器中的日志等。
因此,在 v1.1 版本中 OpenKruise 支持了按容器優(yōu)先級順序的原地升級。在實(shí)際使用過程中,用戶無需配置任何額外參數(shù),只要 Pod 在創(chuàng)建時(shí)已經(jīng)帶有了容器啟動(dòng)優(yōu)先級,則不僅在 Pod 創(chuàng)建階段,會(huì)保證高優(yōu)先級容器先于低優(yōu)先級容器啟動(dòng);并且在單次原地升級中,如果同時(shí)升級了多個(gè)容器,會(huì)先升級高優(yōu)先級容器,等待它升級啟動(dòng)完成后,再升級低優(yōu)先級容器。
這里的原地升級,包括修改 image 鏡像升級與修改 env from metadata 的環(huán)境變量升級,詳見原地升級介紹[3] 總結(jié)來說:
-
對于不存在容器啟動(dòng)順序的 Pod,在多容器原地升級時(shí)沒有順序保證。
-
對于存在容器啟動(dòng)順序的 Pod:
-
如果本次原地升級的多個(gè)容器具有不同的啟動(dòng)順序,會(huì)按啟動(dòng)順序來控制原地升級的先后順序。
-
如果本地原地升級的多個(gè)容器的啟動(dòng)順序相同,則原地升級時(shí)沒有順序保證。
例如,一個(gè)包含兩個(gè)不同啟動(dòng)順序容器的 CloneSet 如下:
apiVersion: apps.kruise.io/v1alpha1 kind: CloneSet metadata:... spec:replicas: 1template:metadata:annotations:app-config: "... config v1 ..."spec:containers:- name: sidecarenv:- name: KRUISE_CONTAINER_PRIORITYvalue: "10"- name: APP_CONFIGvalueFrom:fieldRef:fieldPath: metadata.annotations['app-config']- name: mainimage: main-image:v1updateStrategy:type: InPlaceIfPossible當(dāng)我們更新 CloneSet,將其中 app-config annotation 和 main 容器的鏡像修改后, 意味著 sidecar 與 main 容器都需要被更新,Kruise 會(huì)先原地升級 Pod 來將其中 sidecar 容器重建來生效新的 env from annotation。
接下來,我們可以在已升級的 Pod 中看到?apps.kruise.io/inplace-update-state?annotation 和它的值:
{"revision": "{CLONESET_NAME}-{HASH}", // 本次原地升級的目標(biāo) revision 名字"updateTimestamp": "2022-03-22T09:06:55Z", // 整個(gè)原地升級的初次開始時(shí)間"nextContainerImages": {"main": "main-image:v2"}, // 后續(xù)批次中還需要升級的容器鏡像// "nextContainerRefMetadata": {...}, // 后續(xù)批次中還需要升級的容器 env from labels/annotations"preCheckBeforeNext": {"containersRequiredReady": ["sidecar"]}, // pre-check 檢查項(xiàng),符合要求后才能原地升級后續(xù)批次的容器"containerBatchesRecord":[{"timestamp":"2022-03-22T09:06:55Z","containers":["sidecar"]} // 已更新的首個(gè)批次容器(它僅僅表明容器的 spec 已經(jīng)被更新,例如 pod.spec.containers 中的 image 或是 labels/annotations,但并不代表 node 上真實(shí)的容器已經(jīng)升級完成了)] }當(dāng) sidecar 容器升級成功之后,Kruise 會(huì)接著再升級 main 容器。最終你會(huì)在 Pod 中看到如下的?apps.kruise.io/inplace-update-state?annotation:
{"revision": "{CLONESET_NAME}-{HASH}","updateTimestamp": "2022-03-22T09:06:55Z","lastContainerStatuses":{"main":{"imageID":"THE IMAGE ID OF OLD MAIN CONTAINER"}},"containerBatchesRecord":[{"timestamp":"2022-03-22T09:06:55Z","containers":["sidecar"]},{"timestamp":"2022-03-22T09:07:20Z","containers":["main"]}] }通常來說,用戶只需要關(guān)注其中?containerBatchesRecord?來確保容器是被分為多批升級的。?如果這個(gè) Pod 在原地升級的過程中卡住了,你可以檢查?nextContainerImages/nextContainerRefMetadata?字段,以及?preCheckBeforeNext?中前一次升級的容器是否已經(jīng)升級成功并 ready 了。
StatefulSetAutoDeletePVC 功能
從?Kubernetes?v1.23?開始,原生的?StatefulSet?加入了?StatefulSetAutoDeletePVC?功能,即根據(jù)給定策略來選擇保留或自動(dòng)刪除?StatefulSet?創(chuàng)建的?PVC?對象,參考文檔 [4] 。
因此,v1.1?版本的?Advanced?StatefulSet?從上游同步了這個(gè)功能,允許用戶通過?.spec.persistentVolumeClaimRetentionPolicy?字段來指定這個(gè)自動(dòng)清理策略。這需要你在安裝或升級 Kruise 的時(shí)候,啟用?StatefulSetAutoDeletePVC?feature-gate 功能。
apiVersion: apps.kruise.io/v1beta1 kind: StatefulSet spec:...persistentVolumeClaimRetentionPolicy: # optionalwhenDeleted: Retain | DeletewhenScaled: Retain | Delete其中,兩個(gè)策略字段包括:
- whenDeleted:當(dāng) Advanced StatefulSet 被刪除時(shí),對 PVC 的保留/刪除策略。
- whenScaled:當(dāng) Advanced StatefulSet 發(fā)生縮容時(shí),對縮容 Pod 關(guān)聯(lián) PVC 的保留/刪除策略。
每個(gè)策略都可以配置以下兩種值:
- Retain(默認(rèn)值):它的行為與過去 StatefulSet 一樣,在 Pod 刪除時(shí)對它關(guān)聯(lián)的 PVC 做保留。
- Delete:當(dāng) Pod 刪除時(shí),自動(dòng)刪除它所關(guān)聯(lián)的 PVC 對象。
除此之外,還有幾個(gè)注意點(diǎn):
Advanced DaemonSet 重構(gòu)并支持生命周期鉤子
早先版本的 Advanced DaemonSet 實(shí)現(xiàn)與上游控制器差異較大,例如對于 not-ready 和 unschedulable 的節(jié)點(diǎn)需要額外配置字段來選擇是否處理,這對于我們的用戶來說都增加了使用成本和負(fù)擔(dān)。
在 v1.1 版本中,我們對 Advanced DaemonSet 做了一次小重構(gòu),將它與上游控制器重新做了對齊。因此,Advanced DaemonSet 的所有默認(rèn)行為會(huì)與原生 DaemonSet 基本一致,用戶可以像使用 Advanced StatefulSet 一樣,通過修改?apiVersion?就能很方便地將一個(gè)原生 DaemonSet 修改為 Advanced DaemonSet 來使用。
另外,我們還為 Advanced DaemonSet 增加了生命周期鉤子,首先支持 preDelete hook,來允許用戶在 daemon Pod 被刪除前執(zhí)行一些自定義的邏輯。
apiVersion: apps.kruise.io/v1alpha1 kind: DaemonSet spec:...# define with labellifecycle:preDelete:labelsHandler:example.io/block-deleting: "true"當(dāng) DaemonSet 刪除一個(gè) Pod 時(shí)(包括縮容和重建升級):
- 如果沒有定義 lifecycle hook 或者 Pod 不符合 preDelete 條件,則直接刪除。
- 否則,會(huì)先將 Pod 更新為?PreparingDelete?狀態(tài),并等待用戶自定義的 controller 將 Pod 中關(guān)聯(lián)的 label/finalizer 去除,再執(zhí)行 Pod 刪除。
Disable DeepCopy 性能優(yōu)化
默認(rèn)情況下,我們在使用 controller-runtime 來編寫 Operator/Controller 時(shí), 使用其中?sigs.k8s.io/controller-runtime/pkg/client?Client?客戶端來 get/list 查詢對象(typed),都是從內(nèi)存 Informer 中獲取并返回,這是大部分人都知道的。
但很多人不知道的是,在這些 get/list 操作背后,controller-runtime 會(huì)將從 Informer 中查到的所有對象做一次 deep copy 深拷貝后再返回。
這個(gè)設(shè)計(jì)的初衷,是避免開發(fā)者錯(cuò)誤地將 Informer 中的對象直接篡改。在深拷貝之后,無論開發(fā)者對 get/list 返回的對象做了任何修改,都不會(huì)影響到 Informer 中的對象,后者只會(huì)從 kube-apiserver 的 ListWatch 請求中同步。
但是在一些很大規(guī)模的集群中,OpenKruise 中各個(gè)控制器同時(shí)在運(yùn)行,同時(shí)每個(gè)控制器還存在多個(gè) worker 執(zhí)行 Reconcile,可能會(huì)帶來大量的 deep copy 操作。例如集群中有大量應(yīng)用的 CloneSet,而其中一些 CloneSet 下管理的 Pod 數(shù)量非常多,則每個(gè) worker 在 Reconcile 的時(shí)候都會(huì) list 查詢一個(gè) CloneSet 下的所有 Pod 對象,再加上多個(gè) worker 并行操作, 可能造成 kruise-manager 瞬時(shí)的 CPU 和 Memory 壓力陡增,甚至在內(nèi)存配額不足的情況下有發(fā)生 OOM 的風(fēng)險(xiǎn)。
在上游的 controller-runtime 中,我在去年已經(jīng)提交合并了?DisableDeepCopy 功能 [5] ,包含在 controller-runtime v0.10 及以上的版本。它允許開發(fā)者指定某些特定的資源類型,在做 get/list 查詢時(shí)不執(zhí)行深拷貝,而是直接返回 Informer 中的對象指針。
例如下述代碼,在 main.go 中初始化 Manager 時(shí),為 cache 加入?yún)?shù)即可配置 Pod 等資源類型不做深拷貝。
mgr, err := ctrl.NewManager(cfg, ctrl.Options{...NewCache: cache.BuilderWithOptions(cache.Options{UnsafeDisableDeepCopyByObject: map[client.Object]bool{&v1.Pod{}: true,},}),})但在?Kruise?v1.1?版本中,我們沒有選擇直接使用這個(gè)功能,而是將?Delegating?Client [6]?重新做了封裝,?從而使得開發(fā)者可以在任意做?list?查詢的地方通過?DisableDeepCopy?ListOption?來指定單次的 list 操作不做深拷貝。
if err := r.List(context.TODO(), &podList, client.InNamespace("default"), utilclient.DisableDeepCopy); err != nil {return nil, nil, err}這樣做的好處是使用上更加靈活,避免為整個(gè)資源類型關(guān)閉深拷貝后,眾多社區(qū)貢獻(xiàn)者在參與開發(fā)的過程中如果沒有注意到則可能會(huì)錯(cuò)誤修改 Informer 中的對象。
其他改動(dòng)
你可以通過?Github release [7]?頁面,來查看更多的改動(dòng)以及它們的作者與提交記錄。
社區(qū)參與
非常歡迎你通過 Github/Slack/釘釘/微信 等方式加入我們來參與 OpenKruise 開源社區(qū)。你是否已經(jīng)有一些希望與我們社區(qū)交流的內(nèi)容呢?可以在我們的社區(qū)雙周會(huì) [8] 上分享你的聲音,或通過以下渠道參與討論:
- 加入社區(qū)?Slack channel [9] (English)
- 加入社區(qū)釘釘群:搜索群號?23330762?(Chinese)
- 加入社區(qū)微信群(新):添加用戶?openkruise?并讓機(jī)器人拉你入群 (Chinese)
相關(guān)鏈接?* *?
[1]OpenKruise
??https://openkruise.io/??
[2]容器啟動(dòng)順序控制
??https://openkruise.io/zh/docs/user-manuals/containerlaunchpriority/??
[3]原地升級介紹
??https://openkruise.io/zh/docs/core-concepts/inplace-update??
[4]參考文檔
??https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#persistentvolumeclaim-retention??
[5]DisableDeepCopy 功能
??https://github.com/kubernetes-sigs/controller-runtime/pull/1274???
[6]Delegating Client
??https://github.com/openkruise/kruise/blob/master/pkg/util/client/delegating_client.go??
[7]Github release
??https://github.com/openkruise/kruise/releases??
[8]社區(qū)雙周會(huì)
??https://shimo.im/docs/gXqmeQOYBehZ4vqo??
[9]Slack channel
??https://kubernetes.slack.com/?redir=%2Farchives%2Fopenkruise??
?點(diǎn)擊?此處?,查看 OpenKruise 項(xiàng)目官方主頁與文檔!?
總結(jié)
以上是生活随笔為你收集整理的OpenKruise v1.1:功能增强与上游对齐,大规模场景性能优化的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 阿里云与达摩院合作 AHPA 弹性预测论
- 下一篇: 恭喜我的同事丁宇入选年度 IT 领军人物