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