針對 Kubernetes 軟體棧有狀態服務設計的思考
本文關鍵點
- 大多數在 Kubernetes 之上構建應用程式的開發人員仍然主要依賴於無狀態協議和設計。問題是,只關注無狀態設計會忽略分散式系統中最難的部分:管理狀態資料。
- 挑戰不是設計和實現服務本身,而是服務之間的空間管理:資料的一致性保證、可靠的通訊、資料的複製和故障轉移、元件故障的檢測和恢復、分片、路由、共識演算法等等。
- 自 Kubernetes 和 Akka 在應用程式軟體棧各自負責不同的層和功能以來,兩者就可以很好地協作了。Kubernetes 允許粗粒度的容器級彈性和可伸縮性管理。Akka 允許細粒度的實體級彈性和可伸縮性管理。
- Akka 在過去兩年中發展迅速。如今,它的月下載量約為 500 萬次,而兩年前是 50 萬次。
在今年夏天在紐約舉行的 QCon 大會上,Jonas Bonér 發表了會議上最受歡迎的演講之一,他專注於設計事件優先的微服務。在 InfoQ 的問答中,我們請 Bonér 解釋“從整體大型系統設計中引入壞習慣”對於服務設計來說是一條沒有出路的路,他認為自己的 Akka 框架適用於雲端計算本地棧。
InfoQ:在 QCon 上,您說過,“當開始微服務之旅時,應該注意不要最終弄成一堆緊耦合的‘微型獨體應用( ofollow,noindex">microliths )’,因為你可能會從大型獨體應用的設計中將一些壞習慣帶到微服務中,使服務之間產生非常緊密的耦合。”,您可以進一步幫我們解釋一下嗎?
Jonas Bonér:在雲本地應用程式開發領域,我仍然看到了對無狀態協議(通常是同步協議)的強烈依賴。
大多數在 Kubernetes 之上構建應用程式的開發人員仍然主要依賴於 無狀態協議 和設計。
它們包含容器,並且常常保留舊的體系結構、設計、習慣、模式、實踐和工具——這些都是為執行在強大的 RDBMS 之上的單節點系統的整體世界而設計的。
問題是,只關注無狀態設計會忽略分散式系統中最難的部分:管理狀態資料。
忽略最困難的部分,把它的責任從應用程式層中剝離出來,這聽起來可能是個好主意。有時確實如此,然而,隨著應用程式越來越以資料為中心和資料驅動,通過一種高效、效能和可靠的方式管理、處理、轉換和豐富接近應用程式本身的資料,從而獲得資料的所有權比以往任何時候都更加重要。
許多應用程式無法為每次資料訪問或儲存支付往返資料庫的費用,它們需要近實時地連續處理資料,從永無止境的資料流中挖掘知識。為了可伸縮性、低延遲和吞吐,這些資料在儲存之前通常還需要以分散式方式進行處理。
InfoQ:有狀態服務一直被認為是主流容器採用的最大障礙。告訴我們為什麼這是一個如此棘手的領域?
Bonér:將容器視為邏輯上相同的單元(這些單元可以被替換、啟動和移動)的策略非常適合於無狀態服務,無需太多思考,但是與您想要管理分散式有狀態服務和資料庫的方式相反。
首先,有狀態例項並不是簡單可替換的,因為每個例項都有自己需要考慮的狀態。
其次,有狀態副本的部署通常需要在副本之間進行協調,比如引導依賴順序、版本升級、模式更改等等。
第三,複製需要時間,從複製開始的機器將承受比平時更大的負載,因此,如果在負載下啟動一個新的副本,實際上可能會導致整個資料庫或服務崩潰。
解決這個問題的一種方法,是將狀態管理委託給 Kubernetes 叢集之外的雲服務或資料庫。也就是說,如果我們想使用 Kubernetes 以統一的方式管理您的所有基礎設施,那麼我們該怎麼做呢?
此時,Kubernetes 對執行非雲本地的有狀態服務的問題的回答是 StatefulSet 的概念,它確保每個 pod 都有一個穩定的標識和專用磁碟,這些磁碟在重啟時(甚至在重新排程到另一臺物理機器之後)會保留資料。因此,現在在 Kubernetes 上部署分散式資料庫、流資料管道和其他有狀態服務是可能的(儘管仍然很有挑戰性)。
我們需要的是新一代的工具,這些工具允許開發人員構建真正的雲本地有狀態服務,這些服務只具有 Kubernetes 提供給無狀態服務的基礎設施需求。這並不是反對使用像 Kubernetes 和 istia 這樣的低階基礎設施工具(它們顯然帶來了大量的價值),而是呼籲基礎設施和應用程式層之間進行更緊密的協作,以維護整體的正確性和安全性保證。
InfoQ:請告訴我們 Akka 適用於 Kubernetes 軟體棧有狀態服務需求的地方。
Bonér:實際上,困難的部分不是設計和實現服務本身,而是管理服務之間的空間。所有的困難在於:資料的一致性保證、可靠的通訊、資料的複製和故障轉移、元件故障的檢測和恢復、分片、路由、協商一致性演算法等等。把所有這些都縫合在一起,並長期保持,你自己做是非常非常困難的。
端到端正確性、一致性和安全性對於不同的服務意味著不同的東西,它們完全依賴於用例,不能完全外包給基礎設施。我們需要的是雲端計算的程式設計模型,搭配一個可以做繁重工作的執行時,讓我們專注於商業價值的構建,而不是陷入網路程式設計的複雜性和故障模式,我相信 Akka 搭配 Kubernetes 就是可以成為這樣的解決方案。
Akka 是一個開源專案,創建於 2009 年,旨在成為分散式系統和雲端計算的結構和程式設計模型。
Akka 在最真實的意義上是雲本地的,在“雲本地”一詞出現之前,它構建出來直接在雲中執行。
Akka 基於 Actor 模型,並建立在 Reactive Manifesto 中概述的原則之上,該宣言將 Reactive 系統定義為一組架構設計原則,這些原則旨在滿足當今和未來系統所面臨的需求。
在 Akka 中,工作和狀態單元稱為參與者,可以看作是有狀態、容錯、隔離和自治的元件或實體。就資源而言,這些參與者 / 實體是非常輕量級的,您可以在一臺機器上輕鬆地同時執行數百萬個參與者 / 實體,並使用非同步訊息傳遞進行通訊。它們具有自動自修復的內建機制,預設情況下是可分發和位置透明的。這意味著它們可以根據需要在叢集中伸縮、複製和移動,針對應用程式的使用方式作出反應,這對參與者 / 實體的使用者來說是透明的。
InfoQ:那麼當 Kubernetes 和 Akka 一起使用時,它們之間在哪裡做關注點的分離呢?
Bonér:一種方法是,Kubernetes 非常擅長管理和編排軟體的“盒子”(容器),但是管理這些盒子只會讓你走到一半。同樣重要的是你在盒子裡放了什麼,這是 Akka 可以幫助你的。
Kubernetes 和 Akka 結合得非常好,它們各自負責應用程式軟體棧中的不同層和功能。Akka 是編寫應用程式的程式設計模型,它支援執行時,幫助管理業務邏輯、資料一致性和完整性、操作語義、
分散式和本地工作流和通訊、與其他系統的整合等。Kubernetes 是一種運維工具,以統一的方式管理大量的容器例項,幫助管理容器的生命週期、容器的版本控制和分組、容器間的路由通訊、管理容器之間的安全性、身份驗證和授權等。
從本質上說,Kubernetes 的工作是為應用程式提供足夠的計算資源,將外部流量傳送到應用程式中的某個節點,並管理諸如訪問控制之類的東西;而 Akka 的工作則是決定如何在給予它的所有計算資源中分配工作。
InfoQ: Akka 採用了一種“讓它崩潰”的方法,失去一個參與者並不重要,因為另一個參與者會接手工作。您能解釋一下它在容器環境中是如何工作的嗎?管理員需要進行干預嗎?
Bonér:傳統的基於執行緒的程式設計模型只提供一個控制執行緒,所以如果這個執行緒異常崩潰,您就有麻煩了。這意味著您需要在這個執行緒中顯式地處理所有錯誤。異常不會線上程之間傳播,也不會在網路中傳播,因此甚至無法發現有什麼失敗。但是丟失執行緒,或者在最壞的情況下,丟失整個容器是非常昂貴的。更糟糕的是,使用同步協議可能導致這些故障跨整個應用程式產生級聯效應。我們可以做得更好。
在 Akka 中,你在所謂的“主管層次結構”中設計應用程式,參與者在其中監視彼此的健康狀況並管理彼此的失敗。如果一個參與者失敗了,它的錯誤將被隔離幷包含在其中,並被具體化為一條訊息,該訊息將被非同步傳送(如果需要的話,將跨網路傳送)給它的監督參與者,後者可以在一個安全健康的上下文中處理失敗,並根據宣告定義的規則自動重啟失敗的參與者。這自然會產生一種非防禦性的程式設計方式和一種快速失敗(和恢復)的方法,也稱為“讓它崩潰”。
這聽起來可能與 Kubernetes 的角色重疊,確實 Kubernetes 和 Akka 都有助於管理彈性和可伸縮性,但是在應用程式軟體棧中具有不同的粒度級別。你還可以從細粒度與粗粒度彈性和可伸縮性的角度來研究這兩種技術。
Kubernetes 允許對彈性和可伸縮性進行粗粒度容器級別的管理,其中將容器作為一個整體進行復制、重新啟動或伸縮。Akka 允許對彈性和可伸縮性進行細粒度的實體級管理(與應用程式緊密協作),其中每個服務本身就是一組實體副本,這些副本在需要時被複制、重新啟動和伸縮,由 Akka 執行時自動管理,而不需要運維人員或 Kubernetes 進行干預。
InfoQ:您能給我們介紹一下 Akka 的應用嗎?你現在每月能看到有多少次下載?
Bonér:Akka 在過去兩年中發展迅速。如今,它的月下載量約為 500 萬次,而兩年前是 50 萬次。如果您對專案的一些早期歷史和里程碑感興趣,這張 資訊圖 中有一些很酷的資料點。
關於受訪者
Jonas Bonér是 Lightbend 的創始人和 CTO,他是 Akka 專案的創造者、響應宣言的發起者和合著者,也是 Java 的擁護者。
檢視英文原文: Stateful Service Design Considerations for the Kubernetes Stack