阿里巴巴基於 Kubernetes 的實踐經驗

本文整理自孫健波在 ArchSummit 大會 2019 北京站演講稿記錄。首先介紹了阿里巴巴基於 Kubernetes 項目進行大規模應用實踐過程中遇到的問題;隨後會逐一介紹解決這些問題的現有實踐及其本身存在的侷限性;最後會介紹阿里巴巴目前正在進行的嘗試和社區在這一領域的發展方向。

如今,阿里巴巴內部維護了數十個大規模的 K8s 集群,其中最大的集群約 1 萬個節點,每個集群會服務上萬個應用;在阿里雲的 Kubernetes 服務 ACK 上,我們還維護了上萬個用戶的 K8s 集群。我們在一定程度上解決了規模和穩定性問題之後,發現其實在 K8s 上管理應用還有很大的挑戰等著我們。

應用管理的兩大難題

今天我們主要討論這兩個方面的挑戰:

對應用研發而言,K8s API 針對簡單應用過於複雜,針對複雜應用難以上手;
對應用運維而言,K8s 的擴展能力難以管理;K8s 原生的 API 沒有對雲資源全部涵蓋。

總體而言,我們面臨的挑戰就是:如何基於 K8s 提供真正意義上的應用管理平臺,讓研發和運維只需關注到應用本身。

研發對應用管理的訴求

1. K8s all in one 的 YAML 文件

讓我們來看一下這樣一個 K8s 的 yaml 文件,這個 yaml 文件已經是被簡化過的,但是我們可以看到它仍然還是比較長。

阿里巴巴基於 Kubernetes 的實踐經驗

面對這樣一個廣受“複雜”詬病的 YAML 文件,我相信大家都會忍不住想該怎麼簡化。

自上而下,我們大致把它們分為三塊:

  • 一塊是擴縮容、滾動升級相關的參數,這一塊應該是應用運維的同學比較關心的;
  • 中間一塊是鏡像、端口、啟動參數相關的,這一塊應該是開發的同學比較關心的;
  • 最後一塊大家可能根本看不懂,通常情況下也不太需要明白,可以把它們理解為 K8s 平臺層的同學需要關心的。

看到這樣一個 yaml 文件,我們很容易想到,只要把裡面的字段封裝一下,把該暴露的暴露出來就好了。確實,我們內部就有 PaaS 平臺這麼做。

2. 只透出部分字段:簡單卻能力不足

內部的某個 PaaS 平臺精心挑選了部分字段,並做了一個漂亮的前端界面給用戶,只透出給用戶 5 個左右的字段,大大降低了用戶理解 K8s 的心智負擔。然後底層實現用類似模板的方式把用戶這五個字段渲染出來一個完整的 yaml 文件。

突出的字段大概如下圖所示:

阿里巴巴基於 Kubernetes 的實踐經驗

不得不說這種方式是非常有效的,針對簡單無狀態的應用,精簡 API 可以大大降低 K8s 的門檻,快速並且高效的對接用戶,PaaS 平臺也順利讓大家使用了起來。同時,我也從一些技術分享中瞭解到許多其他公司也是用這種類似的方式簡化的 K8s API。

但是當用戶的業務開始大規模對接以後,我們就會自然而然遇到有狀態的複雜應用,用戶就會開始抱怨 PaaS 平臺能力不夠了。比如我們的 Zookeeper 多實例選主、主從切換這些邏輯,在這五個字段裡就很難展開了。

歸根結底就是屏蔽大量字段的方式會限制基礎設施本身的能力演進,但是 K8s 的能力是非常強大而靈活的。我們不可能為了簡化而放棄掉 K8s 強大的能力。

就比如當前這個例子,我們很容易想到,針對複雜有狀態的應用,應該通過 K8s 裡面的 CRD 和 Operator 來解決。

3. CRD+Operator: K8s 擴展能力強大卻難以上手

確實,我們內部對接複雜應用雲原生化的時候,也推薦他們編寫 Operator,但是經常出現這樣一段對話。

阿里巴巴基於 Kubernetes 的實踐經驗

中間件的工程師跟我們說,我這有個 Zookeeper 該用哪種 K8s 的 Workload 接入啊?我們想了想,K8s 設計如此精妙,自然沒有解決不了的問題,於是我們推薦他們使用 Operator。他們就懵了,說你們搞雲原生的這幾年造新詞的能力絕對一流,之前都沒聽說過。

想想也是,業務方理解這些新概念不難,但是真的要自己去上手實現,還是非常困難的。我們自然也覺得業務方更應該專注於他們的業務本身,於是我們不得不幫他們一起寫。

