我們將 Helm 用至極限,然後建立了一個 Kubernetes Operator

語言: CN / TW / HK

K8ssandra 是 Apache Cassandra®在 Kubernetes 上的一個發行版,由多個開源元件構建而成。從一開始直到最近的 K8ssandra 1.3版本 ,K8ssandra 一直使用 Helm 圖表進行安裝和管理。雖然該專案的某些元件使用了 Kubernetes Operators——包括 Cassandra(cass-operator)和 Medusa(medusa-operator),但還沒有一個 Operator 對所有元件進行整體管理。

K8ssandra 團隊最近完成了一個我們討論了幾個月的決定:為 K8ssandra 專案建立一個 Operator。在本文中,我們介紹了我們使用 Helm 的經驗,我們為 K8ssandra 建立 Operator 的決定,以及我們希望這將為專案帶來的好處。

背景

K8ssandra 的核心是 cass-operator ,我們使用它來部署 Cassandra 節點。我們圍繞它添加了一系列元件,組成一個生態系統,用於在 Kubernetes 中有效地執行 Cassandra。這些元件包括用於管理反熵修復(Reaper)和備份(Medusa)的操作工具。我們引入了用於指標收集和報告的 Prometheus/Grafana 技術棧。Stargate 則是一個數據閘道器,通過 REST、GraphQL 和 Document API 提供了對 Cassandra 更靈活的訪問。

一開始,我們使用 Helm 來幫助管理這些元件的安裝和配置。這使我們能夠快速啟動專案並開始組建社群。最初對該專案感興趣的人主要是 Cassandra 社群的開發人員,他們不一定有很多 Kubernetes 的專業知識和經驗。他們中的許多人發現掌握像 Helm 這樣的包管理工具和安裝程式比掌握 Operator 和 CRD(定製資源定義)更容易。這並不是說 Helm 是為“不太瞭解 Kubernetes 的人”準備的,因為 Kubernetes 生態的很大一部分都在使用 Helm。

進展:Helm 的優缺點

隨著專案的發展,我們開始在 Helm 上遇到一些限制。雖然正確安裝 K8ssandra 叢集非常簡單,但我們在升級和管理叢集時遇到了比較多的問題。

編寫複雜的邏輯

Helm 通過迴圈和 if 語句很好地支援控制流。然而,當巢狀層次比較多時,整個程式碼就很難理解和閱讀,而且縮排也成為一個問題。特別是,我們發現對修改後的 Helm 圖表進行同行評審變得相當困難。

重用和可擴充套件性

Helm 變數的作用範圍被限制在宣告它們的模板內。例如,我們在 Cassandra 資料中心模板中定義了一個變數,在 Stargate 模板中不可能重用它,我們必須在 Stargate 模板中重新建立相同的變數。這使得我們的程式碼很難保持 DRY原則 ,我們發現這是缺陷的來源。

類似地,Helm 有一個很好很大的幫助模板函式庫,但是這個庫並沒有涵蓋所有用例,並且沒有介面來定義您自己的函式。您可以定義自己的模板,模板可以被大量重用,但它們不能代替函式。

專案結構和繼承

傘形圖設計模式是 Helm 的最佳實踐,但我們在嘗試實現該模式時也遇到了困難。我們能夠建立一個頂級 K8ssandra Helm 圖表,其中包含 Cassandra 和 Prometheus 的子圖表,但當我們試圖為 Reaper 和 Stargate 建立額外的子圖表時,卻遇到了變數作用範圍的問題。我們的目的是僅僅在頂級圖表定義身份驗證設定,這樣它們不僅可以應用於 Cassandra,還可以應用於 Stargate 和 Reaper。Helm 的繼承模型不支援這種將變數向下推到子圖表的概念。

定製資源定義(CRD)管理

