略干,Kubernetes 集群二進制無損升級實踐
作者:vivo互聯(lián)網(wǎng)服務(wù)器團隊-Shu Yingya
一、背景
活躍的社區(qū)和廣大的用戶群,使 Kubernetes 仍然保持3個月一個版本的高頻發(fā)布節(jié)奏。高頻的版本發(fā)布帶來了更多的新功能落地和 bug 及時修復(fù),但是線上環(huán)境業(yè)務(wù)長期運行,任何變更出錯都可能帶來巨大的經(jīng)濟損失,升級對企業(yè)來說相對吃力,緊跟社區(qū)更是幾乎不可能,因此高頻發(fā)布和穩(wěn)定生產(chǎn)之間的矛盾需要容器團隊去衡量和取舍。

vivo 互聯(lián)網(wǎng)團隊建設(shè)大規(guī)模 Kubernetes 集群以來,部分集群較長時間一直使用 v1.10 版本,但是由于業(yè)務(wù)容器化比例越來越高,對大規(guī)模集群穩(wěn)定性、應(yīng)用發(fā)布的多樣性等訴求日益攀升,集群升級迫在眉睫。集群升級后將解決如下問題:
高版本集群在大規(guī)模場景做了優(yōu)化,升級可以解決一系列性能瓶頸問題。
高版本集群才能支持 OpenKruise 等 CNCF 項目,升級可以解決版本依賴問題。
高版本集群增加的新特性能夠提高集群資源利用率,降低服務(wù)器成本同時提高集群效率。
公司內(nèi)部維護多個不同版本集群,升級后減少集群版本碎片化,進一步降低運維成本。
這篇文章將會從0到1的介紹 vivo 互聯(lián)網(wǎng)團隊支撐在線業(yè)務(wù)的集群如何在不影響原有業(yè)務(wù)正常運行的情況下從 v1.10 版本升級到 v1.17 版本。之所以升級到 v1.17 而不是更高的 v1.18 以上版本, 是因為在 v1.18 版本引入的代碼變動 [1] 會導(dǎo)致 extensions/v1beta1 等高級資源類型無法繼續(xù)運行(這部分代碼在 v1.18 版本刪除)。
二、無損升級難點
容器集群搭建通常有二進制 systemd 部署和核心組件靜態(tài) Pod 容器化部署兩種方式,集群 API 服務(wù)多副本對外負載均衡。兩種部署方式在升級時沒有太大區(qū)別,二進制部署更貼合早期集群,因此本文將對二進制方式部署的集群升級做分享。
對二進制方式部署的集群,集群組件升級主要是二進制的替換、配置文件的更新和服務(wù)的重啟;從生產(chǎn)環(huán)境 SLO 要求來看,升級過程務(wù)必不能因為集群組件自身邏輯變化導(dǎo)致業(yè)務(wù)重啟。因此升級的難點集中在下面幾點:
首先,當前內(nèi)部集群運行版本較低,但是運行容器數(shù)量卻很多,其中部分仍然是單副本運行,為了不影響業(yè)務(wù)運行,需要盡可能避免容器重啟,這無疑是升級中最大的難點,而在 v1.10 版本和 v1.17 版本之間,kubelet 關(guān)于容器 Hash 值計算方式發(fā)生了變化,也就是說一旦升級必然會觸發(fā) kubelet 重新啟動容器。
其次,社區(qū)推薦的方式是基于偏差策略 [2] 的升級以保證高可用集群升級同時不會因為 API resources 版本差異導(dǎo)致 kube-apiserve 和 kubelet 等組件出現(xiàn)兼容性錯誤,這就要求每次升級組件版本不能有2個 Final Release 以上的偏差,比如直接從 v1.11 升級至 v1.13是不推薦的。
再次,升級過程中由于新特性的引入,API 兼容性可能引發(fā)舊版本集群的配置不生效,為整個集群埋下穩(wěn)定性隱患。這便要求在升級前盡可能的熟悉升級版本間的 ChangeLog,排查出可能帶來潛在隱患的新特性。
三、無損升級方案
針對前述的難點,本節(jié)將逐個提出針對性解決方案,同時也會介紹升級后遇到的高版本 bug 和解決方法。希望關(guān)于升級前期兼容性篩查和升級過程中排查的問題能夠給讀者帶來啟發(fā)。
3.1 升級方式
在軟件領(lǐng)域,主流的應(yīng)用升級方式有兩種,分別是原地升級和替換升級。目前這兩種升級方式在業(yè)內(nèi)互聯(lián)網(wǎng)大廠均有采用,具體方案選擇與集群上業(yè)務(wù)有很大關(guān)系。
替換升級
1)Kubernetes 替換升級是先準備一個高版本集群,對低版本集群通過逐個節(jié)點排干、刪除最后加入新集群的方式將低版本集群內(nèi)節(jié)點逐步輪換升級到新版本。
2)替換升級的優(yōu)點是原子性更強,逐步升級各個節(jié)點,升級過程不存在中間態(tài),對業(yè)務(wù)安全更有保障;缺點是集群升級工作量較大,排干操作對pod重啟敏感度高的應(yīng)用、有狀態(tài)應(yīng)用、單副本應(yīng)用等都不友好。
原地升級
1)Kubernetes 原地升級是對節(jié)點上服務(wù)如 kube-controller-manager、 kubelet 等組件按照一定順序批量更新,從節(jié)點角色維度批量管理組件版本。
2)原地升級的優(yōu)點是自動化操作便捷,并且通過適當?shù)男薷哪軌蚝芎玫谋WC容器的生命周期連續(xù)性;缺點是集群升級中組件升級順序很重要,升級中存在中間態(tài),并且一個組件重啟失敗可能影響后續(xù)其他組件升級,原子性差。

