Kubernetes 資源編排系列之二: Helm 篇

作者 凌可(彭蘭舒) 雪堯(郭耀星)

這是我們的**《Kubernetes 資源編排系列》**的第二篇——Helm 篇,在上篇(《Kubernetes 資源編排系列之一: Pod YAML 篇》,可從文末鏈接直達)我們見識到了 Pod YAML 的強大能力,在 k8s 的集群中,所見之處皆是 YAML。YAML 多了之後,大家就希望有一種方案能將海量的 YAML 管理起來。於是本篇我們來介紹一下 Helm。

Helm 是什麼

Helm 是 Kubernetes 生態系統中的一個軟體包管理工具,類似於 Ubuntu 中的 apt、CentOS 中的 **yum **等,它用於自動創建、打包、配置和部署應用程式和服務到 Kubernetes 集群。

Kubernetes 為用戶提供了自動部署、擴展和管理容器化應用程式的能力,然而用戶部署在 Kubernetes 集群上的應用可能會非常複雜,一個典型的應用(以 **SREWorks **為例)通常會有一系列對象進行內部交互來使得程式正常運轉。

首先我們需要 Deployment 用於部署我們想要運行的 pods,例如 Mysql 資料庫;StorageClass 用於動態分配存儲卷;Persistent Volume(PV)用於儲存資料庫;Service 用於指定 pod 的訪問策略使得外部可以訪問 pod 的服務;Job 用於執行任務保證指定數量的 pods 部署成功……

在 Helm 出現之前,每個對象都需要一個**單獨的 **YAML 文件來配置,然後通過kubeclt apply逐一創建這些對象。如果需要對其中的一些內容進行更改,必須找到對應的 yaml 文件並在其中找到對應的屬性;而如果想要升級其中的一些組件,也要仔細地編輯多個文件,防止錯誤和遺漏;當要刪除這個應用程式時,也要手動刪除其對應的所有組件……

Kubernetes 並不是從**「應用程式」**的層面來管理集群中的各個組件,它只是將用戶提交上去的 yaml 保存在集群中並各自運行,實際上它並不知道用戶聲明的各個對象,例如 Job、Service、StorageClass、Deployment 等,是同一個應用程式下不同的組成成分。當應用較為複雜的時候,要記住變數的位置並人工執行操作是一件冗餘且複雜的事,並且會給多人協作開發應用增加難度。

在這樣的基礎上,Helm 應運而生,為開發人員提供了模組化的應用管理工具,降低了 Kubernetes 用戶的工作量和工作複雜度。

Helm 是怎麼做的?

Helm 將上述對象都看作一個程式包內的部分, 當我們想要對程式執行操作的時候,不需要告訴 helm 我們要對哪個對象進行變更,只用傳入程式名稱,Helm 會幫助我們對程式下的每個對象執行相應的操作。

這個包含了一組 yaml 文件的程式包,就叫做 Helm Chart

Chart 是一個描述 Kubernetes 相關資源的文件集合,它的格式如下:

└── sreworks-chart/           
     ├── Chart.yaml    # 文件包含了對該chart的描述           
     ├── values.yaml   # 文件包含了導入模版中的chart的默認值,會在用戶執行helm install或helm upgrade時被覆蓋           
     ├── charts/       # 目錄包含依賴的其他chart           
     ├── templates/    # 目錄包括了模板文件           
     └── ...

開發時通常不會將 name 硬編碼在資源中,用戶可以通過插入發布名稱來生成 name 欄位。

所以我們的 job.yaml 就變為了:

apiVersion: v1
kind: Job
metadata:    
   name: {{ .Release.Name }}-init-job

模板命令{{ .Release.Name }} 將發布名稱注入了模板。值作為一個命名空間對象傳給了模板,用點(.)分隔每個命名空間的元素。

在執行helm install命令時,模版渲染引擎會將括在{{ }} 之間的模版命令替換為 values.yaml 中定義的值或用戶通過--set設置的值,並將渲染後的文件發送到templates目錄中,然後收集結果並發送給 Kubernetes。

可以通過 helm 命令對 chart 執行相應的操作:

// 安裝
$ helm install sreworks . -n sreworks、

// 升級
$ helm upgrade sreworks . -n sreworks

// 回滾
$ helm rollback sreworks -n sreworks

// 卸載
$ helm uninstall sreworks -n sreworks

還能用 Helm 創建自己的 charts,也可以從 Helm 倉庫中下載。

社區的 Helm Chart 倉庫位於 Artifact Hub,可以下載其他人上傳的 chart 到本地使用,也可以將自己製作的 chart 上傳到倉庫中。同時 Helm 也支援創建並運行私有倉庫,供個人和組織內部使用。

Helm 的優點

  • 生命周期管理:可以實現對組件實例的查詢、安裝、卸載、升級、回滾
  • 方便的命令行:對於簡單變數,可以在部署的同時指定對應的參數,方便部署
  • 插件和工具生態:作為 CNCF 項目,Helm 已經變成了 K8S 基礎生態的一部分,各種各樣的外部系統都會對它進行默認支援,CICD 工具集成方面有得天獨厚的優勢;同時用戶能夠從社區中獲取豐富的專業知識和共享的 Chart 包
  • 確保 Secret 安全性:一些敏感數據在 Kubernetes 中會存儲為文本文件,作為 Helm 模版和值的一部分,而helm-secrets插件為關鍵資訊提供秘密管理和保護,將相關值進行加密
  • Chart 調試功能:Helm 提供了一些命令讓用戶在安裝前測試資源的正確性,檢查 Chart 是否正確生成,例如helm linthelm install —dry-run — debug

