1. 程式人生 > >阿里巴巴的 Kubernetes 應用管理實踐經驗與教訓

阿里巴巴的 Kubernetes 應用管理實踐經驗與教訓

作者 | 孫健波(天元)  阿里巴巴技術專家

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

如今,阿里巴巴內部維護了數十個大規模的 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 管理應用的公司和團隊都在自己定義應用。如下所示,我就搜到了兩家公司的應用定義:


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

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

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

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

讓我們回過頭來重新審視我們面臨的挑戰,歸根結底在於 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 名稱。

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 是一個完全屬於社群的應用定義模型,我們非常希望大家都能參與進來。

  • 釘釘掃碼進入 OAM 專案中文討論群

  • 通過 Gitter 直接參與討論:https://gitter.im/oam-dev/

我們會整合 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 等等資源,以便最大程度的實現社群中大家的協作與複用。

  • OAM 規範地址:https://github.com/oam-dev/spec
  • OAM 開源實現地址:https://github.com/oam-dev/rudr

期待大家的參與!

關注阿里巴巴雲原生公眾號回覆應用即可下載本文 PPT!

“> 阿里巴巴雲原生微信公眾號(ID:Alicloudnative)關注微服務、Serverless、容器、Service Mesh 等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,做最懂雲原生開發者的技術公眾號。