這兩個原因,使Kubernetes變得如此復(fù)雜
1、為什么Kubernetes這么難?
Anthropic在Kubernetes內(nèi)運行我們的大部分系統(tǒng),因此我對該工具積累了更多的經(jīng)驗,對其也更加熟悉。雖然在它真的很棒,但我也確實經(jīng)歷了(大家會普遍經(jīng)歷的)其復(fù)雜性和調(diào)試的超高難度。
雖然在學習新系統(tǒng)時,這些感覺相當普遍,但Kubernetes確實比我使用過的其他一些系統(tǒng)感覺更大、更可怕、更棘手。在學習并使用它的過程中,我試圖理解為什么它看起來是這樣的,以及哪些設(shè)計決策和權(quán)衡導致它成為現(xiàn)在這樣。這篇文章嘗試寫出兩種特定的想法,并會解釋為什么與Kubernetes一起工作有時會感到棘手。
2、Kubernetes是一個集群操作系統(tǒng)
大家很容易將Kubernetes視為部署容器化應(yīng)用程序或一些類似功能描述的系統(tǒng)。雖然這可能是一個有用的視角,但我認為將Kubernetes視為通用集群操作系統(tǒng)內(nèi)核會更合理。這兩者之間有何區(qū)別?
傳統(tǒng)操作系統(tǒng)的工作是使用一臺計算機及其所有附屬硬件,并公開程序可用于訪問該硬件的接口。雖然確切的細節(jié)各不相同,但通常這個界面有以下幾個目標:
1)資源共享——我們希望將一臺物理計算機的資源細分到多個程序中,以便在某種程度上相互隔離。
2)可移植性——我們希望在某種程度上抽象底層硬件的精確細節(jié),以便同一程序可以在不同的硬件上運行,而無需修改或僅進行小幅修改。
3)通用性——當我們想出新型硬件或?qū)⑿掠布迦胗嬎銠C時,我們希望能夠以增量的方式將這些硬件放入我們的抽象和接口中,建議不要大幅更改任何接口或破壞任何不使用該硬件的現(xiàn)有軟件。
4)整體性——與通用性相關(guān),我們希望操作系統(tǒng)調(diào)解對硬件的所有訪問:軟件幾乎不可能完全繞過操作系統(tǒng)內(nèi)核。軟件可以使用操作系統(tǒng)內(nèi)核設(shè)置與硬件的直接連接,以便未來的交互直接發(fā)生(例如設(shè)置內(nèi)存映射的命令管道),但初始分配和配置仍在操作系統(tǒng)的保護下。
5)性能——與“直接編寫一個專用軟件,它直接運行在硬件上,并且對硬件有獨占的直接訪問權(quán)”相比,我們希望為擁有這種抽象支付可接受的小性能成本。在某些情況下,我們希望通過提供I/O調(diào)度器或緩存層等優(yōu)化,在實踐中實現(xiàn)比此類系統(tǒng)更高的性能。
雖然“編程的便捷性”通常是一個額外的目標,但在實踐中,它往往因為上述擔憂而被忽視。操作系統(tǒng)內(nèi)核通常圍繞上述目標進行設(shè)計,然后編寫用戶空間庫,將低級、通用、高性能的接口封裝到更易于使用的抽象中。操作系統(tǒng)開發(fā)人員更關(guān)心“在我的操作系統(tǒng)上運行nginx有多快”,而不是“nginx端口到我的操作系統(tǒng)的代碼能短多少行?”
我認為Kubernetes在一個非常相似的設(shè)計空間中運行;然而,它的目標不是抽象單個計算機,而是抽象整個數(shù)據(jù)中心或云,或其中的很大一部分。
這種觀點有幫助的原因是,這個問題比“使在容器中部署HTTP應(yīng)用程序成為可能”更困難,也更普遍,它指出了Kubernetes如此靈活的具體原因。Kubernetes渴望足夠通用和強大,可以在任何類型的硬件(或虛擬機實例)上部署任何類型的應(yīng)用程序,而無需“繞過”或“跳開”Kubernetes接口。
我不會試圖在這里就它是否實現(xiàn)了這個目標(或者,它在實踐中是否實現(xiàn)了這個目標)發(fā)表意見;只需將它視為一個要解決的問題,就能理解所遇到的許多設(shè)計決策,這樣的視角是可行的。
從這個角度來看,Kubernetes的可插拔性和可配置性可能是比較大的設(shè)計選擇。一般來說,不可能做出對所有人都適用的選擇,特別是你希望在沒有高昂的性能成本的情況下做出選擇。特別是在現(xiàn)代云環(huán)境中,部署的應(yīng)用程序類型和硬件類型差異很大,是變化速度非常快的目標。因此,想成為所有人的萬能工具,你最終需要高度可配置性,這最終會創(chuàng)建一個強大的系統(tǒng),但這個系統(tǒng)可能很難理解,甚至使“簡單”任務(wù)變得復(fù)雜。
當然,還有另一個視角:
許多用戶認為Kubernetes本質(zhì)上是“Heroku”,也就是說,Kubernetes本質(zhì)上是一個部署應(yīng)用程序的平臺,抽象了大多數(shù)傳統(tǒng)的底層操作系統(tǒng)和分布式系統(tǒng)的細節(jié)。
Kubernetes認為自己解決了更接近“CloudFormation”的問題——在某種意義上,它希望足以定義您的整個基礎(chǔ)設(shè)施—它也試圖以比底層云提供商或硬件通用的方式做到這一點。
3、Kubernetes中的所有內(nèi)容都是一個控制循環(huán)
大家可以想象一個十分必要的“集群操作系統(tǒng)”,上文中,它暴露了“分5個CPU的計算能力”或“創(chuàng)建新的虛擬網(wǎng)絡(luò)”等原語,這些原語反過來又支持系統(tǒng)內(nèi)部抽象中的配置更改或?qū)C2 API(或其他底層云提供商)的調(diào)用。
Kubernetes作為核心設(shè)計決策,并不是這樣工作的。相反,Kubernetes做出了核心設(shè)計決策,即所有配置都是聲明性的,所有配置都是通過作為控制回路的“操作員”實現(xiàn)的:他們不斷將所需的配置與現(xiàn)實狀態(tài)進行比較,然后試圖采取行動使現(xiàn)實與所需的狀態(tài)保持一致。
這是一個非常深思熟慮的設(shè)計選擇,而且是有充分理由的。一般來說,任何不是設(shè)計為控制回路的系統(tǒng)都會不可避免地偏離預(yù)期的配置,因此,在規(guī)模上,需要有人編寫控制回路。通過內(nèi)部化它們,Kubernetes希望允許大多數(shù)核心控制循環(huán)只編寫一次,并由領(lǐng)域?qū)<揖帉?,從而更容易在它們之上?gòu)建可靠的系統(tǒng)。
對于一個本質(zhì)上是分布式、為構(gòu)建分布式系統(tǒng)而設(shè)計的系統(tǒng)而言,這也是一個自然的選擇。分布式系統(tǒng)的定義本質(zhì)是局部故障的可能性,這要求超過一定規(guī)模的系統(tǒng)能夠自我修復(fù),并在不考慮局部故障的情況下收斂到正確的狀態(tài)。
然而,這種設(shè)計選擇也帶來了產(chǎn)生巨大的復(fù)雜性和混亂的可能性。以下為兩個具體的例子:
1)錯誤被延遲。在Kubernetes中創(chuàng)建對象(例如pod),通常只需在配置存儲中創(chuàng)建一個對象,斷言該對象的預(yù)期存在。如果事實證明無法實際滿足該請求,要么是因為資源限制,要么是因為對象在某些方面內(nèi)部不一致(引用的容器映像不存在),通常不會在創(chuàng)建時發(fā)現(xiàn)該錯誤。配置創(chuàng)建將會進行,然后,當相關(guān)操作符喚醒并嘗試實現(xiàn)更改時,才會創(chuàng)建錯誤。
這種間接性使調(diào)試和推理變得更加困難,因為你不能用“創(chuàng)建成功”作為“生成的對象存在”的良好標記。這也意味著與失敗相關(guān)的日志消息或調(diào)試輸出不會出現(xiàn)在創(chuàng)建對象的流程的上下文中。
編寫良好的控制器會發(fā)出Kubernetes事件來解釋正在發(fā)生的事情,或以其他方式注釋有問題的對象;但對于測試較差的控制器或更罕見的故障,您可能只會在控制器自己的日志中獲取日志垃圾郵件。一些更改可能涉及多個控制器,會獨立或聯(lián)合行動,這使得跟蹤某一段代碼變得更加困難。
2)運算符可能有漏洞。聲明性控制環(huán)模式提供了隱含的承諾,即您作為用戶無需擔心如何從狀態(tài)A到狀態(tài)B;您只需將狀態(tài)B寫入配置數(shù)據(jù)庫,然后等待。當它運行良好時,這實際上是一個巨大的簡化。
然而,有時候從狀態(tài)A到狀態(tài)B是不可能的,即使狀態(tài)B可以自己實現(xiàn)?;蛟S這是可能的,但需要停機時間。雖然這是可能的,但這是一個罕見的用例,所以控制器的作者忘了實現(xiàn)它。
對于Kubernetes中的核心內(nèi)置原語,您可以保證它們經(jīng)過良好的測試和使用,并希望它們能很好地工作。但當您開始添加第三方資源、管理TLS證書或云負載平衡器或托管數(shù)據(jù)庫或外部DNS名稱時,您會偏離常規(guī),所有路徑的測試效果會變得不那么清楚。
而且,與之前關(guān)于延遲錯誤的要點一致,故障模式很微妙,而且發(fā)生在較遠的位置;很難區(qū)分“更改尚未被接收”和“更改永遠不會被接收”之間的區(qū)別。
4、結(jié)論
本文一直試圖避免就這些設(shè)計決策的好壞做出價值判斷。因為關(guān)于Kubernetes何時成為什么樣的有價值的系統(tǒng)才是有意義的。
我發(fā)現(xiàn)以自己的方式對Kubernetes有很好的理解,并更好地理解其復(fù)雜性來自哪里,以及它正在服務(wù)的目標,這是非常有價值的。
這種分析可以應(yīng)用于現(xiàn)在使用的任何系統(tǒng)。即使一個系統(tǒng)的設(shè)計方式在當前的環(huán)境中并不理想,但出于某種原因,它總是以這種方式出現(xiàn)。只要這是一個你必須與之互動、推理和決策的系統(tǒng),如果你能理解這些原因、動機和將系統(tǒng)推向這一點的內(nèi)部邏輯,而不是立即將其忽視,則會有更好的使用體驗。
希望這篇文章能幫助其他對在生產(chǎn)中使用Kubernetes不熟悉、或正在考慮采用Kubernetes的人,幫助大家提供一些有用的框架來解釋為什么(我相信)它看起來的樣子,以及對它有什么合理的期望。
如果我們想更細致入微,我們可以認為它預(yù)先加載復(fù)雜性,而不是添加復(fù)雜性。這種設(shè)計讓你預(yù)先處理可能長期忽視的實際問題。這是否是一個理想的選擇取決于您的目標、規(guī)模、時間范圍和相關(guān)因素。