如何基於 OpenKruise 打破原生 Kubernetes 中的容器執行時操作侷限?
作者:王思宇,阿里雲技術專家,OpenKruise 社群負責人
通常情況下,人們只能使用普通舊資料作為 Kubernetes 中最小的操作單元。一些公司在他們的叢集中入侵了 Kubelet 的程式碼,以便他們可以對容器做更多的事情。然而,為執行時擴充套件操作確實是一種錯誤的方法,因為它不利於開源和社群的合作。現在,雲原生計算基金會沙箱專案之一 OpenKruise 提供了高階功能,可以在每個原始 Kubernetes 叢集中操作容器執行時。在本次演講中,我們將介紹 OpenKruise 中一些功能的用法,以及它如何與 Kubelet 和 CRI 合作。
本次分享主要分為以下幾個部分,首先我們介紹在 Kubernetes 中,針對於對容器 runtime 的操作限制有哪些,也就是說我們在 Kubernetes 中,它的機制限制了我們哪些操作,其實是對 controller runtime 是做不到的;第二點是 OpenKruise 是怎樣拓展對 controller runtime 的這些操作;第三點是我們簡單做一個 demo,我們如何通過 OpenKruise 來實現這些操作的;第四點是簡單介紹一下我們後續的一些規劃。
Kubernetes 中針對容器執行時的操作有哪些限制?
Kubernetes 中的容器執行時
如上圖所示,這是一個 Kubernetes 的基本結構,它的結構在每個節點( Node)上,其實是 Kubelet 在 API server 裡面收到它的。比如 Pod 的變化,當 Kubelet 收到一個 Pod 建立之後,通過 CRI(Container Runtime Interface) , CNI 以及類似的公共介面(例如 CSI)來呼叫底層真正的介面實現者去完成操作。對於容器執行時來說,是通過 CRI 介面呼叫底層真正的 Runtime 執行時來完成對容器的建立和啟動映象拉取這些操作。
其中 CRI 是 Kubernetes1.5 之後加入的一個新功能,由協議緩衝區和 gRPC API 組成,提供了一個明確定義的抽象層,它的目的是對於 Kubelet 能遮蔽底下 Runtime 實現的細節而只顯示所需的介面。
(https://github.com/kubernetes/cri-api)
在 Kubernetes1.5 之前,Kubelet 與 Docker 是相耦合的,Kubelet 其實是引入了 Docker 的 client,由它們直接對 Docker 操作。有了 CRI ,對於 Kubelet 來說就不用關心底層真正的 Runtime 實現是什麼,而只需要呼叫這層介面,介面背後的實現可能是 Containerd-d ,可能是 CRI-O,也可能是 Docker。
CRI 的職責是對容器執行時以及對映象做相關的管理,包括對容器的啟停操作,對 Sandbox 容器的操作,容器 States 的資料採集,以及映象的拉取和查詢等操作。因此,CRI 提供了比較完善的容器介面,如下圖。
Kubernetes 中容器執行時的操作限制
Kubernetes API 並沒有提供對容器執行時的介面操作,它唯一提供的是對 v1 版本的 Pod 操作(Pod API CRUD,Pod Subresources API)。除了 Pod 建立和更新之外,唯一能跟 Runtime 做比較相對應操作的是 Exec subresource 和 Log subresource。
Kubernetes 的 API 層面限制了使用者只能建立或刪除 Pod ,除此之外,裡面的容器只能做 Exec, Log 這樣的操作。在 Kubernetes 介面層面,使用者無法進行比如拉取映象、重啟容器等操作。
那有沒有可能去拓展這個 API 呢?
我們發現 Kubelet 目前沒有提供任何 hook 解決 plugin 的這個操作,來讓外層能去動態拓展 Kubelet 所做的事情。( Kubelet 的介面是不提供這樣的外掛機制的)那是否可以加入一個與 Kubelet 類似的新元件,可以連線到 CRI API,來拓展 Kubernetes 容器進行時的操作呢?
我們同樣會呼叫 CRI 這一層,比如它可以拉映象,可以重啟容器,它的上層也可以接收一個對於 Kubernetes API 上定義的一個 CRD 資源,這個 CRD 資源定義了讓使用者能夠宣告對 CRI 介面做一些操作。比如它可以定義指定使用者去拉映象,去重啟容器,可以做更多的事情。
這種方式是我們能想到的,對於這個 Kubernetes 容器執行時 operations 的拓展思路。
OpenKruise 是什麼?
OpenKruise 概念
Openkruise 是 Kubernetes 的一個拓展套件,它彌補了 Kubernetes 很多功能不足,例如對於應用工作負載(應用部署釋出相關功能)的不足,對於 container runtime 操作的不足等。它可以配合原生 Kubernetes 使用,併為管理應用容器、sidecar、映象分發等方面提供更加強大和高效的能力。
2020 年 11 月,OpenKruise 作為 Sandbox 專案加入 CNCF。
Openkruise 本身並不是一個 PasS 平臺。但 PasS 平臺可以通過利用 Openkruise 提供的拓展能力更好的管理,運維雲原生的應用。感興趣的朋友可以通過以下網址瞭解更多 OpenKruise 相關資訊。
Github:
https://github.com/openkruise/kruise
WebSite:
https://openkruise.io
OpenKruise 的功能
OpenKruise 是基於 CRD 的拓展,其功能大致可分為五部分:
(1)應用工作負載:包括針對無狀態應用,有狀態應用的灰度釋出、流量控制、它的原地升級等相關功能;
(2)Sidecar 容器管理:提供更多增強的獨立定義以及獨立部署;
(3)應用多分割槽管理(Multi-domain management):一個應用如果要部署在多個分割槽,會進行打散和分片的管理。
(4)應用可用性防護:保護雲原生應用在 Kubernetes 上執行時的高可用性;
(5)拓展增強操作:通過這種方式來實現對 container runtime(執行時)增強的操作能力。其中拓展增強操作是本文主要介紹的功能,後續我們會詳細展開。
OpenKruise 功能圖
OpenKruise 的架構
如圖所示,OpenKruise 主要分成中心端(Kruise-manager)和節點端(Node)兩個元件。中心端的 Kruise-manager 包含 controllers 和 webhooks ,通過 Kruise-manager 中心端的角色和每個節點上 kruise-daemon 功能結合,可以完成很多 Kubernetes 本身不提供的能力。Kruise-daemon 是用來避免對 Kubelet 做改造,通過拓展的方式對 CRI runtime 進行操作。
Runtime 的拓展功能
Runtime 有三個核心拓展功能。
原地升級功能
原地升級是一種可以避免刪除、新建 Pod 的升級映象能力。
如上圖所示:第一部分並不是直接通過 kruise-daemon 拓展,而是利用 Kubelet 的原生機制,叫做原地升級。
如何理解原地升級?我們舉一個簡單的例子:比如原來有一個 pod-a ,此時的 pod-a 是通過 deployment 的或者 Openkruise 的 cloneset 擴容出來的。如果我們想要升級 app容器的映象版本,比如從 Fv1 升級到 Fv2,正常情況下大家使用 development 部署是採用 Recreatte Update 也就是重建 Pod 升級,重建完成後我們會看到 Pod 的名字,Pod UID, (映象也會升級為 V2) 很大程度上都會發生改變。
再看後面兩者,前者 Pod 的名字和 UID 一定會發生變化,因為它已經不是同一個 Pod 的物件了。相對於我們這次介紹的原地升級,Pod 的物件其實還是原來的物件,Pod 的名字,Pod UID 都不變。其次,Pod 所在節點的 IP 也都不變,唯一變化的是映象從 v1 級到了 v2,由於 Pod 節點沒有發生任何變化,Pod 物件就不需要經過排程器重新排程,IP 分配,volume 分配,掛載這些耗時也都消除掉了,因此一個很明顯好處就是節省了排程的耗時。
大家都知道,當應用映象從 v1 升到 v2 的過程中,可能只是最頂上的 layers 發生了變化,底下絕大部分的這個 base 映象,公共 layers 是沒有發生變化的。
當我們在同一個節點上面做原地升級的時候,可以複用原有 v1 映象的大部分 layers ,只用下載小部分的 layers 映象。
在升級 app 容器的過程中,Pod 中的其他容器。如 sidecar 容器,是一直正常執行的,沒有受到影響。反過來說,當我們升級 sidecar 容器時,容器也是正常執行的。這樣可以很大程度上避免在升級一個旁路(比如運維)容器的過程中,也要對業務能力造成影響。
1.1 優勢
• 節省操作耗時,包括:Pod 排程、IP 分配、volume 分配、掛載等;
• 複用大部分映象層;
• 當一個容器進行升級時,不會對 Pod 中的其它容器造成影響;
1.2 工作原理
原地升級的原理可以簡單理解為 Kubelet 在建立每個容器時,會為容器計算一個 hash 值,當上層修改了比如 app 容器的 image 之後,Kubelet 就認為容器的 hash 值發生了變化。當 Kubelet 發現 Pod spec 中 app 容器的 hash 值和實際的,如 container d 對應的 hash 值不一致時,就會把舊的 app 容器停掉,用新的映象再重建新的 app 容器,從而實現容器的原地升級的能力。
容器重啟功能
容器重啟的功能是很多業務,包括運維平臺都很依賴的功能。大家可能會問,在 Kubernetes 中,一個 Pod 既然是無狀態的,那麼想重啟時就直接刪除 Pod,再新建一個 Pod 不就可以了嗎?
這是當然可以的,但對於業務來說可能還存在很多 debug 場景,並不只是重建一個新的 Pod 就可以,而是要從原地把容器進行重啟,相當於把裡面的業務程序重啟。比如想保留一些 volume 中的資料,一些網路、堆疊資訊等,這些場景都導致業務需要有 Kubernetes Pod 的容器原地重啟能力。
Kubernetes 原生是不具備容器重啟能力的,對於 Kubernetes 來說,如果想要重啟容器,只能手動進入容器,把容器裡的應用程序殺掉,這時當容器退出,Kubernetes 會再把它拉起。當然這種方式其實都比較 hack 的這麼一種方式, Openkruise 所提供的容器重啟能力對於 API 來說,只需要建立一個 CR。
CR 裡寫的東西很明確,name spacesl 只需定義跟 pod 在同一個 name spacesl 裡,其中 name 是自定義的名字,通過指定需要重啟的 Pod 是哪個,需要重啟 pod 出來哪些 containers,當定義了這些資訊之後,提交 CR,當 kruise 收到 CR ,kruise - manager 會先經過 webhooks,對它注入一些資訊,接下來 kruise-daemon 拿到 CR 會根據 CR 中定義的資訊(比如會找到對應 Pod 的容器)執行 preStop hook,再通過 CRI 介面,通過 EXTC 執行 preStop,當 preStop 執行完成之後再呼叫這個 CRI 的 stop 。
其實這個停止方式和本身 Kubelet 在刪除 Pod 時對容器的停止方式是一致的。當 kruise-daemon 對舊容器,比如對零號的 app(app_0)容器停掉之後,Kubelet 感知到 app 容器停掉了,接著就會新建一個 F1 容器並把它拉起,通過這種方式來實現優雅的容器原地重啟能力。
程式碼示例:
apiVersion: apps.kruise.io/v1alpha1
kind: ContainerRecreateRequest
metadata:
namespace: pod-namespace
name: xxx
spec:
podName: pod-name
containers:
- name: app
strategy:
# ...
activeDeadlineSeconds: 300
ttlSecondsAfterFinished: 1800
status:
containerRecreateStates:
- name: app
phase: Succeeded
phase: Completed
# ..
映象預熱功能
提前在節點上包括新建的 Node 上預熱,就可以大幅度減少後續 Pod 擴容的耗時。
從上圖我們可以看到,對於上層使用者來說 Openkruise 提供了一個 CRD 叫 ImagePullJob,使用者可以定義需要預熱哪個映象,也可以選擇性的配置 selector(selector 可以是節點的標籤選擇器也可以根據 Pod 進行選擇),以上都可以在 Pod 所在節點上進行預熱。
當用戶建立 ImagePullJob 後,對於 kruise 內部邏輯來說,kruise 會把 ImagePullJob 拆分到每個 node 對應 node image 的 CR 上,當同步上去後,節點上的 kruise-daemon 會拿到這個節點所對應node image 的 CR ,在節點上預熱 CR 中定義的多個映象。
換句話說,每個節點所對應的 node image 中的映象列表,其實就表示了上層所有 ImagePullJob 指定在這個節點上要拉取映象全集。kruise-daemon 底層拿到 node image 後,相當於也是呼叫 CRI 的 Pod image 介面來完成映象的預熱。
程式碼示例:
apiVersion: apps.kruise.io/v1alpha1
kind: ImagePullJob
metadata:
name: test-job
spec:
image: nginx:latest
parallelism: 10
selector:
# ...
podSelector:
# ...
completionPolicy:
# ...
未來專案規劃
2021 年 12 月,OpenKruise 完成了首個正式版本 v1.0 的釋出,使雲原生應用自動化達到新的高峰。OpenKruise 從 2019 年釋出 0.1 版本到現在已經有 2 年多的時間,已有超過 70 位貢獻者參與貢獻,star 數量已經超過 3000。2022,我們將推進 OpenKruise 成為 CNCF Incubation 專案,推動雲原生應用自動化領域進一步成熟。
使用使用者:
• 阿里巴巴集團, 螞蟻集團, 鬥魚TV, 申通, Boss 直聘
• 杭銀消費, 萬翼科技, 多點, Bringg, 佐疆科技
• Lyft, 攜程, 享住智慧, VIPKID, 掌門 1 對 1
• 小紅書, 比心, 永輝科技中心, 跟誰學, 哈囉出行
• Spectro Cloud, 艾佳生活, Arkane Systems, 滴普科技, 火花思維
• OPPO, 蘇寧, 歡聚時代, 匯量科技, 深圳鳳凰木網路有限公司
• 小米, 網易,美團金融, Shopee, LinkedIn
釋出雲原生技術最新資訊、彙集雲原生技術最全內容,定期舉辦雲原生活動、直播,阿里產品及使用者最佳實踐釋出。與你並肩探索雲原生技術點滴,分享你需要的雲原生內容。
關注【阿里巴巴雲原生】公眾號,獲取更多雲原生實時資訊!