可以看到,我們亟需一個統一的模型去解決研發對應用管理的訴求。

運維對應用管理的訴求

除了研發側的問題之外,我們在運維側也遇到了很大的挑戰。

1. 運維能力眾多卻難以管理

K8s 的 CRD Operator 機制非常靈活而強大,不光是複雜應用可以通過編寫 CRD Operator 實現,我們的運維能力也大量通過 Operator 來擴展,比如灰度發佈、流量管理、彈性擴縮容等等。

我們常常讚歎於 K8s 的靈活性,它讓我們基礎平臺團隊對外提供能力非常方便,但是對應用運維來說,他們要使用我們提供的這些運維能力,卻變得有些困難。

比如我們上線了一個 CronHPA,可以定時設置在每個階段會根據 CPU 調整實例數的範圍。應用運維並不知道跟原生不帶定時功能的 HPA 會產生衝突,而我們也沒有一個統一的渠道幫助管理這麼多種複雜的擴展能力,結果自然是引起了故障。這血的教訓提醒我們要做事前檢查,熟悉 K8s 的機制很容易讓我們想到為每個 Operator 加上 admission webhook。

這個 admission webhook 需要拿到這個應用綁定的所有運維能力以及應用本身的運行模式,然後做統一的校驗。如果這些運維能力都是一方提供的還好,如果存在兩方,甚至三方提供的擴展能力,我們就沒有一個統一的方式去獲知了。

事實上如果我們想的更遠一些就會發現,我們需要一個統一的模型來協商並管理這些複雜的擴展能力。

2. 雲資源難以描述和統一交付

當我們把應用的 Operator 以及對應的運維能力都寫好以後,我們很容易想到要打包交付這個應用,這樣無論是公有云還是專有云都可以通過一個統一的方式去交互。社區的主流方式目前就是使用 Helm 來打包應用,而我們也採用了這樣的方式給我們的用戶交付,但是卻發現我們的用戶需求遠不止於此。

雲原生應用有一個很大的特點,那就是它往往會依賴雲上的資源,包括數據庫、網絡、負載均衡、緩存等一系列資源。

阿里巴巴基於 Kubernetes 的實踐經驗

當我們使用 Helm 打包時,我們只能針對 K8s 原生 API,而如果我們還想啟動 RDS 數據庫,就比較困難了。如果不想去數據庫的交互頁面,想通過 K8s 的 API 來管理,那就又不得不去寫一個 CRD 來定義了,然後通過 Operator 去調用實際雲資源的 API。

這一整套交付物實際上就是一個應用的完整描述,即我們所說的“應用定義”。但事實上,我們發現“應用定義”這個東西,在整個雲原生社區裡其實是缺失的。這也是為什麼阿里巴巴內部有很多團隊開始嘗試設計了自己的“應用定義”。

阿里巴巴基於 Kubernetes 的實踐經驗

這種定義方式最終所有的配置還是會全部堆疊到一個文件裡,這跟 K8s API all-in-one 的問題其實是一樣的,甚至還更嚴重了。而且,這些應用定義最終也都成為了黑盒,除了對應項目本身可以使用,其他系統基本無法複用,自然就更無法使得多方協作複用了。

3. 每個公司和團隊都在自己定義應用

不光是阿里巴巴內部的團隊需要應用定義,事實上幾乎每個基於 K8s 管理應用的公司和團隊都在自己定義應用。如下所示,我就搜到了兩家公司的應用定義:

阿里巴巴基於 Kubernetes 的實踐經驗

阿里巴巴基於 Kubernetes 的實踐經驗

應用定義實際上是應用交付/分發不可或缺的部分。但是在具體的實踐中,我們感受到這些內部的應用定義都面臨著如下的問題:

  • 定義是否足夠開放,是否可以複用?
  • 如何與開源生態協作?
  • 如何迭代與演進?

這三個問題帶來的挑戰都是巨大的,我在上文已經提到,一個應用定義需要容易上手,但又不失靈活性,更不能是一個黑盒。應用定義同樣需要跟開源生態緊密結合,沒有生態的應用定義註定是沒有未來的,自然也很難持續的迭代和演進。

區分使用者的分層模型與模塊化的封裝

讓我們回過頭來重新審視我們面臨的挑戰,歸根結底在於 K8s 的 All in One API 是為平臺提供者設計的,我們不能像下圖左側顯示的一樣,讓應用的研發、運維跟 K8s 團隊一樣面對這一團 API。

阿里巴巴基於 Kubernetes 的實踐經驗

一個合理的應用模型應該具有區分使用者角色的分層結構,同時將運維能力模塊化的封裝。讓不同的角色使用不同的 API,正如上圖右側部分。

OAM:以應用為中心的 K8s API 分層模型

OAM(Open Application Model) 正是這樣一個以應用為中心的 K8s API 分層模型:

  • 從研發的角度,他操作和關注的 API 對象叫 Component;
  • 從運維的角度,模塊化的運維能力封裝就是 Trait,而運維可以通過 App Config 將 Component 和 Trait 自由組合,最終實例化成一個運行的應用;
  • K8s 團隊本身則仍然基於 K8s 的原生 API 迭代這一層能力。
阿里巴巴基於 Kubernetes 的實踐經驗

針對研發時常抱怨的 K8s API 太複雜,我們通過關注點分離,區分使用者面對的 API 來解決,同時提供了幾種核心的 Workload,讓研發只需要填寫少數幾個字段就可以完成組件的定義;針對複雜應用定義,我們通過擴展的 Workload,允許研發對接 CRD Operator 的方式對接自定義 Workload。

針對運維需要的模塊化封裝運維能力和全局管理的需求,OAM 模型通過 Trait 來提供。

Trait 是運維能力的體現,不同的 Trait 也對應了不同類型的運維能力,如日誌收集 Trait、負載均衡 Trait、水平擴縮容 Trait 等等;同時 OAM 本身就提供了一個全局管理的標準,OAM 模型的實現層可以輕鬆針對 OAM 定義裡的種種 Trait 描述進行管理和檢查。

針對雲資源,OAM 向上也提供了統一的 API,也是通過關注點分為三類:

一類是研發關注的雲資源,如數據庫 RDS、對象存儲 OSS 等,通過擴展 Workload 接入;

另一類是運維關注的雲資源,如負載均衡 SLB 等,通過 Trait 接入;

最後一類也是運維關注的雲資源,但是可能包含多個應用之間的聯動關係,如虛擬專有網絡 VPC 等,通過應用的 Scope 接入。Scope 則是 OAM 中管理多應用聯動關係的概念。

可以看到,OAM 通過統一的一套標準,解決了我們今天提到的所有難題。讓我們繼續深入到 OAM 中看看不同的概念具體是什麼。

1. OAM Component:研發關注的 API

Component 就是 OAM 模型提供給研發的 API 對象,如下所示:

<code>  

apiVersion: core.oam.dev/v1alpha1
kind: Component
metadata:
name: nginx
annotations:
version: v1.0.0
description: >
Sample component schematic that describes the administrative interface for our nginx deployment.
spec:
workloadType: Server
osType: linux
containers:
- name: nginx
image:
name: nginx:1.7.9
digest: <...>
env:
- name: initReplicas
value: 3
- name: worker_connections
fromParam: connections
workloadSettings:
...
parameters:
- name: connections
description: "The setting for worker connections"
type: number
default: 1024
required: false/<code>

可以看到 Component 本身就是一個 K8s 的CRD,spec 字段裡面的部分就是 Component 具體的定義。其中第一個重要的字段就是 workloadType,這個決定了應用的運行模式。

針對簡單應用,OAM 提供了 6 種核心 Workload,如下表所示:

阿里巴巴基於 Kubernetes 的實踐經驗

主要通過是否可訪問、是否可複製、是否長久運行來區分。如 Server,就代表了大家最常用的 K8s 裡面 Deployment+Service 的組合。

填寫了核心 workloadType 的 Component,只需要定義 Container 裡的注入鏡像、啟動參數等字段,就如我們最開始所說的屏蔽掉大量字段的 PaaS 一樣,為用戶大大降低了門檻。

而針對複雜的有狀態應用,OAM 則允許通過擴展 Workload 來實現,如下圖所示,我們可以定義一個新的叫 openfaas 的 WorkloadType,它的定義實際上完全等價於一個 CRD 定義。

阿里巴巴基於 Kubernetes 的實踐經驗

OAM 模型中,使用自定義的 Workload 也是通過 Component,只是 WorkloadType 改為你自定義的 WorkloadType 名稱。

2. OAM Trait:可發現、可管理的運維能力

Trait 就是模塊化的運維能力,我們能通過命令行工具發現一個系統裡支持哪些 Traits(運維能力)。

<code>$ kubectl get traits
NAME AGE
autoscaler 19m
ingress 19m
manual-scaler 19m
volume-mounter 19m/<code>

這時候,運維要查看具體的運維能力該怎麼使用,是非常簡單的:

<code>$ kubectl get trait ingress -o yaml
apiVersion: core.oam.dev/v1alpha1
kind: Trait
metadata:
name: cron-scaler
annotations:
version: v1.0.0
description: "Allow cron scale a workloads that allow multiple replicas."
spec:
appliesTo:
- core.oam.dev/v1alpha1.Server
properties: |
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": [
"schedule"
],
"properties": {
"schedule": {
"type": "array",
"description": "CRON expression for a scaler",
"item": {
"type": "string"
}
},
"timezone": {
"type": "string",
"description": "Time zone for this cron scaler."
},
"resource":{
"type": "object"
"description": "Resources the cron scaler will follow",
"properties": {
"cpu": {
type: "object"
...
}

}
}
}
}/<code>

可以看到,他可以在 Trait 定義裡清晰的看到這個運維能力可以作用於哪種類型的 Workload,包括能填哪些參數、哪些必填/選填、參數的作用描述是什麼。你也可以發現,OAM 體系裡面,Component 和 Trait 這些 API 都是 Schema,所以它們是整個對象的字段全集,也是瞭解這個對象描述的能力“到底能幹嗎?”的最佳途徑。

事實上,大家可能已經發現,Trait 的定義和 CRD 是對等的,而你完全也可以通過 Operator 實現 Trait。

阿里巴巴基於 Kubernetes 的實踐經驗

所以 OAM 事實上將原本散亂的 Operator 通過不同的角色有機的管理起來了。

3. OAM Application Config:組裝 Component 和 Trait,應用實例化運行

Component 和 Trait 最終通過 Application Configuration 結合,並真實運行起來。

阿里巴巴基於 Kubernetes 的實踐經驗

更重要的是,這個 OAM 應用描述文件是完全自包含的,也就是說通過 OAM YAML,作為軟件分發商,我們就可以完整地跟蹤到一個軟件運行需要的所有資源和依賴。這就使得現在對於一個應用,大家只需要一份 OAM 的配置文件,就可以快速、在不同運行環境上把應用隨時運行起來,把這種自包含的應用描述文件完整地交付到任何一個運行環境中。

而我們圖中的 Rudr 項目就是 OAM 的一個實現,也可以理解為 OAM 的一個解釋器,將 OAM 的統一描述轉換為背後眾多的 Operator。

同時 Rudr 也是一個統一管理的媒介,如果 Application Configuration 中出現了一個 Component 綁定 2 個 Trait 並且互相沖突的情況,就可以快速被檢驗並發現問題,如下圖所示:

阿里巴巴基於 Kubernetes 的實踐經驗

同樣,包括複雜應用的編排、雲資源的拉起、Workload 與 Trait 交互等等,都可以在這個 OAM 解釋器中實現。

大家可以通過 Rudr 項目中的教程文檔體驗 OAM 的這些交互過程。

OAM 加持下的 Kubernetes PaaS

事實上,OAM 加持下的 PaaS 基於 Kubernetes,將眾多 Operator 分層的管理了起來。

阿里巴巴基於 Kubernetes 的實踐經驗

對於研發,通常他關心的應用可能是一個由 web 和數據庫組合而成的應用,數據庫組件的背後可能是一個 RDS Operator 實現。web 應用背後,則可以是我們開源的 K8s 原生 StatefulSet的增強項目 OpenKruise,OpenKruise 中提供的包括原地升級等增強能力則通過 Trait 的方式去配置。而額外的一些監控報警、日誌等能力,則由一個個獨立的 Operator 去實現,由運維在第二層去關注和管理。

最終 K8s 團隊聯合各種基礎軟件的提供商,圍繞 K8s 原生 API,以 Operator 的形式不斷提供擴展能力,通過 OAM 這樣統一的規範和標準向外標準化輸出。

更重要的是,OAM 的統一描述大大提高了 Operator 的複用能力,使得 Operator 的編寫主需要關注業務邏輯本身。比如原先你寫一個 Zookeeper Operator,你需要寫實例的服務發現、需要寫升級時主備切換的編排邏輯、需要寫實例備份的邏輯,而這一切在 OAM 的標準化下,你將可以輕鬆在社區找到類似組成部分。

OAM 加持下的 Kubernetes PaaS,使得不同的 Operator 可以像樂高積木一樣靈活組裝,使得應用定義成為了社區共同建設的項目,使得應用管理變得統一,功能卻更加強大!


分享到:


相關文章: