這周借著參加 KubeCon 之名跑到會場划了三天水,最後一天良心發現頓覺需要記錄一下,同時也順帶再消化一遍,遂有此文。

整個三天里除了 keynote 之外跑去聽得最多的就是大家在 CRD 和自定義控制器上的各種實踐,也就是各種 "Operator"。雖然 Operator 本身已經是一種大家司空見慣的模式,但具體如何為生產環境中的 場景與問題定義 CRD 還是一個很有意思的事情。有些設計能夠啟發我去修正一些以往淺薄的認知,比如螞蟻的 CafeDeployment 試圖去解決的問題讓我感受到 k8s 的 API 離"一個好用的 PaaS"還是有距離的; 有些 talk 能在範式方面給我一些啟發,比如某個 talk 里提到的多個業務組之間如何基於 CRD 協作;還有一些"意料之外但又情理之中"的設計,讓人大開眼界,直呼"我怎麼沒想到呢?",比如 OpenCruise 里 的 SidecarSet

另外還聽了一些偏 ops 的 session 和我司 TiKV 的 session,也有很多收穫。但我想寫的更聚焦一點,因此就只提一下這些 session 里和 CRD 或自定義控制器有關聯的部分。下面就開始吧!

CRD no longer 2nd class thing

slides

第一天傍晚一個專門安利 CRD 的 Keynote。大致內容是講了一個故事:

(以下內容經過博主個人演繹,我也不知道有沒有記岔...)

  • sig-storage: 我們想加一個 PV 備份的功能,希望在 k8s 內增加一個內置 API 對象 "VolumeSnapshot" 來描述對一個 PV 的快照。
  • sig-architecture: 不同意增加內置對象,請使用 CRD
  • sig-storage: 什麼!CRD 不是給第三方擴展 k8s 用的嗎? 我們現在是要 給 Kubernetes 主幹增加功能

然後呢,keynote 里表示 sig-architecture 是對的,CRD 在 Kubernetes 中已經是 "一等公民",最後帶大家入了個門,講了一下 CRD 的概念,用法以及 kubebuilder。

假如由我來給這個 keynote 一句總結,那就是 Kubernetes 本身都在用 CRD 加新功能了,我們還有啥理由不用嗎?

(一張 slides 的截圖)

To CRD or not to CRD

Slides

這個 session 雖然叫 "使用還是不使用 CRD,這是一個問題",最後卻沒有給出確切的標準來區分某個場景該不該使用 CRD(當然,即使有這樣的"標準",那也是充滿爭議的)。但這個 session 仍然誠意十足,不僅簡明扼要地列出了使用 CRD 需要考慮的問題,還探討了 CRD 除了擴展 Kubernetes 之外本身的架構意義。這一點讓我覺得,很多本身不需要和 k8s 做整合的場景有可能 也可以通過 CRD 放到 k8s 上來做,進而得到一些架構和編程模型上的收益。

先看 slides 里的一個例子,我們有一個微服務體系,分別有 Room、Light、Lock 三個 service:

而當用戶想打開房間里的某盞燈時,則需要發送一個 Rest 請求:

{
「action」: 「switch_on」,
「lights」: [
「lamp-1」,
「lamp-2」
],
「room」: 「kitchen」
}

接下來一個可能的流程是:

  1. Room Service 調用 Light Service,打開 lamp-1lamp-2 這兩盞燈
  2. Light Service 打開這兩盞燈,更新資料庫中等的狀態,返迴響應給 Room Service
  3. Room Service 收到影響,更新 Room 對象中燈的亮度
  4. Room Service 返迴響應給用戶

這個系統要做好,其實要解決不少問題:

  • 每個服務都要解決自己的存儲問題:字面意思
  • 每個服務都要解決高可用問題:字面意思
  • 可靠性問題:個人解讀一下,我們發送請求給 Room 服務更新房間,Room 服務再調用 Light 服務打開燈,這時候假如 Light 服務有問題,怎麼辦?諸如此類的問題最後會需要去做服務間的重試限流熔斷這類事
  • 服務間的 API 規範:這個大家應該都有感受,每個公司都會制定服務間的調用規範
  • 組與組之間圍繞 API 的協作:"過程式" 的 API 其實協作起來有很多問題,比如過長的鏈式調用,循環調用,這些都得通過架構和框架設計去防患於未然

接下來就開腦洞了:我們把這三個 service,全部用 k8s 的自定義 controller 來實現怎麼樣?

這時候,我們就可以聲明式 API 來開燈了:

apiVersion: v1
kind: Room
metadata:
name: kitchen
namespace: default
spec:
lights:
- name: lamp-1
brightness: 0.5
- name: lamp-2
brightness: 1.0

接下來的流程就是:

  1. Room Controller watch 到這個對象的期望狀態(spec)變更;
  2. Room Controller 更新對應的 Light 對象的亮度(更新 spec); yaml apiVersion: v1 kind: Light metadata: name: lamp-1 namespace: default spec: brightness: 0.5 status: currentBrightness: 0 3. Light Controller watch 到 lamp-1lamp-2 這兩個對象的期望亮度(spec)發生變化 4. Light Controller 調整這兩個燈的亮度,並更新目標對象的 .status.currentBrightness 5. Room Controller watch 到兩個燈的 status 發生變化,以此為依據更新自己的 status

可以發現整個協作的核心是 k8s 的 api-server,並且所有組件和邏輯都圍繞著聲明式 API 進行設計。這個設計下:

  • 存儲問題簡化(etcd 解決)
  • 高可用問題簡化(k8s 部署 3 個 api-server 自然就高可用了,controller 反正隨時可以拉起來)
  • 可靠性優化(控制循環與聲明式 API 這種可以不斷自我修正的機制本身就適合解決可靠性)
  • 不用考慮 API 規範和 API 協作(自定義控制器基本上就按這個模式寫了)

這裡要額外解釋一下 API 協作,大家可以想像一下,這個例子里用 controller 的方式,協作的心智成本是很低的,我們只需要看一下其它組的 API(也就是 CRD)里的欄位含義,然後開始"聲明自己要幹嘛" 就可以了。

當然,其實這兩者的對比是不公平的,因為例子一基本上等於沒有框架也沒有 PaaS 在裸寫應用,而例子二是在現成的架子上搭東西。真正搞開發的時候,底下的資料庫,高可用以及由框架或 Mesh 定義的規範與 協作形式基本也都是做完一遍之後開箱即用的,整體成本和自己整一個生產級 kubernetes 孰高孰低還不好說。另外,CRD 雖然改聲明方便,但用過 k8s 原生對象的都知道,改 spec 前我們必須得對下面的 機制有所了解,才能明白改了之後到底能否實現自己的需求,因此這個"心智成本"也只是變了一下形式而已。最後,這個"房間、燈、鎖"的例子其實選得很好,因為這個系統里組件明確並且都需要去協調一個不可靠的 模塊(燈、鎖這些設備),假如是我們只是在虛擬賬戶間轉個賬對個賬啥的,恐怕就很難塞進這個模式里去了。

因此,這是個挺有啟發性的例子,但不是一個"安利 CRD"的例子,整體還是非常中立的。

於是乎,Slides 里緊接著就講了使用與不使用 CRD 的優缺點對比:

  • 數據模型受限:etcd 並不是關係型資料庫
  • 系統整體性能基本取決於 etcd
  • 聲明式 vs 命令式,沒有好不好,只有適不適合
  • 團隊合作:CRD 很有優勢,每個團隊提供 CRD 和 controller,可以互相 watch 對方的 API 對象

當然了,到底用不用 CRD 還是取決於個人理解的,只是不要忘了(Slides 的最後一頁):

CafeDeployment

Slides

這個 talk 的中文題目是"為互聯網金融關鍵任務場景擴展部署",相當...不知所云,假如不是看了眼英文名"Extending Deployment for Internet Financial Mission-Critical Scenarios", 我差點錯過了這個精彩的 session...

Session 的主角是 CafeDeployment,後來看到在 KubeCon 前幾天螞蟻的公眾號就發文章講了這個東西,大家可以直接前往 原文 看看它解決的問題和具體的技術場景,假如覺得太長不看,也可以看下面的 三個 Key Takeaways:

  1. CafeDeployment 是一個頂級對象,就像 Deployment 管理 ReplicaSet 一樣, CafeDeployment 下面管理一個叫做 InPlaceSet 的對象。 CafeDeployment 的職責主要是按照策略 *在多個機房內各自創建一個 InPlaceSet*,來做容災,同時提供部署策略的控制,比如現場演示的就是新版本分三批發布,每批升級中間需要手工確認(改 Annotation)
  2. InPlaceSet 提供了 Pod 本地升級的能力(實現細節沒講,但是要 PoC 的話其實在 Controller 里依次修改 Pod 的鏡像就行,修改後 Pod 里的容器會 Restart 使用新鏡像,Pod 並不會重建)
  3. 用 Readiness Gate 控制了 Pod 本地升級時的上下線過程。大體就是通過設置 readiness gate 為 true 或 false 來設置 Pod 的狀態是否為 Ready,從而協調 endpoints,具體邏輯可以看原文

一開始聽這個 session 的時候,講需求的時候說現在的策略無法滿足跨機房高可用部署、無法實現優雅升級我其實是很疑惑的:這不是 anti-affinity 以及 preStopHook + readinessGate 就能實現了嗎? 聽到 demo 和實現部分才知道, CafeDeployment 的跨機房高可用部署是指均勻分布在多個機房中,並且分批升級時每次要從多個機房中選擇一部分進行升級,以實現全面的灰度驗證;而原地升級的特性也沒法自動 摘乾淨流量。對這些實際業務需求的梳理和展示其實是這個 session 給我最大的收穫。

還有幾個比較有意思的事情:

  • CafeDeployment 用一個 annotation 來控制升級過程中的手動確認:升級完一個批次後,annotation 被置為 false ,用戶修改為 true 後繼續開始下一批次的升級。其實我本來覺得這個做法不符合聲明式 API,status 和 spec 是對不上的,像 statefulset 這樣用 paritition 更合理。但這種辦法也有好處,就是用戶界面最簡化,用戶只需要關心是否能繼續升級即可,這裡也是一個權衡;
  • CafeDeployment 沒有用 CRD,而是用 AA (Aggregated ApiServer)實現的。原因是 CRD 不能定義 /scale 這樣的 subresource,另外,聽說 CafeDeployment 也正在打算把自定義的 APIServer 的存儲換掉,不用 etcd;

Alibaba CloudNative

Slides

也就是"電商巨頭的原生雲遷移經驗"這個 session,張磊老師的 session 是一定要去聽的 —— 當然,還有幾百個小夥伴也都是這麼想的,因此在開始前 10 分鐘 Room 609 就直接出現了爆場,外面的小夥伴都進不來 的情況,火爆程度可見一斑。事實上內容也確實是乾貨滿滿。

記得 KubeCon 聽到的 talk 里,阿里似乎還在說 "把 Sigma 的 Control Plane 換成 Kubernetes",而這次的架構圖就已經直接是 Kubernetes 原生 ApiServer + 一個完全自研的 Scheduler + 自研 Controllers (Cruise)了,Pouch 也換成了 containerd。session 里還著重說了消滅富容器,全面擁抱了社區的最佳實踐,阿里這樣的體量展現出如此敏捷的技術升級,當時在會場聽的時候確實是很震撼。

另外要說的一點是,關於最佳實踐的文檔和博客我們大家都常常看,但真正要去講給別人的聽,要用最佳實現說服別的人的時候,卻總是感覺講不生動,詞不達意,最後講來講去變成復讀機 "這是最佳實踐,這是最佳實踐..." 而張磊老師講的時候總是能恰到好處地"我舉個例子"一波講明白,這個姿勢要能學來可就不得了了...

還有一個重頭是 OpenCruise,提供三種 CRD:SidecarSet,(Advanced)StatefulSet,BroadcastJob,具體的作用大家看一下項目文檔就能了解個大概。我想寫的還是一些體會。

首先是 OpenCruise 里的 StatefulSet 提供的原地升級功能。我原本以為原地升級不就是保持 IP 不變嗎,這不就是遷就虛擬機時代的基礎設施嗎,一點也不雲原生。當然,你讓我說為什麼不支持原地升級,我也講 不明白,可能只會說 Pod 的設計意圖就是 Mortal 的,你們現在逆天而為要給 Pod 續命實在是搞得太丑了。事實證明我完全錯了,這些認知根本是沒有見過實際的業務場景在意淫(還好我還曉得參加 KubeCon)。 講師之一的酒祝講了個例子,阿里自研的調度器支持 Batch Scheduling,對於一批十幾萬個 Pod,可能要嘗試非常多種方案才能得出一個排布拓撲,這時候再去做刪除式的滾動升級代價太大了;同時,類似雙十一 這樣的大促之前還要搞全鏈路壓測,壓測完之後要是一個滾動拓撲變了,系統承壓量又不一樣了怎麼辦?

這個例子說服力超強,而且也凸顯出本地升級"穩定為先"的優勢。

然後是 SidecarSet,與 session 前半部分的內容結合來看,這個也是符合去富容器歷史進程的產物。因為大量富容器中的非業務進程需要做成 sidecar,而把 Sidecar 和業務容器的管理剝離開始,則是一個 四兩撥千斤的創新,不說了不說了...我都感覺快成軟廣了(其實是寫不動了 orz)

總之,假如你沒去聽的話,看一下 slides 絕對值得。

結語

上面的各種 session 再結合 KubeCon 前幾天發布的 Kubernetes 1.15 對 CRD 的大量增強以及社區里俯拾皆是的 xxx-operator,無疑印證了 CRD 和自定義控制器已經是最 "稀鬆平常" 的 Kubernetes 擴展模式。這時候,CRD 本身也就不再有意思,需要把光彩讓給 SidecarSet, CafeDeployment 這些匠心獨運的業務實踐了。另外,要插個硬廣,我司在做的 tidb-operator 面臨的場景同樣極富挑戰,假如你想知道在雲上編排一個複雜的分散式資料庫是一種怎樣的體驗,歡迎通過郵箱 [email protected] 聯繫我!

推薦閱讀:

相关文章