極簡容器化交付 | 部署元件分析
之前給大家講了構建映象的原理,那麼有了映象之後,我們怎麼樣能將它快速的部署到kuberentes叢集上呢?
早期,大家都習慣於使用kubernetes介面,或者cli命令列來完成這些操作,但是yaml檔案語法的複雜性、大量容器和kuernetes的概念,讓人難以理解,無疑成為容器化交付路上的又不障礙。
為了解決這些問題,華為雲容器服務推出了嚮導式映象部署,通過一步步引導、對複雜概念的遮蔽或抽象,在一定程度上降低了使用者首次部署映象的難度,但是靈活度相對較差,對於經常變更版本的場景,還是需要使用原生介面進行操作,使得整體研發交付流程的複雜度並未降低太多。
鑑於此現狀華為雲容器團隊又推出了一款易用性更好、自動化程度更高的服務產品——容器交付流水線,它以容器技術為基礎,踐行DevOps的理念,圍繞容器業務端到端場景提供持續整合和持續交付能力,包括程式碼編譯、映象構建與交付、自動化部署、升級回滾等能力,並提供全量能力介面,便於與企業已有流程相結合,同時介面遮蔽底層容器概念,對介面進行了二次封裝,介面定義更貼近於CI/CD業務概念,使得熟悉CI/CD流程的使用者能快速切換。
釋出元件作為該產品的一個重要組成部分,其直接影響了整個釋出週期,我們今天就跟大家一起聊聊該部件的一些實現原理。
01
Kubernetes Deployment
Kubernetes Deployment提供了官方的用於更新Pod和ReplicaSet(下一代的Replication Controller)的方法,我們可以在Deployment物件中填寫我們需要用到的配置和版本資訊,Deployment控制器將現在的實際狀態轉換成我們所期望的狀態,例如,將nginx:v1.0升級成nginx:v2.0,我們只需建立一個Deployment,Kubernetes會按照Deployment自動進行升級。而隨著Kubernetes的迭代更新,目前雲容器引擎服務提供了幾個版本的叢集,那麼如何使得部署元件支援不同的叢集版本呢?由於我們的CI/CD的工具提供了deployment的yaml頁面編輯,部署元件會根據deployment中apiversion即:apps/v1, apps/v1beta1, extensions/v1beta1。提供不同版本的API介面。自行封裝介面使得釋出元件自主能動性強,免於受制於第三方庫。
02
如何判斷髮布成功?
解決了版本問題,還有一個最重要的問題,是如何判斷元件釋出結果呢?對於一個CI/CD工具,我們判斷工作負載執行成功的標準並不僅僅是Pod處於running狀態又或者工作負載處於可用狀態。我們需要保證的是工作負載執行的映象或者配置是新版本的。因此我們判斷成功的標誌應該是新起的pod處於running狀態,那如何找到這些新起的pod呢?下圖展示了Deployment,ReplicaSet和Pod之間的關係,以無狀態工作負載為例,我們通過查詢deployment中Annotations的"deployment.kubernetes.io/revision",根據這個revision尋找Deployment控制的ReplicaSet。最後根據ReplicaSet的label去尋找這些新起的pod。我們已經找到這些新起的Pod了,那麼現在就需要對pod的狀態進行分析了。
在K8s原始碼中PodPhase欄位表示了pod的不同階段:
Pending: k8s已經接受建立pod的請求,但是容器並沒有啟動成功,這個階段包括排程之前的,下載映象等。
Running: pod已經繫結到node節點,並且所有的容器已經啟動成功,或者至少有一個容器在執行,或者在重啟中。
Succeeded: pod中的所有的容器已經正常的自行退出,並且k8s永遠不會自動重啟這些容器。
Failed: pod中的所有容器已經終止,並且至少有一個容器已經終止於失敗(退出非零退出程式碼或被系統停止)。
Unknown: 由於一些未知的原因,無法獲得pod的狀態,通常是因為pod的主機通訊錯誤。
而以上四個階段只是一個粗略狀態階段。而對於每一個階段都有更詳細的pod conditions資訊,podcondition陣列的元素都包含了型別和狀態欄位,這個型別分為以下四種:
"Ready":Pod能夠提供服務
"PodScheduled":pod正處於排程中
"Unschedulable":排程程式現在無法排程Pod,例如由於缺乏資源或其他限制;
"Initialized":所有pod的容器初始化已經完成。
"ContainersReady":pod中的容器都已準備好。
其中狀態欄位用"true" 表示處於,"false"表示不處於,我們發現當階段為Running, condition中Ready狀態為True時, 即表示pod中的容器可以提供服務了。因此,我們現在就可以依次判斷新起的pod是否升級成功了。
03
如何判斷髮布失敗?
現在我們能夠判斷成功了,下一步就需要對失敗的狀態進行一個判斷,其實理論上負載的成功或者失敗不是一個確定性的東西,k8s本身具有重試機制,也存在概率性排程失敗的情況。所以一開始我們考慮的是通過一個超時機制來判斷髮布失敗的情況。但後面分析考慮到,釋出作為一個重要的元件,需要第一時間知道問題所在,以致可以快速進行版本回退等操作。
對於容器的狀態,pod中的containerstatus包含了更詳細的當前容器的狀態。其state欄位表示容器當前的狀態,state分為三種狀態:等待中,執行中和已終止,對應三種不同型別的結構體。等待中的結構體包含了處於非執行狀態的原因和資訊。其他結構體就不在此一一贅述。結合podphase、podcondition、以及containerstatus,我們總結了如下幾個升級異常狀態(如有不全,歡迎補充):
PodPhase == Failed
PodPhase == Succeeded
ContainerStatuses 中有一項為state.waiting 並且 waiting 的原因不是ContainerCreating 或者 PodInitializing(ContainerCreating表示容器建立中,PodInitializing表示pod初始化中)
ContainerStatuses 中有一項為state.running 並且 containerStatuses.ready == false
Conditions中存在一項的type為Ready且status為False且Containerstatues存在
Conditions中存在一項的type為PodScheduled且status為False:排程失敗
由於pod的狀態是動態變化的,為了保證釋出結果的準確性,我們選擇pod超過3次時置為失敗狀態,即釋出失敗。
看完上面這些原理解析,您是不是有什麼收穫,或者有什麼想法與我們交流呢?請在下方留下您寶貴意見和建議吧?說不定下個版本,您的創意就會出現在我們的產品裡。