螞蟻大規模 Kubernetes 集羣無損升級實踐指南【探索篇】

文|王連平(花名:燁川 )

螞蟻集團高級開發工程師

負責螞蟻 Kubernetes 集羣容器交付

專注於集羣交付能力、交付性能及交付 Trace 等相關領域

本文 12623 字 閱讀 20 分鐘

 

 

—— 庖丁解牛,讓升級不再煩惱

 

 

PART. 1

背 景

 

螞蟻 Sigma 作爲螞蟻集團核心的基礎設施,經過多年的發展其規模已經處於業界領先位置,大規模集羣對 Kubernetes 的穩定性及功能性提出更高的要求。螞蟻 Sigma 力爭在萬級規模的雲原生環境下,挑戰高效穩定、無損無感的雲原生操作系統升級,給用戶帶來極致穩定的、功能新穎的雲原生服務。 

 

 爲什麼要持續迭代升級 ?

 

Kubernetes 社區的活躍度非常高,衆多的雲原生愛好者爲社區貢獻智慧,推動社區版本不斷更新。升級是爲了緊跟社區的步伐,及時享用社區沉澱下來的優秀特性,進而給公司帶來更大利益。

 

 爲什麼升級那麼難 ?

 

按照螞蟻 Sigma 的規模,升級對我們來講是一件非常不容易的事情,主要體現在:

 

- 在升級準備階段,要全量推動客戶端進行升級,業務方要安排專門的人投入進來,耗時耗力;

 

- 在升級過程中,爲了規避版本滾動時對 Kubernetes 資源操作可能帶的來不可預期後果,升級過程中一般會關停流量,業務體感不好;

 

- 對於升級時間窗口選擇,爲了給用戶更好的服務體驗,升級要放到業務量少的時間進行,這對平臺運維人員不太友好。

 

因此,升級過程中如何提升用戶、研發、SRE 的幸福感是我們想要達成的目標。我們期望實現無損升級來降低升級風險,解耦用戶來提升幸福感,高效迭代來提供更強大的平臺能力,最終實現無人值守。

 

本文將結合螞蟻 Sigma 系統升級實踐,從 Kubernetes 系統升級的目標、挑戰開始,逐步剖析相關的 Kubernetes 知識,針對這些挑戰給出螞蟻 Sigma 的一些原則和思考。

 

【兩種不同的升級思路】

 

在介紹挑戰和收益前,我們先了解下當前集羣升級的方式。Kubernetes 升級與普通軟件升級類似,主要有以下兩種常見的升級方式:替換升級和原地升級。

 

替換升級:將應用運行的環境切換到新版本,將舊版本服務下線,即完成替換。在 Kubernetes 升級中,即升級前創建新版本的 Kubernetes 集羣,將應用遷移到新的 Kubernetes 集羣中,然後將舊版本集羣下線。當然,這種替換升級可以從不同粒度替換,從集羣爲度則是切換集羣;從節點維度,則管控節點組件單獨升級後,kubelet 節點升級時遷移節點上的 Pod 到新版本節點,下線舊版本節點。

 

原地升級:將升級的軟件包原地替換,舊服務進程停止,用新的軟件包重新運行服務。在 Kubernetes 升級中,apiserver 和 kubelet 採用原地軟件包更新,然後重啓服務,這種方式與替換升級最大的區別在於節點上的 workload 不用遷移,應用不用中斷,保持業務的連續性。

 

上述兩種方式各有優缺點,螞蟻 Sigma 採用的是原地升級。

 

【方法論-庖丁解牛】

 

採用原地升級時也必然會遇到原地升級的問題,其中最主要問題就是兼容性問題,主要包含兩個方面:Kubernetes API 和組件內部的控制邏輯兼容性。

 

Kubernetes API 層面包含 API 接口、resource 結構和 feature 三方面變化,而組件內部控制邏輯變化主要是 resource 在 Kubernetes 內部流轉行爲的變化。

 

前者是影響用戶及集羣穩定性最重要的因素,也是我們重點解決的問題。

API 接口的變化固然要涉及到客戶端的升級,特別是對於 deprecated 和 removed 的 API,客戶端無法再使用舊版本的 API 接口。resource 接口的變化主要指 resource 字段變化,字段的調整意味着 API 能力的變化,同一 resource 在新舊版本中存在字段上的差異會導致 API 能力上差異,主要體現在新增某個字段、廢棄某個字段和字段默認值變化。feature 方面,主要是一些 feature 的 GA 導致 featrue 開關能力被移除,以及一些新的 feature 的加入。

 

面對上述的核心問題,我們將升級中遇到的兼容性問題按照升級階段分爲“升級前”、“升級中”和“升級後”三個階段。

 

- 升級前,將面臨大量客戶端升級推動問題,通過探索版本之間的差異和多版本客戶端並存的問題,我們來制定一些規則,這將大大減少客戶端升級的數量,提升升級的效率。

 

- 升級中,將面臨多版本 apiserver 並存的問題,以及數據的存儲版本轉換問題,當然還會有可回滾性的問題,這些問題我們將採用精細化流量控制能力避免篡改,壓制 resource 存儲版本和 GVK 版本保證可回滾,同時對於 etcd 中的數據進行版本遷移,如此實現無損升級和回滾。

 

- 升級後,對於少量的可能引發不可接受故障的客戶端,我們通過識別資源修改請求意圖,降低篡改的風險。

還有一個重要的環節,整個過程我們要做到自動化、可視化,在升級過程中流量的充分灰度是很有必要的,升級節奏的自動化推進和應急場景下的人工可控性也是非常重要的,這些將在另一篇文章中詳細介紹。

 

整體來看,我們通過客戶端最小化升級和滾動自動化升級能力、提升升級的效率,通過精細化流量控制、灰度可回滾能力以及長效的字段管控能力,提升整個升級過程的可靠性、穩定性。

 

 

PART. 2

升級前

 

集羣升級必然會有 API 的更新和迭代,主要體現在 API 新增、演進和移除,在 Kubernetes 中 API 的演進一般是 Alpha、beta、GA,一個 resouce 的 API version 會按照上述版本進行迭代,當一個 API 新增時,最開始是 Alpha 階段,例如"cert-manager.io/v1alpha3",經過若干次迭代,新特性進入 beta 版本,最後進入穩定的 GA 版本,這個過程可能跨若干個大的社區版本,一些版本會在 GA 版本穩定運行一定時間後被 deprached 掉,並且被 deprached 的 API 版本在一段時間後會被直接移除,這就對我們的客戶端有了升級的剛性需求。

 

在介紹客戶端升級前,先介紹下一般 resource API 變化有哪些方面。

 

【Schema 變化】

 

不同版本的 Kubernetes 資源的 Schema 字段可能存在差異,主要表現在以下兩個方面:

 

- 字段的增加/刪除/修改

- 字段的默認值調整

 

字段增刪改

Kubernetes 的 resource 如果對某個字段的調整,包括:增加、刪除、修改三種。對於“增加”操作,可以在新 GV(GroupVersion)出現也可以在舊 GV 中出現。對於“刪除”和“修改”,一般只會在新的 GV 中出現。

 

基於以上條件,對於新版本 APISever 引入的 resource 字段調整,可以得出以下結論:

 

字段默認值變化

字段默認值變化是指,在新舊 apiserver 中 resource 某個字段默認值填充不一致。字斷默認值變化可能帶來的兩個問題:

 

- container hash 變化,會導致容器重啓

- 影響控制組件的控制動作

 

字段變化帶來的影響主要在客戶端新舊版本交叉訪問和 apiserver 多版本並存交叉訪問上,具體影響在下文中介紹。

 

 【客戶端升級】 

 

客戶端升級是爲了兼容新版本 API,保證在升級後不出現問題,同時實現 operator 精確化、差異化升級,提升升級效率。低版本客戶端不升級會遇到的如下問題:

 

核心問題

按照新舊版本 GVK(GroupVersionKind)的變化梳理一下升級過程中低版本客戶端可能出現的各種情況:

如上圖所示,核心問題主要有以下三種:

 

1.低版本客戶端訪問已經被 depreached/removed 的 GroupVersionKind

2.低版本客戶端操作的 resource 存在字段增加問題

3.低版本操作的 resource 存在字段默認值變化問題

 

針對第一個問題,訪問已經被 depreached 特別是被 removed 的 GVK 時,服務器端直接返回類 404 錯誤,表示此 rest url 或者次 GVK 已經不存在。

 

針對第二個和第三個問題,都出現在低版本客戶端的 Update 操作,爲什麼對於 patch 操作不會出現問題呢?因爲 Update 操作是全量更新,patch 操作是局部更新,全量更新的情況下,如果客戶端版本低沒有新增字段或者沒默認值變化的字段,此時去 Update 此 resource,提交的請求數據中不會出現此字段,但是此字段在 apiserver 內部會被補全和填充,如此此字段的值就完全依賴 apiserver 內部的邏輯了。

 

針對字段增加的情況,我們做了一個實驗,如下:

 

1.18 版本中 Ingress 中多了一個 patchType 的字段,我們首先通過 1.18 的客戶端進行創建,並設定 pathType=Prefix,然後通過 1.16 版本的客戶端進行 Update,發現此職被修改爲 pathType 的默認值。如下:

思考與解決

針對第一個問題,處理方式比較明確,客戶端必須升級,因爲 GVK 已經從新版本的 apiserver 中移除了。按照 Kubernetes 社區的 API 廢棄規則(給定類別的 API 版本在新的、穩定性未降低的 API 版本發佈之前不可被廢棄;除了每類 API 版本中的最新版本,舊的 API 版本在其被宣佈被廢棄之後至少一定時長內仍需被支持),我們可以顯式地控制一些 API 向下兼容,來滿足一些低版本客戶端的需求,但是此方式不是無限的,在某個高版本中總是會被移除掉的,所以不推薦這麼做來延遲或容忍客戶端的升級。

 

針對第二個和第三個問題,都涉及到 Update 操作,因爲 Update 操作會出現誤操作的情況。如果用戶客戶端版本較低,但是用戶並不關心 resource 的新增字段,也不想使用這些新功能,他可以完全不理會此字段,對於 create/delete/patch 都沒有問題,同時 patch 操作也不會針對此字段進行。所以控制好 Update 操作的客戶端就可以避免新增字段的篡改行爲。

 

 

PART. 3

升級中

 

Kubernetes 集羣的升級主要包含客戶端升級、核心組件升級,核心組件包括 apiserver、controller-manger、scheduler 和 kubelet。

 

這裏的客戶端是廣義上的客戶端,即業務的 operator 及管控的 operator 都稱爲客戶端。客戶端的升級由客戶端自行做流量灰度,大版本升級過程中最核心的是 apiserver 升級過程中可能會出現髒數據問題。

 

這裏提到的髒數據問題主要體現在以下兩個方面:

 

- 多版本 apiserver 交叉操作同一資源

是高低版本 apiserver 中對有 Schema 變化資源的操作會出現篡改問題,其問題本質與多版本客戶端操作同一有 Schema 變化的資源時發生的篡改一致。只不過這裏是 apiserver 版本不一樣,客戶端版本是否一致都會引起篡改問題。

 

- 存儲在 etcd 中的數據如何保證正確更新

是大家一般不太注意的問題,因爲 apiserver 升級過程中會幫我們很好的處理,但也不是百分百完美處理,這裏單獨拿出來講一下也有助於大家對 Kubernetes 數據存儲有更深入的瞭解。

 

髒數據的問題在升級過程中很容易聯想到可回滾性,我們無法保證升級百分百成功,但是我們一定要有可回滾能力,按照社區的建議 Kubernetes 在升級過程中不建議回滾,它會帶來更多的兼容性問題。

 

上述這些問題將在下文詳細講述。

 

【多版本 apiserver 並存】

 

從升級的過程中可以看到,在流量管控時主要管控的流量有兩項:

 

- Updateu/patch 動作的流量

- 剩餘其他所有流量

 

這裏重點提到的是 Update 流量,作爲管控的主要原因也同低版本客戶端篡改字段原因一樣。

 

