談?wù)?Kubernetes 的問(wèn)題和局限性
2014 年發(fā)布的 Kubernetes 在今天儼然已成為容器編排領(lǐng)域的事實(shí)標(biāo)準(zhǔn),相信談到 Kubernetes 的開(kāi)發(fā)者都會(huì)一再?gòu)?fù)述上述現(xiàn)象。如下圖所示,今天的大多數(shù)個(gè)人或者團(tuán)隊(duì)都會(huì)選擇 Kubernetes 管理容器,而也有 75% 的人會(huì)在生產(chǎn)環(huán)境中使用 Kubernetes。
圖 1 - Kubernetes 容器編排[^1]
在這種全民學(xué)習(xí)和使用 Kubernetes 的大背景下,我們也應(yīng)該非常清晰地知道 Kubernetes 有哪些局限性。雖然 Kubernetes 能夠解決容器編排領(lǐng)域的大多數(shù)問(wèn)題,但是仍然有一些場(chǎng)景是它很難處理、甚至無(wú)法處理的,只有對(duì)這些潛在的風(fēng)險(xiǎn)有清晰的認(rèn)識(shí),才能更好地駕馭這項(xiàng)技術(shù),這篇文章將從集群管理和應(yīng)用場(chǎng)景兩個(gè)部分談?wù)? Kubernetes 社區(qū)目前的發(fā)展和一些局限性。
集群管理
集群是一組能夠在一起協(xié)同工作的計(jì)算機(jī),我們可以將集群中的所有計(jì)算機(jī)看成一個(gè)整體,所有資源調(diào)度系統(tǒng)都是以集群為維度進(jìn)行管理的,集群中的所有機(jī)器構(gòu)成了資源池,這個(gè)巨大的資源池會(huì)為待運(yùn)行的容器提供資源執(zhí)行計(jì)算任務(wù),這里簡(jiǎn)單談一談 Kubernetes 集群管理面對(duì)的幾個(gè)復(fù)雜問(wèn)題。
水平擴(kuò)展性
集群大小是我們?cè)谠u(píng)估資源管理系統(tǒng)時(shí)需要關(guān)注的重要指標(biāo)之一,然而 Kubernetes 能夠管理的集群規(guī)模遠(yuǎn)遠(yuǎn)小于業(yè)界的其他資源管理系統(tǒng)。集群大小為什么重要呢,我們先來(lái)看另一個(gè)同樣重要的指標(biāo) — 資源利用率,很多工程師可能沒(méi)有在公有云平臺(tái)上申請(qǐng)過(guò)資源,這些資源都相當(dāng)昂貴,在 AWS 上申請(qǐng)一個(gè)與主機(jī)差不多配置的虛擬機(jī)實(shí)例(8 CPU、16 GB)每個(gè)月大概需要 150 美金,約為 1000 人民幣[^2]。
圖 2 - AWS EC2 價(jià)格
大多數(shù)的集群都會(huì)使用 48 CPU 或者 64 CPU 的物理機(jī)或者虛擬機(jī)作為集群中的節(jié)點(diǎn),如果我們的集群中需要包含 5,000 個(gè)節(jié)點(diǎn),那么這些節(jié)點(diǎn)每個(gè)月大概要 8,000,000 美元,約為 50,000,000 人民幣,在這樣的集群中提升 1% 的資源利用率就相當(dāng)于每個(gè)月節(jié)省了 500,000 的成本。
多數(shù)在線(xiàn)任務(wù)的資源利用率都很低,更大的集群意味著能夠運(yùn)行更多的工作負(fù)載,而多種高峰和低谷期不同的負(fù)載部署在一起可以實(shí)現(xiàn)超售,這樣能夠顯著地提高集群的資源利用率,如果單個(gè)集群的節(jié)點(diǎn)數(shù)足夠多,我們?cè)诓渴鸩煌?lèi)型的任務(wù)時(shí)會(huì)有更合理的組合,可以完美錯(cuò)開(kāi)不同服務(wù)的高峰期。
Kubernetes 社區(qū)對(duì)外宣傳的是單個(gè)集群最多支持 5,000 節(jié)點(diǎn),Pod 總數(shù)不超過(guò) 150,000,容器總數(shù)不超過(guò) 300,000 以及單節(jié)點(diǎn) Pod 數(shù)量不超過(guò) 100 個(gè)[^3],與幾萬(wàn)節(jié)點(diǎn)的 Apache Mesos 集群、50,000 節(jié)點(diǎn)的微軟 YARN 集群[^4]相比,Kubernetes 的集群規(guī)模整整差了一個(gè)數(shù)量級(jí)。雖然阿里云的工程師也通過(guò)優(yōu)化 Kubernetes 的各個(gè)組件實(shí)現(xiàn)了 5 位數(shù)的集群規(guī)模,但是與其他的資源管理方式相比卻有比較大的差距[^5]。
圖 3 - Apache Mesos 與 Hadoop YARN
需要注意的是 Kubernetes 社區(qū)雖然對(duì)外宣稱(chēng)單集群可以支持 5,000 節(jié)點(diǎn),同時(shí)社區(qū)也有各種各樣的集成測(cè)試保證每個(gè)改動(dòng)都不會(huì)影響它的伸縮性[^6],但是 Kubernetes 真的非常復(fù)雜,我們沒(méi)有辦法保證你使用的每個(gè)功能在擴(kuò)容的過(guò)程中都不出問(wèn)題。而在生產(chǎn)環(huán)境中,我們甚至可能在集群擴(kuò)容到 1000 ~ 1500 節(jié)點(diǎn)時(shí)遇到瓶頸。
每個(gè)稍具規(guī)模的大公司都想要實(shí)現(xiàn)更大規(guī)模的 Kubernetes 集群,但是這不是一個(gè)改幾行代碼就能解決的簡(jiǎn)單問(wèn)題,它可能需要我們限制 Kubernetes 中一些功能的使用,在擴(kuò)容的過(guò)程中,etcd、API 服務(wù)器、調(diào)度器以及控制器都有可能出現(xiàn)問(wèn)題。社區(qū)中已經(jīng)有一些開(kāi)發(fā)者注意到了其中的一些問(wèn)題,例如在節(jié)點(diǎn)上增加緩存降低 API 服務(wù)器的負(fù)載[^7],但是要推動(dòng)類(lèi)似的改變還是很困難的,有志之士可以嘗試在社區(qū)推動(dòng)類(lèi)似的項(xiàng)目。
多集群管理
單個(gè)集群的容量再大也無(wú)法解決企業(yè)面對(duì)的問(wèn)題,哪怕有一天 Kubernetes 集群可以達(dá)到 50,000 節(jié)點(diǎn)的規(guī)模,我們?nèi)匀恍枰芾矶鄠€(gè)集群,多集群管理也是 Kubernetes 社區(qū)目前正在探索的方向,社區(qū)中的多集群興趣小組(SIG Multi-Cluster)目前就在完成相關(guān)的工作[^8]。在作者看來(lái),Kubernetes 的多集群會(huì)帶來(lái)資源不平衡、跨集群訪(fǎng)問(wèn)困難以及提高運(yùn)維和管理成本三大問(wèn)題,我們?cè)谶@里談一談目前在開(kāi)源社區(qū)和業(yè)界幾種可供參考和選擇的解決方案。
kubefed
首先要介紹的是 kubefed,該項(xiàng)目是 Kubernetes 社區(qū)給出的解決方案,它同時(shí)提供了跨集群的資源和網(wǎng)絡(luò)管理的功能,社區(qū)的多集群興趣小組(SIG Multi-Cluster)負(fù)責(zé)了該項(xiàng)目的開(kāi)發(fā)工作:
圖 4 - Kubernetes 聯(lián)邦
kubefed 通過(guò)一個(gè)中心化的聯(lián)邦控制面板管理多集群中的元數(shù)據(jù),上層的控制面板會(huì)為管理器群中的資源創(chuàng)建對(duì)應(yīng)的聯(lián)邦對(duì)象,例如:FederatedDeployment:
- kind: FederatedDeployment
- ...
- spec:
- ...
- overrides:
- # Apply overrides to cluster1
- - clusterName: cluster1
- clusterOverrides:
- # Set the replicas field to 5
- - path: "/spec/replicas"
- value: 5
- # Set the image of the first container
- - path: "/spec/template/spec/containers/0/image"
- value: "nginx:1.17.0-alpine"
- # Ensure the annotation "foo: bar" exists
- - path: "/metadata/annotations"
- op: "add"
- value:
- foo: bar
- # Ensure an annotation with key "foo" does not exist
- - path: "/metadata/annotations/foo"
- op: "remove"
- # Adds an argument `-q` at index 0 of the args list
- # this will obviously shift the existing arguments, if any
- - path: "/spec/template/spec/containers/0/args/0"
- op: "add"
- value: "-q"
上層的控制面板會(huì)根據(jù)聯(lián)邦對(duì)象 FederatedDeployment 的規(guī)格文件生成對(duì)應(yīng)的 Deployment 并推送到下層的集群,下層集群可以正常根據(jù) Deployment 中的定義創(chuàng)建特定數(shù)量的副本。
圖 5 - 從聯(lián)邦對(duì)象到普通對(duì)象
FederatedDeployment 只是一種最簡(jiǎn)單的分發(fā)策略,在生產(chǎn)環(huán)境中我們希望通過(guò)聯(lián)邦的集群實(shí)現(xiàn)容災(zāi)等復(fù)雜功能,這時(shí)可以利用 ReplicaSchedulingPreference 在不同集群中實(shí)現(xiàn)更加智能的分發(fā)策略:
- apiVersion: scheduling.kubefed.io/v1alpha1
- kind: ReplicaSchedulingPreference
- metadata:
- name: test-deployment
- namespace: test-ns
- spec:
- targetKind: FederatedDeployment
- totalReplicas: 9
- clusters:
- A:
- minReplicas: 4
- maxReplicas: 6
- weight: 1
- B:
- minReplicas: 4
- maxReplicas: 8
- weight: 2
上述調(diào)度的策略可以實(shí)現(xiàn)工作負(fù)載在不同集群之間的權(quán)重,在集群資源不足甚至出現(xiàn)問(wèn)題時(shí)將實(shí)例遷移到其他集群,這樣既能夠提高服務(wù)部署的靈活性和可用性,基礎(chǔ)架構(gòu)工程師也可以更好地平衡多個(gè)集群的負(fù)載。
我們可以認(rèn)為 kubefed 的主要作用是將多個(gè)松散的集群組成強(qiáng)耦合的聯(lián)邦集群,并提供更加高級(jí)的網(wǎng)絡(luò)和部署功能,這樣我們可以更容易地解決集群之間資源不平衡和連通性的一些問(wèn)題,然而該項(xiàng)目的關(guān)注點(diǎn)不包含集群生命周期的管理,
集群接口
Cluster API 也是 Kubernetes 社區(qū)中與多集群管理相關(guān)的項(xiàng)目,該項(xiàng)目由集群生命周期小組(SIG Cluster-Lifecycle)負(fù)責(zé)開(kāi)發(fā),其主要目標(biāo)是通過(guò)聲明式的 API 簡(jiǎn)化多集群的準(zhǔn)備、更新和運(yùn)維工作,你在該項(xiàng)目的 設(shè)計(jì)提案 中能夠找到它的職責(zé)范圍[^9]。
圖 6 - Cluster API 概念
在該項(xiàng)目中最重要的資源就是 Machine,它表示一個(gè) Kubernetes 集群中的節(jié)點(diǎn)。當(dāng)該資源被創(chuàng)建時(shí),特定提供商的控制器會(huì)根據(jù)機(jī)器的定義初始化并將新的節(jié)點(diǎn)注冊(cè)到集群中,在該資源被更新或者刪除時(shí),也會(huì)執(zhí)行操作達(dá)到用戶(hù)的狀態(tài)。
這種策略與阿里的多集群管理的方式有一些相似,它們都使用聲明式的 API 定義機(jī)器和集群的狀態(tài),然后使用 Kubernetes 原生的 Operator 模型在更高一層的集群中管理下層集群,這能夠極大降低集群的運(yùn)維成本并提高集群的運(yùn)行效率[^10],不過(guò)類(lèi)似的項(xiàng)目都沒(méi)有考慮跨集群的資源管理和網(wǎng)絡(luò)管理。
應(yīng)用場(chǎng)景
我們?cè)谶@一節(jié)將談?wù)?Kubernetes 中一些有趣的應(yīng)用場(chǎng)景,其中包括應(yīng)用分發(fā)方式的現(xiàn)狀、批處理調(diào)度任務(wù)以及硬多租戶(hù)在集群中的支持,這些是社區(qū)中比較關(guān)注的問(wèn)題,也是 Kubernetes 目前的盲點(diǎn)。
應(yīng)用分發(fā)
Kubernetes 主項(xiàng)目提供了幾種部署應(yīng)用的最基本方式,分別是 Deployment、StatefulSet 和 DaemonSet,這些資源分別適用于無(wú)狀態(tài)服務(wù)、有狀態(tài)服務(wù)和節(jié)點(diǎn)上的守護(hù)進(jìn)程,這些資源能夠提供最基本的策略,但是它們無(wú)法處理更加復(fù)雜的應(yīng)用。
圖 7 - Kubernetes 應(yīng)用管理
隨著 CRD 的引入,目前社區(qū)的應(yīng)用管理小組(SIG Apps)基本不會(huì)向 Kubernetes 主倉(cāng)庫(kù)引入較大的改動(dòng),大多數(shù)的改動(dòng)都是在現(xiàn)有資源上進(jìn)行的修補(bǔ),很多常見(jiàn)的場(chǎng)景,例如只運(yùn)行一次的 DaemonSet[^11] 以及金絲雀和藍(lán)綠部署等功能,現(xiàn)在的資源也存在很多問(wèn)題,例如 StatefulSet 在初始化容器中卡住無(wú)法回滾和更新[^12]。
我們可以理解社區(qū)不想在 Kubernetes 中維護(hù)更多的基本資源,通過(guò)幾個(gè)基本的資源可以覆蓋 90% 的場(chǎng)景,剩下的各種復(fù)雜場(chǎng)景可以讓其他社區(qū)通過(guò) CRD 的方式實(shí)現(xiàn)。不過(guò)作者認(rèn)為如果社區(qū)能夠在上游實(shí)現(xiàn)更多高質(zhì)量的組件,這對(duì)于整個(gè)生態(tài)都是很有價(jià)值并且很重要的工作,需要注意的是假如各位讀者想要在 Kubernetes 項(xiàng)目中成為貢獻(xiàn)者,SIG Apps 可能不是一個(gè)很好的選擇。
批處理調(diào)度
機(jī)器學(xué)習(xí)、批處理任務(wù)和流式任務(wù)等工作負(fù)載的運(yùn)行從 Kubernetes 誕生第一天起到今天都不是它的強(qiáng)項(xiàng),大多數(shù)的公司都會(huì)使用 Kubernetes 運(yùn)行在線(xiàn)服務(wù)處理用戶(hù)請(qǐng)求,用 Yarn 管理的集群運(yùn)行批處理的負(fù)載。
hadoop-yarn
圖 8 - Hadoop Yarn
在線(xiàn)任務(wù)和離線(xiàn)任務(wù)往往是兩種截然不同的作業(yè),大多數(shù)的在線(xiàn)任務(wù)都是無(wú)狀態(tài)的服務(wù),它們可以在不同機(jī)器上進(jìn)行遷移,彼此很難有極強(qiáng)的依賴(lài)關(guān)系;但是很多離線(xiàn)任務(wù)的拓?fù)浣Y(jié)構(gòu)都很復(fù)雜,有些任務(wù)需要多個(gè)作業(yè)一同執(zhí)行,而有些任務(wù)需要按照依賴(lài)關(guān)系先后執(zhí)行,這種復(fù)雜的調(diào)度場(chǎng)景在 Kubernetes 中比較難以處理。
在 Kubernetes 調(diào)度器引入調(diào)度框架之前,所有的 Pod 在調(diào)度器看來(lái)是沒(méi)有任何關(guān)聯(lián)的,不過(guò)有了調(diào)度框架,我們可以在調(diào)度系統(tǒng)中實(shí)現(xiàn)更加復(fù)雜的調(diào)度策略,例如保證一組 Pod 同時(shí)調(diào)度的 PodGroup[^13],這對(duì)于 Spark 和 TensorFlow 任務(wù)非常有用。
- # PodGroup CRD spec
- apiVersion: scheduling.sigs.k8s.io/v1alpha1
- kind: PodGroup
- metadata:
- name: nginx
- spec:
- scheduleTimeoutSeconds: 10
- minMember: 3
- ---
- # Add a label `pod-group.scheduling.sigs.k8s.io` to mark the pod belongs to a group
- labels:
- pod-group.scheduling.sigs.k8s.io: nginx
Volcano 也是在 Kubernetes 上構(gòu)建的批處理任務(wù)管理系統(tǒng)[^14],它能夠處理機(jī)器學(xué)習(xí)、深度學(xué)習(xí)以及其他大數(shù)據(jù)應(yīng)用,可以支持包括 TensorFlow、Spark、PyTorch 和 MPI 在內(nèi)的多個(gè)框架。
圖 9 - Volcano
雖然 Kubernetes 能夠運(yùn)行一些批處理任務(wù),但是距離在這個(gè)領(lǐng)域上取代 Yarn 等老牌資源管理系統(tǒng)上還有非常大的差距,相信在較長(zhǎng)的一段時(shí)間內(nèi),大多數(shù)公司都會(huì)同時(shí)維護(hù) Kubernetes 和 Yarn 兩種技術(shù)棧,分別管理和運(yùn)行不同類(lèi)型的工作負(fù)載。
硬多租戶(hù)
多租戶(hù)是指同一個(gè)軟件實(shí)例可以為不同的用戶(hù)組提供服務(wù),Kubernetes 的多租戶(hù)是指多個(gè)用戶(hù)或者用戶(hù)組使用同一個(gè) Kubernetes 集群,今天的 Kubernetes 還很難做到硬多租戶(hù)支持,也就是同一個(gè)集群的多個(gè)租戶(hù)不會(huì)相互影響,也感知不到彼此的存在。
硬多租戶(hù)在 Kubernetes 中是一個(gè)很重要、也很困難的課題,合租公寓就是一個(gè)典型的多租戶(hù)場(chǎng)景,多個(gè)租客共享房屋內(nèi)的基礎(chǔ)設(shè)施,硬多租戶(hù)要求多個(gè)訪(fǎng)客之間不會(huì)相互影響,你可以想象這有多么困難,Kubernetes 社區(qū)甚至有一個(gè)工作小組專(zhuān)門(mén)討論和研究相關(guān)的問(wèn)題[^15],然而雖然感興趣的工程師很多,但是成果卻非常有限。
圖 10 - 多租戶(hù)
盡管 Kubernetes 使用命名空間來(lái)劃分虛擬機(jī)群,然而這也很難實(shí)現(xiàn)真正的多租戶(hù)。多租戶(hù)的支持到底有哪些作用呢,這里簡(jiǎn)單列幾個(gè)多租戶(hù)帶來(lái)的好處:
Kubernetes 帶來(lái)的額外部署成本對(duì)于小集群來(lái)說(shuō)非常高昂,穩(wěn)定的 Kubernetes 集群一般都需要至少三個(gè)運(yùn)行 etcd 的主節(jié)點(diǎn),如果大多數(shù)的集群都是小集群,這些額外的機(jī)器會(huì)帶來(lái)很高的額外開(kāi)銷(xiāo);
Kubernetes 中運(yùn)行的容器可能需要共享物理機(jī)和虛擬機(jī),一些開(kāi)發(fā)者可能在公司內(nèi)部遇到過(guò)自己的服務(wù)被其他業(yè)務(wù)影響,因?yàn)橹鳈C(jī)上容器可能隔離了 CPU 和內(nèi)存資源,但是沒(méi)有隔離 I/O、網(wǎng)絡(luò) 和 CPU 緩存等資源,這些資源的隔離是相對(duì)困難的;
如果 Kubernetes 能夠?qū)崿F(xiàn)硬多租戶(hù),這不僅對(duì)云服務(wù)商和小集群的使用者來(lái)說(shuō)都是個(gè)福音,它還能夠隔離不同容器之間的影響并防止?jié)撛诎踩珕?wèn)題的發(fā)生,不過(guò)這在現(xiàn)階段還是比較難實(shí)現(xiàn)的。
總結(jié)
每個(gè)技術(shù)都有自己的生命周期,越底層的技術(shù)生命周期會(huì)越長(zhǎng),而越上層的技術(shù)生命周期也就越短,雖然 Kubernetes 是當(dāng)今容器界的扛把子,但是未來(lái)的事情沒(méi)有人可以說(shuō)的準(zhǔn)。我們要時(shí)刻清楚手中工具的優(yōu)點(diǎn)和缺點(diǎn),花一些時(shí)間學(xué)習(xí) Kubernetes 中設(shè)計(jì)的精髓,不過(guò)如果在未來(lái)的某一天 Kubernetes 也成為了過(guò)去,我們也應(yīng)該感到喜悅,因?yàn)闀?huì)有更好的工具取代它。
[^1]: Kubernetes and Container Security and Adoption Trends https://www.stackrox.com/kubernetes-adoption-security-and-market-share-for-containers/
[^2]: AWS Pricing Calculator https://calculator.aws/#/createCalculator/EC2
[^3]: Considerations for large clusters https://kubernetes.io/docs/setup/best-practices/cluster-large/
[^4]: How Microsoft drives exabyte analytics on the world’s largest YARN cluster https://azure.microsoft.com/en-us/blog/how-microsoft-drives-exabyte-analytics-on-the-world-s-largest-yarn-cluster/
[^5]: 備戰(zhàn)雙 11!螞蟻金服萬(wàn)級(jí)規(guī)模 K8s 集群管理系統(tǒng)如何設(shè)計(jì)?https://www.sofastack.tech/blog/ant-financial-managing-large-scale-kubernetes-clusters/
[^6]: sig-scalability-kubemark dashboard https://testgrid.k8s.io/sig-scalability-kubemark#kubemark-5000
[^7]: Node-local API cache #84248 https://github.com/kubernetes/kubernetes/issues/84248
[^8]: Multicluster Special Interest Group https://github.com/kubernetes/community/tree/master/sig-multicluster
[^9]: Cluster API Scope and Objectives https://github.com/kubernetes-sigs/cluster-api/blob/master/docs/scope-and-objectives.md
[^10]: Demystifying Kubernetes as a service – How Alibaba cloud manages 10,000s of Kubernetes clusters https://www.cncf.io/blog/2019/12/12/demystifying-kubernetes-as-a-service-how-does-alibaba-cloud-manage-10000s-of-kubernetes-clusters/
[^11]: Run job on each node once to help with setup #64623 https://github.com/kubernetes/kubernetes/issues/64623
[^12]: StatefulSet does not upgrade to a newer version of manifests #78007 https://github.com/kubernetes/kubernetes/issues/78007
[^13]: Coscheduling based on PodGroup CRD https://github.com/kubernetes-sigs/scheduler-plugins/tree/master/kep/42-podgroup-coscheduling
[^14]: Volcano · A Kubernetes Native Batch System https://github.com/volcano-sh/volcano
[^15]: Kubernetes Working Group for Multi-Tenancy https://github.com/kubernetes-sigs/multi-tenancy