vivo 容器集群上運行的部分業(yè)務(wù)對重啟容忍度較低,盡可能避免容器重啟是升級工作的第一要務(wù)。當解決好升級版本帶來的容器重啟后,結(jié)合業(yè)務(wù)容器化程度和業(yè)務(wù)類型不同,因地制宜的選擇升級方式即可。二進制部署集群建議選擇原地升級的方式,具有時間短,操作簡捷,單副本業(yè)務(wù)不會被升級影響的好處。
3.2 跨版本升級
由于Kubernetes 本身是基于 API 的微服務(wù)架構(gòu),Kuberntes 內(nèi)部架構(gòu)也是通過 API 的調(diào)用和對資源對象的 List-Watch 來協(xié)同資源狀態(tài),因此社區(qū)開發(fā)者在設(shè)計 API 時遵循向上或向下兼容的原則。這個兼容性規(guī)則也是遵循社區(qū)的偏差策略 [2],即 API groups 棄用、啟用時,對于 Alpha 版本會立即生效,對于 Beta 版本將會繼續(xù)支持3個版本,超過對應(yīng)版本將導(dǎo)致 API resource version 不兼容。例如 kubernetes 在 v1.16 對 Deployment 等資源的 extensions/v1beta1 版本執(zhí)行了棄用,在v1.18 版本從代碼級別執(zhí)行了刪除,當跨3個版本以上升級時會導(dǎo)致相關(guān)資源無法被識別,相應(yīng)的增刪改查操作都無法執(zhí)行。
如果按照官方建議的升級策略,從 v1.10 升級到 v1.17 需要經(jīng)過至少 7 次升級,這對于業(yè)務(wù)場景復(fù)雜的生產(chǎn)環(huán)境來說運維復(fù)雜度高,業(yè)務(wù)風險大。
對于類似的 API breaking change 并不是每個版本都會存在,社區(qū)建議的偏差策略是最安全的升級策略,經(jīng)過細致的 Change Log 梳理和充分的跨版本測試,我們確認這幾個版本之間不能存在影響業(yè)務(wù)運行和集群管理操作的 API 兼容性問題,對于 API 類型的廢棄,可以通過配置 apiserver 中相應(yīng)參數(shù)來啟動繼續(xù)使用,保證環(huán)境業(yè)務(wù)繼續(xù)正常運行。
3.3 避免容器重啟
在初步驗證升級方案時發(fā)現(xiàn)大量容器都被重建,重啟原因從升級后 kubelet 組件日志看到是 "Container definition changed"。結(jié)合源碼報錯位于 pkg/kubelet/kuberuntime_manager.go 文件 computePodActions 方法,該方法用來計算 pod 的 spec 哈希值是否發(fā)生變化,如果變化則返回 true,告知 kubelet syncPod 方法觸發(fā) pod 內(nèi)容器重建或者 pod 重建。
kubelet 容器 Hash 計算;
func (m *kubeGenericRuntimeManager) computePodActions(pod *v1.Pod, podStatus *kubecontainer.PodStatus) podActions {restart := shouldRestartOnFailure(pod)if _, _, changed := containerChanged(&container, containerStatus); changed {message = fmt.Sprintf("Container %s definition changed", container.Name)// 如果 container spec 發(fā)生變化,將會強制重啟 container(將 restart 標志位設(shè)置為 true)restart = true}...if restart {message = fmt.Sprintf("%s, will be restarted", message)// 需要重啟的 container 加入到重啟列表changes.ContainersToStart = append(changes.ContainersToStart, idx)}}func containerChanged(container *v1.Container, containerStatus *kubecontainer.ContainerStatus) (uint64, uint64, bool) {// 計算 container spec 的 Hash 值expectedHash := kubecontainer.HashContainer(container)return expectedHash, containerStatus.Hash, containerStatus.Hash != expectedHash}
相對于 v1.10 版本,v1.17 版本在計算容器 Hash 時使用的是 container 結(jié)構(gòu) json 序列化后的數(shù)據(jù),而不是 v1.10 版本使用 container struct 的結(jié)構(gòu)數(shù)據(jù)。而且高版本 kubelet 中對容器的結(jié)構(gòu)也增加了新的屬性,通過 go-spew 庫計算出結(jié)果自然不一致,進一步向上傳遞返回值使得 syncPod 方法觸發(fā)容器重建。
那是否可以通過修改 go-spew 對 container struct 的數(shù)據(jù)結(jié)構(gòu)剔除新增的字段呢? 答案是肯定的,但是卻不是優(yōu)雅的方式,因為這樣對核心代碼邏輯侵入較為嚴重,以后每個版本的升級都需要定制代碼,并且新增的字段越來越多,維護復(fù)雜度也會越來越高。換個角度,如果在升級過渡期間將屬于舊版本集群 kubelet 創(chuàng)建的 Pod 跳過該檢查,則可以避免容器重啟。
和圈內(nèi)同事交流后發(fā)現(xiàn)類似思路在社區(qū)已有實現(xiàn),本地創(chuàng)建一個記錄舊集群版本信息和啟動時間的配置文件,kubelet 代碼中維護一個 cache 讀取配置文件,在每個 syncPod 周期中,當 kubelet 發(fā)現(xiàn)自身 version 高于 cache 中記錄的 oldVersion, 并且容器啟動時間早于當前 kubelet 啟動時間,則會跳過容器 Hash 值計算。升級后的集群內(nèi)運行定時任務(wù)探測 Pod 的 containerSpec 是否與高版本計算方式計算得到 Hash 結(jié)果全部一致,如果是則可以刪除掉本地配置文件,syncPod 邏輯恢復(fù)到與社區(qū)完全一致。
具體方案參考?這種實現(xiàn)的好處是對原生 kubelet 代碼侵入小,沒有改變核心代碼邏輯,而且未來如果還需要升級高版本也可以復(fù)用該代碼。如果集群內(nèi)所有 Pod 都是當前版本 kubelet 創(chuàng)建,則會恢復(fù)到社區(qū)自身的邏輯。

3.4 Pod 非預(yù)期驅(qū)逐問題
Kubernetes 雖然迭代了十幾個版本,但是每個迭代社區(qū)活躍度仍然很高,保持著每個版本大約30個關(guān)于拓展性增強和穩(wěn)定性提升的新特性。選擇升級很大一方面原因是引入很多社區(qū)開發(fā)的新特性來豐富集群的功能與提升集群穩(wěn)定性。新特性開發(fā)也是遵循偏差策略,跨大版本升級很可能導(dǎo)致在部分配置未加載的情況下啟用新特性,這就給集群帶來穩(wěn)定性風險,因此需要梳理影響 Pod 生命周期的一些特性,尤其關(guān)注控制器相關(guān)的功能。
這里注意到在 v1.13 版本引入的 TaintBasedEvictions 特性用于更細粒度的管理 Pod 的驅(qū)逐條件。在 v1.13基于條件版本之前,驅(qū)逐是基于 NodeController 的統(tǒng)一時間驅(qū)逐,節(jié)點 NotReady 超過默認5分鐘后,節(jié)點上的 Pod 才會被驅(qū)逐;在 v1.16 默認開啟 TaintBasedEvictions 后,節(jié)點 NotReady 的驅(qū)逐將會根據(jù)每個 Pod 自身配置的 TolerationSeconds ?來差異化的處理。
舊版本集群創(chuàng)建的 Pod 默認沒有設(shè)置 TolerationSeconds,一旦升級完畢 TaintBasedEvictions 被開啟,節(jié)點變成 NotReady 后 5 秒就會驅(qū)逐節(jié)點上的 Pod。對于短暫的網(wǎng)絡(luò)波動、kubelet 重啟等情況都會影響集群中業(yè)務(wù)的穩(wěn)定性。

TaintBasedEvictions 對應(yīng)的控制器是按照 pod 定義中的 tolerationSeconds 決定 Pod 的驅(qū)逐時間,也就是說只要正確設(shè)置 Pod 中的 tolerationSeconds 就可以避免出現(xiàn) Pod 的非預(yù)期驅(qū)逐。
在v1.16 版本社區(qū)默認開啟的 DefaultTolerationSeconds 準入控制器基于 k8s-apiserver 輸入?yún)?shù) default-not-ready-toleration-seconds 和 default-unreachable-toleration-seconds 為 Pod 設(shè)置默認的容忍度,以容忍 notready:NoExecute 和 unreachable:NoExecute 污點。?
新建 Pod 在請求發(fā)送后會經(jīng)過 DefaultTolerationSeconds 準入控制器給 pod 加上默認的 tolerations。但是這個邏輯如何對集群中已經(jīng)創(chuàng)建的 Pod 生效呢?查看該準入控制器發(fā)現(xiàn)除了支持 create 操作,update 操作也會更新 pod 定義觸發(fā) DefaultTolerationSeconds 插件去設(shè)置 tolerations。因此我們通過給集群中已經(jīng)運行的 Pod 打 label 就可以達成目的。
tolerations:effect: NoExecutekey: node.kubernetes.io/not-readyoperator: ExiststolerationSeconds: 300effect: NoExecutekey: node.kubernetes.io/unreachableoperator: ExiststolerationSeconds: 300
3.5 Pod MatchNodeSelector
為了判斷升級時 Pod 是否發(fā)生非預(yù)期的驅(qū)逐以及是否存在 Pod 內(nèi)容器批量重啟,有腳本去實時同步節(jié)點上非Running狀態(tài)的Pod和發(fā)生重啟的容器。
在升級過程中,突然多出來數(shù)十個 pod 被標記為 MatchNodeSelector 狀態(tài),查看該節(jié)點上業(yè)務(wù)容器確實停止。kubelet 日志中看到如下錯誤日志;
predicate.go:132] Predicate failed on Pod: nginx-7dd9db975d-j578s_default(e3b79017-0b15-11ec-9cd4-000c29c4fa15), for reason: Predicate MatchNodeSelector failedkubelet_pods.go:1125] Killing unwanted pod "nginx-7dd9db975d-j578s"
經(jīng)分析,Pod 變成 MatchNodeSelector 狀態(tài)是因為 kubelet 重啟時對節(jié)點上 Pod 做準入檢查時無法找到節(jié)點滿足要求的節(jié)點標簽,pod 狀態(tài)就會被設(shè)置為 Failed 狀態(tài),而 Reason 被設(shè)置為 MatchNodeSelector。在 kubectl 命令獲取時,printer 做了相應(yīng)轉(zhuǎn)換直接顯示了Reason,因此我們看到 Pod 狀態(tài)是 MatchNodeSelector。通過給節(jié)點加上標簽,可以讓 Pod 重新調(diào)度回來,然后刪除掉 MatchNodeSelector 狀態(tài)的 Pod 即可。
建議在升級前寫腳本檢查節(jié)點上 pod 定義中使用的 NodeSelector 屬性節(jié)點是否都有對應(yīng)的 Label。?
3.6 無法訪問 kube-apiserver?
預(yù)發(fā)環(huán)境升級后的集群運行在 v1.17 版本后,突然有節(jié)點變成 NotReady 狀態(tài)告警,分析后通過重啟 kubelet 節(jié)點恢復(fù)正常。繼續(xù)分析出錯原因發(fā)現(xiàn) kubelet 日志中出現(xiàn)了大量 use of closed network connection 報錯。在社區(qū)搜索相關(guān) issue 發(fā)現(xiàn)有類似的問題,其中有開發(fā)者描述了問題的起因和解決辦法,并且在 v1.18 已經(jīng)合入了代碼。
問題的起因是 kubelet 默認連接是 HTTP/2.0 長連接,在構(gòu)建 client 到 server的連接時使用的 golang net/http2 包存在 bug,在 http 連接池中仍然能獲取到 broken 的連接,也就導(dǎo)致 kubelet 無法正常與 kube-apiserver 通信。
golang社區(qū)通過增加 http2 連接健康檢查規(guī)避這個問題,但是這個 fix 仍然存在 bug ,社區(qū)在 golang v1.15.11 版本徹底修復(fù)。我們內(nèi)部通過 backport 到 v1.17 分支,并使用 golang 1.15.15 版本編譯二進制解決了此問題。
3.7 TCP 連接數(shù)問題
在預(yù)發(fā)布環(huán)境測試運行期間,偶然發(fā)現(xiàn)集群每個節(jié)點 kubelet 都有近10個長連接與 kube-apiserver 通信,這與我們認知的 kubelet 會復(fù)用連接與 kube-apiserver 通信明顯不符,查看 v1.10 版本環(huán)境也確實只有1個長連接。這種 TCP 連接數(shù)增加情況無疑會對 LB 造成了壓力,隨著節(jié)點增多,一旦 LB 被拖垮,kubelet 無法上報心跳,節(jié)點會變成 NotReady,緊接著將會有大量 Pod 被驅(qū)逐,后果是災(zāi)難性的。因此除去對 LB 本身參數(shù)調(diào)優(yōu)外,還需要定位清楚kubelet 到 kube-apiserver 連接數(shù)增加的原因。
在本地搭建的 v1.17.1 版本 kubeadm 集群 kubelet 到 kube-apiserver 也僅有1個長連接,說明這個問題是在 v1.17.1 到升級目標版本之間引入的,排查后(問題)發(fā)現(xiàn)增加了判斷邏輯導(dǎo)致 kubelet 獲取 client 時不再從 cache 中獲取緩存的長連接。transport 的主要功能其實就是緩存了長連接,用于大量 http 請求場景下的連接復(fù)用,減少發(fā)送請求時 TCP(TLS) 連接建立的時間損耗。在該 PR 中對 transport 自定義 RoundTripper 的接口,一旦 tlsConfig 對象中有 Dial 或者 Proxy 屬性,則不使用 cache 中的連接而新建連接。
// client-go 從 cache 獲取復(fù)用連接邏輯func tlsConfigKey(c *Config) (tlsCacheKey, bool, error) {...if c.TLS.GetCert != nil || c.Dial != nil || c.Proxy != nil {// cannot determine equality for functionsreturn tlsCacheKey{}, false, nil}...}func (c *tlsTransportCache) get(config *Config) (http.RoundTripper, error) {key, canCache, err := tlsConfigKey(config)...if canCache {// Ensure we only create a single transport for the given TLS optionsc.mu.Lock()defer c.mu.Unlock()// See if we already have a custom transport for this configif t, ok := c.transports[key]; ok {return t, nil}}...}// kubelet 組件構(gòu)建 client 邏輯func buildKubeletClientConfig(ctx context.Context, s *options.KubeletServer, nodeName types.NodeName) (*restclient.Config, func(), error) {...kubeClientConfigOverrides(s, clientConfig)closeAllConns, err := updateDialer(clientConfig)...return clientConfig, closeAllConns, nil}// 為 clientConfig 設(shè)置 Dial屬性,因此 kubelet 構(gòu)建 clinet 時會新建 transportfunc updateDialer(clientConfig *restclient.Config) (func(), error) {if clientConfig.Transport != nil || clientConfig.Dial != nil {return nil, fmt.Errorf("there is already a transport or dialer configured")}d := connrotation.NewDialer((&net.Dialer{Timeout: 30 * time.Second, KeepAlive: 30 * time.Second}).DialContext)clientConfig.Dial = d.DialContextreturn d.CloseAll, nil
在這里構(gòu)建 closeAllConns 對象來關(guān)閉已經(jīng)處于 Dead 但是尚未 Close 的連接,但是上一個問題通過升級 golang 版本解決了這個問題,因此我們在本地代碼分支回退了該修改中的部分代碼解決了 TCP 連接數(shù)增加的問題。
最近追蹤社區(qū)發(fā)現(xiàn)已經(jīng)合并了解決方案 ,通過重構(gòu) client-go 的接口實現(xiàn)對自定義 RESTClient 的 TCP 連接復(fù)用。
四、無損升級操作
跨版本升級最大的風險是升級前后對象定義不一致,可能導(dǎo)致升級后的組件無法解析保存在 ETCD 數(shù)據(jù)庫中的對象;也可能是升級存在中間態(tài),kubelet 還未升級而控制平面組件升級,存在上報狀態(tài)異常,最壞的情況是節(jié)點上 Pod 被驅(qū)逐。這些都是升級前需要考慮并通過測試驗證的。
經(jīng)過反復(fù)測試,上述問題在 v1.10 到 v1.17 之間除了部分廢棄的 API Resources 通過增加 kube-apiserver 配置方式其他情況暫時不存在。為了保證升級時及時能處理未覆蓋到的特殊情況,強烈建議升級前備份 ETCD 數(shù)據(jù)庫,并在升級期間停止控制器和調(diào)度器,避免非預(yù)期的控制邏輯發(fā)生(實際上這里應(yīng)該是停止 controller manager 中的部分控制器,不過需要修改代碼編譯臨時 controller manager ,增加了升級流程步驟和管理復(fù)雜度,因此直接停掉了全局控制器)。
除卻以上代碼變動和升級流程注意事項,在替換二進制升級前,就剩下比對新老版本服務(wù)的配置項的區(qū)別以保證服務(wù)成功啟動運行。對比后發(fā)現(xiàn),kubelet 組件啟動時不再支持 --allow-privileged 參數(shù),需要刪除。值得說明的是,刪除不代表高版本不再支持節(jié)點上運行特權(quán)容器,在 v1.15 以后通過 Pod Security Policy 資源對象來定義一組 pod 訪問的安全特征,更細粒度的做安全管控。
基于上面討論的無損升級代碼側(cè)的修改編譯二進制,再對集群組件配置文件中各個配置項修改后,就可以著手線上升級。整個升級步驟為:
備份集群(二進制,配置文件,ETCD數(shù)據(jù)庫等);
灰度升級部分節(jié)點,驗證二進制和配置文件正確性
提前分發(fā)升級的二進制文件;
停止控制器、調(diào)度器和告警;
更新控制平面服務(wù)配置文件,升級組件;
更新計算節(jié)點服務(wù)配置文件,升級組件;
為節(jié)點打 Label 觸發(fā) pod 增加 tolerations 屬性;
打開控制器和調(diào)度器,啟用告警;
集群業(yè)務(wù)點檢,確認集群正常。
升級過程中建議節(jié)點并發(fā)數(shù)不要太高,因為大量節(jié)點 kubelet 同時重啟上報信息,對 kube-apiserver 前面使用的 LB 帶來沖擊,特別情況下可能節(jié)點心跳上報失敗,節(jié)點狀態(tài)會在 NotReady 與 Ready 狀態(tài)間跳動。
五、總結(jié)
集群升級是困擾容器團隊比較長時間的事,在經(jīng)過一系列調(diào)研和反復(fù)測試,解決了上面提到的數(shù)個關(guān)鍵問題后,成功將集群從 v1.10 升級到 v1.17 版本,1000 個節(jié)點的集群分批執(zhí)行升級操作,大概花費 10 分鐘,后續(xù)在完成平臺接口改造后將會再次升級到更高版本。
集群版本升級提高了集群的穩(wěn)定性、增加了集群的擴展性,同時還豐富了集群的能力,升級后的集群也能夠更好的兼容 CNCF 項目。
如開篇所述,按照偏差策略頻繁對大規(guī)模集群升級可能不太現(xiàn)實,因此跨版本升級雖然風險較大,但是也是業(yè)界廣泛采用的方式。在 2021 年中國 KubeCon 大會上,阿里巴巴也有關(guān)于零停機跨版本升級 Kubernetes 集群的分享,主要是關(guān)于應(yīng)用遷移、流量切換等升級關(guān)鍵點的介紹,升級的準備工作和升級過程相對復(fù)雜。相對于阿里巴巴的集群跨版本替換升級方案,原地升級的方式需要在源碼上做少量修改,但是升級過程會更簡單,運維自動化程度更高。
由于集群版本具有很大的可選擇性,本文所述的升級并不一定廣泛適用,筆者更希望給讀者提供生產(chǎn)集群在跨版本升級時的思路和風險點。升級過程短暫,但是升級前的準備和調(diào)研工作是費時費力的,需要對不同版本 Kubernetes 特性和源碼深入探索,同時對 Kubernetes 的 API 兼容性策略和發(fā)布策略擁有完整認知,這樣便能在升級前做出充分的測試,也能更從容面對升級過程中突發(fā)情況。
六、參考鏈接
[1]https://github.com
[2] https://kubernetes.io/version-skew-policy
[3] 具體方案參考:https://github.comstart
[4] 類似的問題: https://github.com/kubernetes
[5] https://github.com/golang/34978
[6] https://github.com/kubernetes/100376
[7] https://github.com/kubernetes/95427
[8] https://github.com/kubernetes/105490