客戶端的問題是,當 apiserver 都是高版本時,客戶端存在高低版本同時操作同一 resource 時會出現篡改,所以我們會推動具有 Update 動作的客戶端進行升級。

 

升級 apiserver 時另外一個問題出現了,從流程上看,升級過程中流量會同時打到 1.16 和 1.18 版本的客戶端上,這樣即使客戶端高版本,通過不同版本的 apiserver 寫操作同一 resource 同樣會出現篡改現象。

 

多版本 apiserver 交叉訪問

此問題,我們同樣按照前文提到的 resource 變化的類型來講述。

 

- 字段變化

字段變化包含增加、刪除和修改三種,對於刪除和修改會在新的 GVK 中出現,所以我們只考慮增加的情形。如下圖所示,Kubernetes 的 Pod 在 1.18 版本比 1.16 版本中多了一個字段"NewFiled",升級過程中如果交叉訪問同一 PodA 則會出現 PodA 存儲的數據不斷不斷變化,首先通過 1.18 版本 apiserver 創建 PodA,然後通過 1.16 的 apiserver Update 後 PodA 的新增字段會被刪除,再通過 1.18 版本的 apiserver Update 字段又被填充回來。

針對此此問題,有以下結論:

 

(1)對於字段增加情況,當通過舊版本 apiserver 更新帶有新字段的資源時存在字段默認值被刪除的風險;對於字段刪除和修改兩種情況,無此風險;

 

(2)如果新增字段被用於計算 container hash,但由於 apiserver 升級時 kubelet 還處於 1.16 版本,所以依舊按照 1.16 版本計算 hash,apiserver 交叉變化不會導致容器重建。

 

- 字段默認值變化

字段默認值變化是指,在新舊 apiserver 中,對某個資源字段默認值填充不一致。如下圖所示,1.16 版本的 Kubernetes 中 Pod 字段"FiledKey"默認值爲"default_value_A",到 1.18 版本時該字段默認值變爲"default_value_B",通過 1.18 apiserver 創建 PodA 後,再通過 1.16 版本 apiserver 更新會出現默認值被篡改的問題。這種情況的發生條件相對苛刻,一般 Update 之前會拉下集羣中當前的 Pod 配置,更改關心的字段後重新 Update 回去,這種方式會保持默認值變化的字段值,但是如果用戶不拉取集羣 Pod 配置,直接 Update 就會出現問題。

針對此此問題,有以下結論:

 

- 某個字段在使用默認填充功能時,其值將依賴 apiserver 中 defaulting 值進行變化。

- 如果新增字段被用於計算 container hash,將引發容器重建風險。

 

思考與解決

 

前面介紹了多版本 apiserver 交叉訪問的問題,接下來我們如何解此問題。

 

解決這個問題的本質是管控 Update/patch 兩種操作的流量,說到這裏可能有人會有疑問,通過多版本 apiserver 獲取 resource 豈不是也有字段不同的問題?如果有,那麼 get/watch 流量也需要管控。

 

這裏有個前置事實需要講一下,升級 apiserver 之前會存在多個版本的客戶端,這些客戶端有些能看到字段變化有些看不到,但是升級前是他們是一個穩定狀態。高版本客戶端看不到新增字段時也可以穩定運行,對新增字段帶來的新特性並沒有很強的依賴,對於低版本客戶端壓根兒看不到新增字段更不關心新特性。我們核心目標是保證升級過程中沒有字段篡改的問題來規避升級過程中同一 resource 視圖頻繁切換,帶來的不可控的管控行爲。

 

螞蟻 Sigma 已經落地管控層面的 Service Mesh 能力,那麼在升級過程中利用強大的 mesh 能力來做精細化流量管控,規避了交叉訪問的問題我們升級過程中的黑暗地帶也會變得越來越窄,心裏會踏實很多。

 

【etcd 數據存儲更新】 

 

Kubernetes 中的數據存儲有一套自己完整的理論,這裏我們先簡單介紹下 Kubernetes 中一個資源從請求進入到存入 etcd 的幾次變換,之後再詳細介紹升級過程中可能遇到的問題及我們的思考。

 

