kubernetes社區很是活躍,每季度都會發佈一個release。可是線上集羣業務可用性要求較高,場景複雜,任何微小的變動都須要很是當心,此時跟隨社區版本進行升級略顯吃力。可是爲了可以使用到最新的一些feature咱們必須不按期進行一些升級操做,在經歷了一次線上集羣的升級操做,踩完一些坑以後,分享一些收穫與感悟。原來的集羣版本是1.10,爲了提升GPU集羣的資源利用率,須要在調度器層面支持一些搶佔調度等新特性,因此升級到1.14,這次升級的集羣規模不是特別大,可是有一些在線任務,仍是須要慎重操做。目前kubernetes社區中全部的工具(包括使用較多的kubeadm,kops等工具)對於生產環境高可用的集羣升級都顯的比較乏力,因此仍是建議手動升級,更加安全可控,出現問題便於定位修復。node
升級策略
升級方式能夠分爲原地升級和異地升級:git
- 原地升級是指在原有集羣直接替換二進制進行升級,此種方式升級方便,運維代價較小,理論上來講多副本的業務不會有downtime,在充分測試的狀況下優先選擇原地升級.
- 異地升級是指新搭建一個如出一轍的高版本集羣,而後手動或自動遷移業務到新集羣。此種方式須要上層提供滾動升級的能力,支持兩個backend,須要額外的開發工做。且默認double一份原集羣pod到新集羣不會對業務形成影響,對於使用ceph等持久化存儲的集羣來講,可能會有問題。但此種方式升級更加安全可控,風險更低。
官方建議升級過程
- 首先閱讀相關
release node
,重點關注其中幾部分: Known Issues,Action Requireed,Deprecations and removals。社區會將一些變化highlight到這裏,閱讀這些變化能夠明確本身須要採起哪些行動。
- kubernetes 建議不斷地進行小版本升級,而不是一次進行大的版本跳躍。具體的兼容策略是: slave組件能夠與master組件最多延遲兩個小版本(minor version),可是不能比master組件新。client不能與master組件落後一個小版本,可是能夠高一個版本,也就是說: v1.3的master能夠與v1.1,v1.2,v1.3的slave組件一塊兒使用,與v1.2,v1.3,v1.4 client一塊兒使用。官方建議每次升級不要跨越兩個版本,升級順序爲: master,addons,salve。
- slave節點的升級是滾動升級,官方建議首先使用
kubectl drain
驅逐pod以後,而後升級kubelet,由於kubelet會有一些狀態信息文件存儲在node節點上,社區並不保證這些狀態文件在版本間的兼容性。
- apiserver升級以前須要確保resource version被正常支持,目前kubernetes會逐步廢棄掉,例如: DaemonSet,Deployment,ReplicaSet 所使用的 extensions/v1beta1,apps/v1beta1,apps/v1beta2 將會在v1.16中徹底廢棄掉,屆時,若是你再去讀取這些版本的資源,apiserver將不會認識這些資源,任何的增刪改查都沒法進行,只能經過
etcdctl
進行刪除。目前社區正在開發遷移工具,而且在支持該工具以前,全部的版本移除操做都會被凍結,因此目前(2019.5.20)來講是相對安全的。
生產實踐升級過程
- 若是採用官方建議的升級策略須要小版本不斷升級,可是線上運維壓力較大,業務場景複雜,大多數狀況下不可能跟隨社區版本不斷升級,因此能夠在充分測試的前提下進行大版本跳躍升級。
- 官方建議升級kubelet以前先將全部pod驅逐,可是此種方式可能會形成業務容器頻繁重啓。例如升級nodeA時pod漂移到未升級節點nodeB,後續升級nodeB可能須要繼續驅逐該pod。爲避免頻繁重啓業務,在充分測試的狀況下不用驅逐,直接原地升級便可。目前(2019.6.5)在master組件升級以後重啓或升級kubelet會致使大部分容器重啓,由於kubelet經過
hash(container spec)
來生成容器的惟一標識,不一樣版本間container spec會發生變化,引發hash值變化,進而致使容器重啓,參見kubernetes/kubernetes: Issue #63814。在不驅逐pod的狀況下原地升級最壞狀況下只有一次重啓,並且volume等信息不會發生變化,對於集羣的擾動也較小。 有能力的同窗能夠研究下如何作到升級kubelet不重啓容器。
- 雖然kubernetes建議先升級master組件,而後再升級node組件,可是實際應用過程當中建議先停掉controller-manager,而後升級master組件,node組件,最後再升級controller-manager,由於controller-manager中會進行一些狀態的調諧(reconcile),對於actual status不符合desire status的對象會觸發一些操做。升級過程當中儘管咱們會進行充分的測試,可是也不免出現一些非預期的狀況下,例如apiserver中某些資源對象的兼容性很差,或者其中的某些字段進行調整,觸發controller-manager執行非預期操做,例如重建一個deployment下全部的pod,更糟糕的是,若是此時kubelet還未升級,就可能不認識新版本一些資源對象中的新增的某些字段,此時老的pod被刪掉了,可是新的pod沒有起來,就形成必定的服務中斷。(後面會有相關的案例)
- 升級過程當中建議調高日誌級別,方便定位問題,在升級完成以後在下降日誌級別。
- 備份etcd數據,並進行故障恢復的演練,做爲升級失敗的最後一道防線。
- 升級以前review集羣狀態,確保全部組件正常工做,review重要業務的實例數,避免副本數爲1,而且適當設置PDB; 須要檢查是否有單個node帶有特殊的label,可能有pod依賴於該label,若是該node異常致使pod也會發生故障(這個坑真的有人踩過:參見:ingress-cordoned)。
升級過程當中發現的「坑」 (已填)
- 上面的實踐3中就是筆者測試的時候發現的一個坑: 目前升級會致使部分controller(daemonset)重建容器,升級以後由於pod spec發生變化,部分controller會建立新的
controllerrevision
,升級更新全部其控制的容器。若是先升級master/controller-manager,由於此時kubelet還未升級,版本較低,會出現不兼容的字段,嚴重狀況下kubelet會reject掉該pod,致使最終該node上此daemonset container退出並沒有法重啓。若是daemonset 設置爲滾動升級,而且maxUnavailable
設置爲1的話,能夠避免必定損失,最多隻容許同時掛掉一個daemonset container。參見kubernetes/kubernetes: Issue #78633。因此最佳實踐仍是升級以前停掉Controller-manager,集羣實際完畢以後最後升級controller-manager。設置全部controller的升級策略爲滾動升級並設置最大不可用實例數。對於上述Issue官方在新版本中已經進行修復並backport到舊版本中。
- 對於一些剛從alpha轉到beta的特性,beta版本是默認開啓的版本,對於這些特性必定要很是當心,可能仍然會存在一些bug,建議仍是手動關閉,對於一些可用性要求較高的集羣建議只使用GA的feature。此次踩的一個坑就是node lease特性,該特性在1.14中恰好是Beta版本默認開啓,這是一個很是有用的feature,對於大規模集羣能夠有效下降apiserver,etcd的負載,可是kubelet中node lease的默認renew週期是hardcode的10s中且不可調整,也就是說此時node與master心跳的頻率只能是10s,此時指定
--node-status-update-frequency
flag沒有任何做用,若是controller-manager中配置的--node-monitor-grace-period
恰好是10s,這時候node會不斷地在ready和non-ready直接搖擺,參見kubernetes/kubernetes issue#80172,該issue已經被筆者給修復掉了 :)。
寫在後面
kubernetes仍是在高速的迭代過程當中,升級過程當中出現不兼容是在所不免的, 惟有搞懂內部的實現機制才能保障集羣的長治久安。發現問題也須要咱們有能力去解決而且反饋到社區中,取之於社區,理應回報於社區。github
reference
Kubernetes Release Versioningapi