Helm 可以建立 Kubernetes 的定製資源定義(CRD),但不能管理它們。我們知道這是 Helm 開發者為 Helm 3 做出的深思熟慮的設計選擇。由於定製資源的定義是叢集範圍的,如果多個 Helm 安裝過程試圖在不同版本的 CRD 上工作可能會帶來一些混亂。然而,這給我們帶來了一些困難。為了管理資源的更新——比如 Helm 內部的 Cassandra 資料中心,我們必須實現一個變通方案。我們實現了定製的 Kubernetes job,並將它們標記為升級前的鉤子(Hook),這樣 Helm 就可以在升級時執行它們。每個 job 都用 Go 語言編寫,並打包成一個映象。這本質上就像編寫迷你控制器,並且在某種程度上開始感覺像編寫 Operator。

臨界點:多叢集部署

雖然我們已經能夠通過 1.3 版本解決這些 Helm 的問題,但我們路線圖上的下一個主要特性是實現多叢集 K8ssandra 部署(跨越多個 Kubernetes 叢集的 K8ssandra/Cassandra 叢集)。我們意識到,即使沒有複雜的網路配置,我們也無法使用 Helm 有效實現這一步。

設定新方向

最後,我們意識到我們讓 Helm 做得太多了。很容易陷入這樣的情況:您學會了如何使用錘子,所有東西看起來都像釘子,但您真正需要的是螺絲刀。

結果,我們發現我們與 Operator框架 的建立者有一些共同點,他們已經為 Operator 定義了一個 功能模型 ,我們將其展示在這裡:

如圖所示,Helm 最適合 Operator 前兩個級別的功能,側重於簡單的安裝和升級。執行更復雜的操作如故障處理和恢復、自動伸縮,以及更復雜的安裝和升級應該用諸如 Ansible 或 Go 之類的程式語言來實現,而不是使用像 Helm 這樣的模板語言。

構建一個 Operator:K8ssandra 2.0

基於這一分析,團隊決定開始構建一個 Operator,我們將其稱之為 K8ssandra 2.x 系列版本。2.0 版本的首要任務是移植我們在 Helm 圖表中已有的功能,確保 Operator 具有相同的特性,並在其中增加多叢集支援。我們仍然打算解決 1.x 版本中的 bug 或漏洞,但我們正試圖將所有主要的新功能都集中在 Operator 上。

Helm 仍有一席之地

在工具方面,我們不認為 Helm 和 Operator 是相互排斥的。這兩種方法是互補的,我們需要根據其優勢來使用每一種方法。我們將繼續使用 Helm 執行基本的安裝操作,包括安裝 Operator 以及設定 Cassandra 和其他元件使用的管理員服務帳號(Administrator Service Account)。這些都是 Helm 這樣的包管理器最擅長的功能。

Operator 設計和實現的選擇

在 K8ssandra Operator 的設計和實現中,我們做出了幾個關鍵的選擇。

模組化設計

雖然 Reaper Operator、Medusa Operator 和 Stargate Operator 有單獨的倉庫,但我們計劃將它們合併到 K8ssandra Operator 中。K8ssandra Operator 將在單個 pod 中執行,但將包含與每個 CRD 對應的多個控制器。我們將會有多個 CRD 和多個控制器。因為 cass operator 已經被獨立使用,所以它仍將是獨立的,並將成為 K8ssandra Operator 的一個依賴項。

雖然目前這不是微服務架構,但它是松耦合和模組化的,所以未來如果需要,我們可以將控制器重新打包為獨立的微服務。

基於 Operator SDK 使用 Go 語言開發

我們決定基於 Operator SDK 使用 Go 語言編寫 K8ssandra Operator。對於我們來說,這是一個簡單的選擇,因為我們已經從開發 cass-operator 中熟悉了它。我們相信使用像 Go 語言這樣的全功能程式語言會比使用 YAML 模板更有吸引力,並有助於吸引新的貢獻者加入專案。這還將使我們能夠使用該語言的全部功能。例如,Go 可以很容易地建立易於重用的輔助函式。

K8ssandra 叢集級狀態

新的 K8ssandra 叢集 CRD 有一個狀態欄位,可以讓您大致瞭解叢集的狀態,包括是否已經就緒(ready)、尚未就緒(not ready)、正在初始化(initializing)等等。該狀態將彙總組成叢集的所有物件的健康狀況,包括 Cassandra 叢集、Stargate、Reaper 和其他任何部署在其中的物件,而這不是 Helm 可以做到的。

與 Kubernetes 的方式更加一致

我們為每個定製資源開發控制器的設計方法與 Kubernetes 中管理資源的標準方法更加一致。例如,我們有一個特定的啟動順序,我們想定義如下規則:在 Cassandra 初始化之前不啟動 Stargate。開箱即用的 Helm 無法實現這一功能。我們必須在 Stargate pod 中新增一個初始化容器,以執行叢集啟動和執行的基本檢查。有了新的 Operator,Stargate 可以檢查 Cassandra 資料中心 CRD 的狀態變化。當它被調協器(reconciliation)觸發執行,它查詢獲得 Cassandra 資料中心的狀態,一旦其狀態變為就緒(ready),Operator 就將部署 Stargate。

測試覆蓋率

這種方式也將改進測試。有很多可用的測試覆蓋工具,例如我們正在使用的 SonarCloud。然而,我們不能將 SonarCloud 與 Helm 模板一起使用。所以我們現在沒有一個好的方法來衡量測試中的覆蓋水平,而且 IDE 的支援也不像對靜態語言那麼好。

我們仍在研究的事情

在開發 Operator 的過程中,我們還在繼續探索和學習一些領域。

加速迭代開發

Helm 模板非常適合快速迭代,但 Operator 的開發步驟更復雜。在修改 Operator 程式碼之後,我們必須重新構建 Operator 映象並部署它,然後部署 Operator 管理的定製資源,以便它隨後生成 Deployment 物件,然後我們就可以驗證部署了。這個過程涉及更多步驟,所以我們正在尋求改進方法,使其更加自動化。

多叢集整合測試

測試多叢集 K8ssandra 部署存在一些挑戰。到目前為止,我們已經能夠使用 GitHub Actions 進行大多數持續整合測試(使用免費的 tier runner),但我們發現這在涉及多叢集資源的時候是不夠的。

我們正在調研的整合測試工具之一是 Kuttl 。使用 Kuttl,測試用例和預期結果都在 YAML 檔案中描述,這意味著您不必是 Go 語言或 Kubernetes API 的專家也可以貢獻測試。我們相信這會讓開發人員更容易參與測試並立即做出貢獻,然後如果他們願意,可以按照自己的節奏開始使用 Go。

您應該使用 Operator 嗎?您應該開發一個 Operator 嗎?

如果您已經讀到這裡,您可能想知道這對您自己的專案的影響。如果您在 Kubernetes 中使用資料庫或其他基礎設施,那麼使用 Operator 儘可能自動化的操作工作負載肯定是有意義的。

如果您為資料基礎設施供應商工作,或者為開源資料基礎設施專案做出貢獻,您可能會好奇何時應該開始構建 Operator。我們在自己的過渡過程中進行了很多思考,特別是在時間安排和對使用者的影響方面。最終我們的建議是:您在很多情況下發現您所使用的工具對您不利,而不是對您有利,此時可能是時候考慮不同的解決方案了。

建立社群

我們現在看到對 K8ssandra 的貢獻有所增加,尤其是在問題建立方面。現在我們已經開始在 Operator 的發展中獲得動力,不斷增長的使用者社群幫助我們認識到為了加快產品成熟而需要做的事情,這是擁有這樣一個社群的巨大好處。

我們還想繼續加強程式碼貢獻團隊。如果您有興趣在 Kubernetes 上執行 Cassandra 或構建 Operator,我們很樂意讓您成為 K8ssandra 專案的一員。您可以檢視我們的網站,也可以在 論壇 或我們的 Discord 服務上提出任何問題。

原文連結 (This article initially appeared on  The New Stack