Kubernetes資源版本轉換

- apiserver 中的資源版本

Kubernetes 中的 resource 都會有一個 internal version,因爲在整個迭代過程中一個 resource 可能會對應多個 version,比如 deployment 會有extensions/v1beta1,apps/v1。

 

爲了避免出現問題,kube-apiserver 必須要知道如何在每一對版本之間進行轉換(例如,v1⇔v1alpha1,v1⇔v1beta1,v1beta1⇔v1alpha1),因此其使用了一個特殊的 internal version,internal version 作爲一個通用的 version 會包含所有 version 的字段,它具有所有 version 的功能。Decoder 會首先把 creater object 轉換到 internal version,然後將其轉換爲 storage version,storage version 是在 etcd 中存儲時的另一個 version。

 

- apiserver request 處理

一次 request 請求在 apiserver 中的流轉:

  http filter chain          |  =>  |       http handler    auth ⇒ sentinel ⇒ apf         =>          conversion ⇒ admit ⇒ storage

 

一個資源讀取/存儲的過程如下:

數據存儲兼容性

本文將側重講解 Kubernetes 中 API resource 存儲的數據在升級過程中如何保證兼容性的。主要回答以下兩個問題:

 

問題一:

 Kubernetes 中存儲在 etcd 中的 

 API resource 長什麼樣?

 

Kubernetes 中的 resource 都會有一個 internal version,這個 internal version 只用作在 apiserver 內存中數據處理和流轉,並且這個 Internal version 對應的數據是當前 apiserver 支持的多個 GV 的全集,例如 1.16 中支持 apps/v1beta1 和 apps/v1 版本的 deployments。

 

但是存儲到 etcd 時,apiserver 要先將這個 internal 版本轉換爲 storage 版本 storage 版本怎麼確定的呢?

 

如下,分爲兩種情況:

 

- core resource

存儲版本在 apiserver 初始化時確定,針對某個 GroupVersion 分兩步確定其存儲版本:

 

(1)確定與本 GV 使用同一存儲版本 group resource ---> StorageFactory 中靜態定義的 overrides

 

(2)在 group 中選擇優先級最高的 version 作爲存儲版本---> Schema 註冊時按照靜態定義順序獲取

 

- custom resource

自定義 CR 的存儲版本確定在 CRD 的配置中

(詳見:CRD 配置) https://kubernetes.io/zh/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definition-versioning/

 

問題二:

 不同版本的 Kubernetes 中 

 如何做到存儲數據兼容?

 

storage version 在 Kubernetes 版本迭代更新中不是一成不變的,也會不斷的更新。首先我們看一個 Kubernetes 中的存儲版本的升級規則:給定 API 組的 “storage version(存儲版本)”在既支持老版本也支持新版本的 Kubernetes 發佈 版本出來以前不可以提升其版本號。

 

這條規則換句話說,當某個 Kubernetes 版本中某個 resource 的 storage version 有變化時,此版本的 Kubernetes 一定是同時支持新舊兩個 storage version 的。如此加上 Schema 中多個 version 之間的轉換能力,就能輕鬆做到版本的升級和降級了。

 

對於升級或者降級,apiserver 可以動態的識別當前 etcd 中存儲的是什麼版本的數據,並將其轉換爲 Internal 版本,然後寫入到 etcd 時再轉換爲當前升級後的最新的 Storage Version 進行存入。

思考與解決

從上文可以看到,apiserver 具備動態轉換存儲版本的的能力,但是要對舊版本的數據進行一次讀寫操作。動態轉換的能力也不是無限的,在某個 Kubernetes 版本中轉換的版本是當前版本兼容支持的幾個版本。

 

假設某個 resource 數據在 etcd 中一直沒有讀取,此時 Kubernetes的版本已經升了好幾個版本,並且 apiserver 已經不再兼容 etcd 中的版本,當讀取到此數據時就會報錯,首先這條數據是無法再訪問到了,嚴重的可能導致 apiserver crash。

 