Helm 的缺點

  • 應用訂製受限於預置變數:Chart 是一種模板,Chart 的用戶僅能通過對 values 的控制來訂製組件的部署行為,模板中沒有提供變數的位置,是無法在下游直接進行變更。
  • YAML 文件的無序下發: 在 YAML 文件變多之後,尤其在 Operator-CRD 場景下,YAML 下發需要存在一定的先後順序。helm 中利用一個叫 crd 目錄來進行優先下發來避免問題。
  • 應用安裝狀態感知:對於 Helm 而言,將所有 Chart 中的模版推到 Kubernetes 之後,它的 install 過程就結束了,它並不關心 yaml 中配置的各個組件在 Kubernetes 中是否能有效運行,而這恰恰是用戶最關心的部分,因此使用 Helm 安裝可能會出現用戶無法感知的異常

SREWorks 對 Helm 的能力補充

部署 SREWorks 時的 Helm 安裝進度回饋

SREWorks 平台自身的安裝也是通過 Helm 包來實現的,常常會有一些 SREWorks 的用戶以為 helm install 執行完就安裝完了。事實上這個時候安裝才剛剛開始。我們希望用戶在這個過程中能夠感知到具體的安裝進度和異常回饋,於是我們在 helm install 執行完後提醒用戶(該功能將於 v1.3 版本上線):

Please execute following command in terminal to trace the install progress:

kubectl logs job.batch/sreworks-progress-check -nsreworks -f

After install finished (5-10mins) open the following URL in your browser:
  
   //xxxx/#/
   
   account: admin   
   password: *****

在這個 sreworks-progress-check 的 job 中,我們實現了對 SREWorks 安裝進度全跟蹤。

我們編寫了進度查詢和錯誤診斷腳本,將其包裝成 SREWorks 的一個 Job 和其他所有組件一起同步部署,這樣用戶就能在終端實時查看安裝進度,並能在異常出現時及時進行排查。

進度查詢:

錯誤診斷:

SREWorks 中的 Helm 組件狀態統一管理

鑒於 Helm 並不跟蹤各個組件的部署狀態,在 SREWorks 中,我們復用 AppManager 已有的 Groovy 腳本託管能力,自己編寫了一個小 Groovy 腳本,目的在於等待終態 + 獲取數據。

程式碼如下:

getStatus(request) {
    def client = getKubeClient(kubeconfig from parameters)
    def service = client.services().inNamespace(namespace).withName(name).get()
    def response = new JSONObject()
    if (service.get("loadbalancer", "ingress", 0, "ip") not empty) {
        response.put("spec.metadata.annotations.vvpSlb", service.get("loadbalancer", "ingress", 0, "ip"))
        return Status.builder().response(response).status("SUCCESS").build();
    } else {
        return Status.builder().status("RUNNING").build();
    }
}

上述流程完成了 Helm 的安裝、終態等待及數據獲取。

上述 getStatus() 函數除了部署過程會刷 (5s/次);部署完之後也會一直刷,不過頻率逐步降低到 5min/次。作為狀態感知的數據來源。

SREWorks 中的 Helm 組件順序部署

前面也提到過 Helm 的 YAML 文件無序下髮針對大型工程而言,會有一定的影響。SREWorks 的 Appmanager 基於 OAM 模型實現了 workflow 能力,能夠支援多個 Helm 組件按照 DAG 圖的順序部署。

SREWorks 應用 Helm 組件實踐

Tips: 雖然 Helm 官方將自身託管的 Chart 對應的包稱為應用 (Application),但在一個真實的複雜應用 (Appliation) 下,Helm Chart 更像是應用 (Application) 中的組件 (Component)。故在 SREWorks 中將 Helm 託管的 Chart 歸為組件 (Component)。

點擊進入 SREWorks 的「運維開發」應用,點擊運維開發-後端開發-Helm 組件就能看到 Helm 組件列表,圖中所示均為安裝在本地的 Helm 組件。

點擊上圖中「新增 Helm 組件的按鈕」,就可以將 Helm 組件添加到應用中,而組件來源可以是社區倉庫也可以是程式碼倉庫。針對社區中的開源軟體,可以直接使用社區倉庫中持續更新維護的 helm 源。

只要在頁面上點擊 Helm 組件添加,SREWorks-Appmanager 就會根據這些資訊會自動生成標準的 OAM 模型 YAML 文件。用戶無需關心 helm install 的細節,只需要在頁面上點擊部署就可以實現一個或多個 Helm 組件的部署。

部署完成後可在運維應用-應用部署的頁面查看部署記錄。同時在應用實例中也可以看到這個應用。

SREWorks 也在公共市場中添加了幾個開源的應用組件供用戶使用(持續更新中~),同時也支援用戶將自己製作或部署的 Helm 組件上傳到本地市場中。

總結

在 SREWorks(Appmanager)的應用體系中,對於承載組件 (Component) 這個概念而言,Helm 是再合適不過的工具。K8S YAML 或 Kustomize(下一篇會提到) 會讓使用者沉浸於過多的細節中難以自拔,而 Helm 非常明確自己的選擇:把複雜交給開發者,把簡單交給使用者。Helm 的自定義參數可以讓開發者把 Chart 包裝為一個黑盒,並明確這個黑盒可以接受什麼參數。使用者不用關心黑盒裡面是什麼,只需要調整這些開發者暴露出來的參數來滿足自己的需求即可。開發者、使用者的邊界在組織中可以被正常映射為研發團隊和 SRE 團隊,在認知和共識層面無需進一步投入成本。

後續文章我們會分享更多的 Kubernetes 組件和應用管理工具,均會發布在我們的公眾號「阿里智慧運維」上,請大家持續關注~也歡迎大家在公眾號後台留言想了解的內容和感興趣的相關話題,與 **SREWorks **團隊進行交流。

Tags: