近年來随着雲原生技術的高速發展,阿裡巴巴看到了雲原生技術的潛力,以及未來企業 IT 全面上雲的必然趨勢,從2018年開始全面轉型到Kubernetes技術。到了 2019 年,Kubernetes 基礎設施底盤在阿裡巴巴經濟體中已經覆寫了阿裡巴巴方方面面的業務,規模化的接入了包括核心電商、物流、金融、外賣、搜尋、計算、AI 等諸多頭部網際網路場景。這套技術底盤,也逐漸成為了阿裡巴巴支撐 618、雙 11 等網際網路級大促的主力軍之一。
如今,阿裡巴巴和螞蟻金服内部運作的叢集規模也達到了一個新的高度,我們内部維護了數十個大規模的K8s叢集,其中最大的叢集約1萬個節點,這其實還不是能力上限。每個叢集會服務上萬個應用。在阿裡雲的Kubernetes服務ACK上,我們還維護了上萬個使用者的K8s叢集,這個規模和技術挑戰在全世界也是首屈一指的。
應用管理的兩大難題
在一定程度上解決了規模和穩定性問題之後,我們發現其實在K8s上管理應用還有很大的挑戰等着我們解決。今天我們主要讨論這兩個方面的挑戰:
- 對應用研發而言,K8s API 針對簡單應用過于複雜,針對複雜應用難以上手。
- 對應用運維而言,K8s 的擴充能力難以管理;K8s 原生的 API 沒有對雲資源全部涵蓋。
總體而言,我們面臨的挑戰就是,如何基于K8s提供真正意義上的應用管理平台,讓研發和運維隻需關注到應用本身。
研發對應用管理的訴求
K8s all in one 的 YAML檔案
讓我們來看一下這樣一個 K8s 的 yaml 檔案,這個 yaml 檔案已經是我簡化過的,但是我們仍然可以看到還是比較長。

面對這樣一個廣受“複雜”诟病的YAML檔案,我相信大家都會忍不住想該怎麼簡化。
自上而下,我們大緻把他們分為三塊,一塊是擴縮容、滾動更新相關的參數,這一塊應該是應用運維的同學比較關心的;然後中間一塊是鏡像、端口、啟動參數相關的,這一塊應該是開發的同學比較關心的;最後一塊大家可能根本看不懂,通常情況下也不太需要明白,可以把他們了解為K8s平台層的同學需要關心的。
看到這樣一個yaml檔案,我們很容易想到,隻要把裡面的字段封裝一下,把該暴露的暴露出來就好了。确實,我們内部就有 PaaS 平台這麼做。
隻透出部分字段:簡單卻能力不足
部分PaaS平台精心挑選了部分字段,并做了一個漂亮的前端界面給使用者,隻透出給使用者5個左右的字段,大大降低了使用者了解K8s的心智負擔。然後底層實作用類似模闆的方式把使用者這五個字段渲染出來一個完整的yaml檔案。突出的字段大概如下圖所示:
不得不說這種方式是非常有效的,針對簡單無狀态的應用,精簡 API 可以大大降低 K8s 的門檻,快速并且高效的對接使用者,PaaS平台也順利讓大家使用了起來。同時,我也從一些技術分享中了解到許多其他公司也是用這種類似的方式簡化的 K8s API。
但是當使用者的業務開始大規模對接以後,我們就會自然而言開始遇到有狀态的複雜應用。使用者就會開始抱怨 PaaS 平台能力不夠了。比如我們的Zookeeper多執行個體選主、主從切換這些邏輯,在這五個字段裡就很難展開了。
歸根結底就是屏蔽大量字段的方式會限制基礎設施本身的能力演進,但是 K8s 的能力是非常強大而靈活的。我們不可能為了簡化而放棄掉K8s強大的能力。
就比如目前這個例子,我們很容易想到,針對複雜有狀态的應用,應該通過K8s裡面的 CRD 和 Operator 來解決。
CRD+Operator: K8s 擴充能力強大卻難以上手
确實,我們内部對接複雜應用雲原生化的時候,也推薦他們編寫Operator,但是經常出現這樣一段對話。
中間件的工程師跟我們說,我這有個Zookeeper該用哪種K8s的Workload接入啊? 我們想了想,K8s設計如此精妙,自然沒有解決不了的問題,于是我們推薦他們使用Operator。他們就懵了,說你們搞雲原生的這幾年造新詞的能力絕對一流,之前都沒聽說過。
想想也是,業務方了解這些新概念不難,但是真的要自己去上手實作,還是非常困難的。我們自然也覺得業務方更應該專注于他們的業務本身,于是我們不得不幫跟他們一起寫。
可以看到,我們亟需一個統一的模型去解決研發對應用管理的訴求。
運維對應用管理的訴求
除了研發側的問題之外,我們在運維側也遇到了很大的挑戰。
運維能力衆多卻難以管理
K8s的CRD Operator 機制非常的靈活而強大,不光是複雜應用可以通過編寫CRD Operator實作,我們的運維能力也大量通過Operator來擴充,比如灰階釋出、流量管理,彈性擴縮容等等。我們常常贊歎于 K8s 的靈活性,它讓我們基礎平台團隊對外提供能力非常友善,但是對應用運維來說,他們要使用我們提供的這些運維能力,卻變得有些困難。
比如我們上線了一個CronHPA,可以定時設定在每個階段會根據 CPU 調整執行個體數的範圍。應用運維并不知道跟原生不帶定時功能的 HPA 會産生沖突,而我們也沒有一個統一的管道幫助管理這麼多種複雜的擴充能力,結果自然是引起了故障。這血的教訓提醒我們要做事前檢查,熟悉K8s的機制很容易讓我們想到為每個 Operator 加上 admission webhook。
這個 admission webhook 需要拿到這個應用綁定的所有運維能力以及應用本身的運作模式,然後做統一的校驗。如果這些運維能力都是一方提供的還好,如果存在二方,甚至三方提供的擴充能力,我們就沒有一個統一的方式去獲知了。事實上如果我們想的更遠一些就會發現,我們需要一個統一的模型來協商并管理這些複雜的擴充能力。
雲資源難以描述和統一傳遞
當我們把應用的Operator以及對應的運維能力都寫好以後,我們很容易想到要打包傳遞這個應用,這樣無論是公有雲還是專有雲都可以通過一個統一的方式去互動。社群的主流方式目前就是使用Helm來打包應用,而我們也采用了這樣的方式給我們的使用者傳遞。但是卻發現我們的使用者需求遠不止于此。
雲原生應用有一個很大的特點,那就是它往往會依賴雲上的資源,包括資料庫、網絡、負載均衡、緩存等一系列資源。
當我們使用 Helm 打包時,我們隻能針對K8s 原生API,而如果我們還想啟動 RDS 資料庫,就比較困難了。如果不想去資料庫的互動頁面,想通過K8s的API來管理,那就又不得不去寫一個 CRD 來定義了,然後通過Operator去調用實際雲資源的API。
這一整套傳遞物實際上就是一個應用的完整描述,即我們所說的“應用定義”。但事實上,我們發現“應用定義”這個東西,在整個雲原生社群裡其實是缺失的。這也是為什麼阿裡巴巴内部有很多團隊開始嘗試設計了自己的“應用描述”。
這種定義方式最終所有的配置還是會全部堆疊到一個檔案裡,這跟 K8s API all-in-one 的問題其實是一樣的,甚至還更嚴重了。而且,這些應用定義最終也都成為了黑盒,除了對應項目本身可以使用,其他系統基本無法複用。自然就更無法使得多方協作複用了。
每個公司和團隊都在自己定義應用
不光是阿裡内部的團隊需要應用定義,事實上幾乎每個基于K8s管理應用的公司和團隊都在自己定義應用。如下所示,我就搜到了兩家公司的應用定義:
應用定義實際上是應用傳遞、分發不可或缺的部分。但是在具體的實踐中,我們感受到這些内部的應用定義都面臨着如下的問題:
- 定義是否足夠開放,是否可以複用?
- 如何與開源生态協作?
- 如何疊代與演進?
這三個問題帶來的挑戰都是巨大的,我在上文已經提到,一個應用定義需要容易上手,但又不是靈活性,更不能是一個黑盒。應用定義同樣需要跟開源生态緊密結合,沒有生态的應用定義注定是沒有未來的,自然也很難持續的疊代和演進。
區分使用者的分層模型與子產品化的封裝
讓我們回過頭來重新審視我們面臨的挑戰,歸根結底在于 K8s 的 All in One API 是為平台提供者設計的,我們不能像下圖左側顯示的一樣,讓應用的研發、運維跟K8s團隊一樣面對這一團API。
一個合理的應用模型應該具有區分使用者角色的分層結構,同時将運維能力子產品化的封裝。讓不同的角色使用不同的API,正如上圖右側部分。
OAM: 以應用為中心的K8s API分層模型
OAM(Open Application Model)正是這樣一個以應用為中心的 K8s API 分層模型:
- 從研發的角度,他操作和關注的API對象叫Component。
- 從運維的角度,子產品化的運維能力封裝就是Trait,而運維可以通過App Config将Component和Trait自有組合,最終執行個體化成一個運作的應用。
- K8s團隊本身則仍然基于K8s的原生 API 疊代這一層能力。
針對研發時常抱怨的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中看看不同的概念具體是什麼。
OAM Component:研發關注的API
Component就是OAM模型提供給研發的API對象,如下所示:
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: <sha256:...>
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
可以看到Component本身就是一個K8s的CRD,spec 字段裡面的部分就是Component具體的定義。其中第一個重要的字段就是workloadType,這個決定了應用的運作模式。
針對簡單應用,OAM提供了6種核心Workload,如下表所示:
主要通過是否可通路、是否可複制、是否長久運作來區分。如Server,就代表了大家最常用的K8s裡面Deployment+Service的組合。
填寫了核心workloadType的Component,隻需要定義Container裡的注入鏡像、啟動參數等字段,就如我們最開始所說的屏蔽掉大量字段的PaaS一樣,為使用者大大降低了門檻。
而針對複雜的有狀态應用,OAM 則允許通過擴充Workload來實作,如下圖所示,我們可以定義一個新的叫 openfaas 的WorkloadType,它的定義實際上完全等價于一個CRD定義。
OAM模型中,使用自定義的Workload也是通過Component,隻是WorkloadType改為你自定義的WorkloadType名稱。
通過WorkloadType,OAM的Component就把簡單無狀态應用和複雜有狀态應用統一了起來。
OAM Trait:可發現、可管理的運維能力
Trait 就是子產品化的運維能力,我們能通過指令行工具發現一個系統裡支援哪些Traits(運維能力)。
$ kubectl get traits
NAME AGE
autoscaler 19m
ingress 19m
manual-scaler 19m
volume-mounter 19m
這時候,運維要檢視具體的運維能力該怎麼使用,是非常簡單的:
$ 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"
...
}
}
}
}
}
可以看到,他可以在 Trait 定義裡清晰的看到這個運維能力可以作用于哪種類型的 Workload,包括能填哪些參數、哪些必填/選填、參數的作用描述是什麼。 你也可以發現,OAM 體系裡面,Component 和 Trait 這些 API 都是 Schema,是以它們是整個對象的字段全集,也是了解這個對象描述的能力“到底能幹嗎?”的最佳途徑。
實時上,大家也可能已經發現,Trait 的定義和 CRD 也是對等的,而你完全也可以通過 Operator 實作 Trait。
是以 OAM 事實上将原本散亂的Operator通過不同的角色有機的管理起來了。
OAM Application Config:組裝Component和Trait,應用執行個體化運作
Component 和 Trait 最終通過Application Configuration結合,并真實運作起來。
更重要的是,這個 OAM 應用描述檔案是完全自包含的,也就是說通過 OAM YAML,作為軟體分發商,我們就可以完整地跟蹤到一個軟體運作所需要的所有資源和依賴。這就使得現在對于一個應用,大家隻需要一份 OAM 的配置檔案,就可以快速、在不同運作環境上把應用随時運作起來,把這種自包含的應用描述檔案完整地傳遞到任何一個運作環境中。
而我們圖中的
Rudr 項目就是OAM的一個實作,也可以了解為OAM的一個解釋器,将OAM的統一描述轉換為背後衆多的Operator。同時Rudr也将是一個統一管理的媒介,如果Application Configuration中出現了一個Component綁定2個Trait并且互相沖突的情況,就可以快速被檢驗并發現問題,如下圖所示。
同樣的,包括複雜應用的編排、雲資源的拉起、Workload與Trait互動等等,都可以在這個OAM解釋器中實作。
大家可以通過
rudr項目中的教程文檔體驗OAM的這些互動過程。
OAM 加持下的 Kubernetes PaaS
事實上,OAM 加持下的 PaaS 基于Kubernetes,将衆多 Operator 分層的管理了起來。
對于研發,通常他關心的應用可能是一個由 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 可以像樂高積木一樣靈活組裝,使得應用定義成為了社群共同建設的項目,使得應用管理變得統一,功能卻更加強大!
最後
最後,給大家分享一下 OAM 項目近期的計劃,OAM 是一個完全屬于社群的應用定義模型,我們非常希望大家都能參與進來。
- 我們會內建 OpenFaaS、Terraform、Knative,支援不同的 Workload,讓 OAM 可以對接不同的實作。
- 我們會針對 K8s Operator 提供一鍵接入的轉換方式,讓現在的 Operator 快速融入OAM。
- 我們也會開源一個 oam-framework 項目,這個項目可以快速建構一個 OAM 的實作,類似kubebuilder/Operator-sdk 快速建構 Operator 一樣,oam-framework 會幫助你快速建構OAM實作。
- 我們還會建構一個 CRD (traits/workloads) Registry項目,可以讓大家注冊自己的OAM實作、自定義Workload、Trait等等資源,以便最大程度的實作社群中大家的協作與複用。
最後的最後,我們也非常歡迎大家加入阿裡雲原生應用平台-基礎技術中台團隊,這裡大牛雲集,有世界級的複雜場景。