因此我們需要在版本升級過程中保證把 etcd 中的數據都轉換爲最新版本。很自然的,大家會想到通過自己實現一個轉換器來解決這個問題,思路沒有問題,但這會給人一種重複造輪子的感覺,因爲 apiserver 在各個版本中已經有了轉換器的能力,我們只需要把原生的轉換能力利用好就行,每次升級後用 apiserver 原有存儲數據轉換能力把數據做一下更新,這樣可以輕鬆規避多次版本升級後數據殘留或不兼容問題。不要小看這個動作,很容易被忽略,可以想象一下在原本就緊張的升級過程中突然出現 apiserver crash 的詭異現象,這個時候內心一定是崩潰的。

 

【升級可回滾】

 

提到升級大家自然地會想到回滾,Kubernetes 集羣的升級類似業務應用的迭代發佈,如果過程出現問題怎麼辦,最快速的止血方式就回退版本

 

一個單體的應用服務很容易做到版本回退,但對於數據中心操作系統的回滾沒有那麼容易,其問題涉及到很多方面,這裏我們認爲以下幾個問題是 Kubernetes 升級回滾中遇到的常見的棘手問題:

 

API 不兼容,導致回退後組件調用失敗

- etcd 中數據存儲不兼容問題

 

API 不兼容

API 兼容性問題前面已經詳細講述了 API 變化的幾種類型,這裏再提下主要爲 API 接口的變化和 Schema 的字段變化。

 

對於 API 接口的變化問題並不大,原因是升級前所有控制器與客戶端與低版本的 apiserver 已經達到一個穩定的狀態,也就是說 API 接口是可用的,所以回退後問題不大。但是通常在升級到高的 apiserver 版本後會有一些新的 GVK 出現,如一些 Alpha 的能力出現或者 Beta 版本的 GV 變成了 GA 版本。

 

一個真實的例子:1.16 到 1.18 升級過程中新增了 v1beta1.discovery.k8s.io 這個 GV,這種情況下低版本的 apiserver 是不識別新版的 GV 的,apiserver 回滾後雖然能正常啓動,但是在執行涉及到這個 GV 的操作時就會出問題,比如刪除一個 namespace 時會刪除這個 ns 下所有的資源,將遍歷所有的 GV,此時會出現卡殼 ns 刪不掉的現象。

 

另外一個是 Schema 的變化,對於回滾其實可以看成另外一種升級,從高版本“升級”到低版本,這個過程遇到的問題與低版本“升級”到高版本是一致的,即高低版本客戶端訪問篡改問題和多版本 apiserver 並存交叉訪問問題,但是客戶端問題在回滾過程中並不存在,因爲高版本的客戶端向下是兼容的。對於交叉訪問的問題,交叉訪問的問題同樣會利用精細化流量控制做規避。

 

etcd 數據存儲不兼容

數據存儲問題在升級過程中遇到,在回滾過程中同樣遇到,其核心在當一個 resource 的存儲版本在高版本的 apiserver 中發生了變化,並且新的存儲 GV 在低版本的 apiserver 中不識別,導致回退後通過舊版本的 apiserver 獲取對應資源時發生錯誤,這個錯誤就是發生在 Storagte Version 到 Internel Version 轉換過程中。

 

一個例子:在 1.16 中 csinodes 的存儲版本爲 v1beta1,到 1.18 中升級成了 v1 版本,如果從 1.18 直接回退到 1.16,csinode 這個資源獲取會出錯,原因是 1.16 的 apiserver 中壓根兒沒有這個 v1 版本的 csinode。

講到這裏可能會有人問,爲什麼要跨版本升級呢?

 

上述這個問題如果是從 1.16 到 1.17 再到 1.18 逐個版本升級就不會出現了,這個想法非常好,但對於螞蟻 Sigma Kubernetes 這種體量來講頻繁的升級難度較大,這也是我們做此事的原生動力,將升級變得更自動化、效率更高,當這個目標實現後此問題也就不復存在了,在當前階段回退存儲版本不兼容問題仍然棘手。

 

思考與解決

升級本身就是一次引入衆多變量的操作,我們儘量做到在變化中找到一條能把控的路子,最基本的方法論就是控制變量,所以對於 API 兼容性問題,我們核心的原則爲:新特性沒有必要開啓的先進性壓制,保證可回滾

 

壓制的主要目標有兩個:

 

- 高版本 apiserver 中新增的 GVK

保證它們在升級的這個版本中不會出現


- etcd 中的數據的存儲版本

存儲版本對用戶是透明的,我們也要保證壓制調整對用戶也是無感的,調整和壓制的手段可以通過對 apiserver 代碼做兼容性調整來實現。

 

對於其他兼容性問題,目前沒有很好的方案解決,當前我們的主要通過升級回滾 e2e 測試暴露問題,針對不兼容的問題做相應兼容性修改。

 

兼容性壓制的手段只存在於升級過程中,也是升級過程中臨時現象。壓制調整的時候我們需要充分的考量是否會引入其他不可控問題,這個要具體看 GVK 自身的變化來定。當然,一切還要從理論回到實踐,充分的 e2e 測試也是需要的。有了理論和測試兩把利刃的加持,我相信兼容性問題會迎刃而解。

 

以上是升級過程中遇到的三個棘手的問題,以及相關的解決思路,接下來介紹下升級後的保障工作。

 

 

PART. 4

升級後

 

大版本升級時無法保證 100% 的客戶端都升級到對應的最新版本。雖然升級前我們會推動 Update 流量的客戶端進行升級,但是可能做不到 100% 升級,更重要的,升級後可能也會出現某個用戶用低版本的客戶端進行訪問。我們期望通過 webhook 能夠避免升級後低版本客戶端意外篡改 resource 字段,達到真正的升級無損的目的。

 

字段管控主要原則一句話總結:防止默認值變化的字段,被用戶使用低版本客戶端以 Update 的方式修改爲新的 default 值。

 

【字段管控】

 

殘留問題

字段管控的最大挑戰是,如何準確的識別出用戶是否是無意篡改字段。判斷用戶是否無意篡改需要拿到兩個關鍵信息:

 

- 用戶原始請求內容

用戶原始請求內容是判斷用戶是否無意篡改的關鍵,如果原始請求中有某個字段的內容,說明用戶是明確要修改,此時不需要管控。

 

用戶客戶端版本信息

否則,要看用戶客戶端版本是否低於當前集羣版本,如果不低於集羣版本說明用戶有此字段明確修改不需要管控,如果低於集羣版本這個字段用戶可能看不到就需要管控了。

 

那麼問題來了,如何拿到這兩個信息呢?先說用戶原始請求內容,這個信息按照 Kubernetes 的能力,我們無法通過 webhook 或者其他插件機制很輕鬆的拿到請求內容,apiserver 調用 webhook 時的內容已經是經過版本轉換後的內容。

 

再說用戶客戶端版本信息,這個信息雖然可以從 apiserver 的監控中拿到,當然我們爲了與管控鏈路對接,並不是直接拉取的監控信息,而是在 apiserver 中做了信息補充。

 

思考與解決

解決此問題本質上是理解“用戶原始意圖”,能夠識別出哪些動作是無意的篡改哪些是真正的需求,此動作需要依賴以下兩個信息:

 

- 用戶原始請求信息

- 用戶客戶端版本信息

 

上述兩個信息存在的獲取和準確性問題是我們後續的工作方向之一,當前我們還沒很好的辦法。理解用戶的意圖並且是實時理解是非常困難的,折中辦法是定義一套規則,按照規則來識別用戶是否在使用低版本的客戶端篡改一些核心字段,我們寧可誤殺一千也不放過一個壞操作,因爲造成業務容器的不符合預期,意外行爲很輕鬆就會帶來一個 P 級故障。

 

PART. 5

提升效果

 

以上的這些探索在螞蟻 Sigma 已經實戰,升級效率得到了很大的提升,主要體現在以下幾個方面:

 

- 升級過程不用再停機了,發佈時間縮短爲 0,整個過程避免了大量 Pod 延遲交付,平臺用戶甚至沒有任何體感,即使升級過程中排查問題也可以從容地應對,讓升級過程更安靜、更放鬆、更順滑;

 

- 升級前期推動的客戶端升級數量得到了大幅降低,數量減少了 80%,整體升級推動時間減少了約 90%,減少了 80% 的業務方人力投入,整個升級工作輕鬆了許多;

 

- 升級的過程實現了自動化推進,爲了防止意外發生升級過程還可以隨時實現人工介入,升級過程解放了 Sigma 研發和 SRE 的雙手,可以端起咖啡看進度了;

 

- 升級過程實現流量精準化控制,針對集羣上千個命名空間的流量按照規則實現了灰度測試,針對新版本實例進行幾十次 BVT 測試,從兩眼一抹黑到心中有底氣的轉變還是挺棒的。

 

PART. 6

未來之路

 

整體來講,做好升級核心就是要做好兼容性這件事,同時也要把整個過程做的更自動化一些,觀測性做的更好一些,接下來有幾個方向的工作要繼續進行:

 

1.更精準

當前在管控的信息獲取上還有缺失,在流量的管控上當前採用 namespace 的維度來處理,這些都存在精度不夠的問題。前面也提到過,我們正在進行管控組件 Mesh 化能力建設,未來會有更靈活的細粒度流量管控和數據處理能力。同時,藉助 Mesh 能力,實現管控組件升級過程中多版本流量灰度測試,對升級做到精確、可控。

 

2.平臺化

本文介紹的這些挑戰和技術方案,其實都是升級過程中的一部分,整個過程包含了前期的客戶端最小化升級、核心組件滾動升級和後續的管控,這個過程繁瑣且易出錯,我們期望把這些過程規範化、平臺化,把類似差異化比對、流量管控、流量監控等的工具集成到平臺中,讓升級更方便。

 

3.更高效

社區迭代速度非常快,當前的迭代速度是無法跟上社區,我們通過上述更智能、平臺化的能力,提升基礎設施的升級速度。當然,升級的速度也與集羣的架構有非常大的關係,後續螞蟻會走向聯邦集羣的架構,在聯邦架構下可以對特定的用戶 API 做向前兼容和轉換,由此可以極大地解耦客戶端與 apiserver 的升級關係。

 

對於螞蟻 Sigma 規模級別的 Kubernetes 集羣來講升級不是一件容易的事,Sigma 作爲螞蟻最核心的運行底座,我們想做到通過技術手段讓基礎設施的迭代升級達到真正的無感、無損,讓用戶不再等待,讓自己不再焦慮。面對 Kubernetes 這個龐然大物要實現上述目標頗有挑戰性,但這並不能阻止我們探索的步伐。道長且阻,行則將至,作爲全球 Kubernetes 規模化建設頭部的螞蟻集團將繼續向社區輸出更穩定、更易用的技術,助力雲原生成爲技術驅動發展的核心動力。

 

螞蟻 Sigma 團隊致力於規模化雲原生調度平臺的建設,爲業務提供更快更好更穩的容器資源交付,近期我們在集羣穩定性、高性能方面也取得了顯著的成果,歡迎大家相互交流。

 

「參考資料」

 

[1] 《Kubernetes API 策略》

https://kubernetes.io/docs/reference/using-api/deprecation-policy/

[2] 《Kubernetes 1.16 版本介紹》

https://kubernetes.io/blog/2019/07/18/api-deprecations-in-1-16/

[3] 《Kubernetes 集羣正確升級姿勢》

https://www.cnblogs.com/gaorong/p/11266629.html

 

求賢若渴:

 

螞蟻集團 Kubernetes 集羣調度系統支撐了螞蟻集團在線、實時業務的百萬級容器資源調度, 向上層各類金融業務提供標準的容器服務及動態資源調度能力, 肩負螞蟻集團資源成本優化的責任。我們有業界規模最大 Kubernetes 集羣,最深入的雲原生實踐,最優秀的調度技術。

 

歡迎有意在 Kubernetes/雲原生/容器/內核隔離混部/調度/集羣管理深耕的同學加入,北京、上海、杭州期待大家的加入。

 

聯繫郵箱:  [email protected]

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章