圖解kubernetes控制器Deployment核心機制

8小時_2020發表於2020-04-02

Deployment是k8s中部署更新的關鍵實現,今天我們一起初探下其關鍵機制包括: 暫停、回滾、擴縮容、更新策略的實現

1. 基礎概念

Deployment本質上其實只是一種部署策略,在瞭解其實現之前,先簡單介紹一下部署系統裡面常見的概念,Deployment裡面的各種引數和設計其實也都是圍繞著這些展開的

1.1 ReplicaSet

image.png Deployment本身並不直接操作Pod,每當其更新的時候通過構建ReplicaSet來進行版本更新,在更新的過程中通過scale up(新的RS)和scale down(舊的RS)來完成

1.2 部署狀態

在k8s的官方文件中主要是介紹了Deployment的三種狀態, 對應的Condition分別為Available、Progressing、ReplicaFailure三種狀態, 並且每個狀態下面又會很有導致對應狀態切換的不同的Reson,Reson可能是運維過程中最需要關注的點

1.3 部署策略

部署策略是Deployment控制ReplicaSet更新的策略,通過對新舊ReplicaSet的擴縮容,再滿足部署策略的情況下,將系統更新至最新的目標狀態,Deployment本身並沒有太多可選的策略,預設只有兩種Recreate和RollingUpdate image.png 在一些大版本產品更新的時候,新舊版本的資料庫模型都不一致的情況下,通常會選擇停服操作,此時可以選擇Recreate即將所有老的副本都幹掉,然後重新建立一批。當然預設情況下大部分業務還是RollingUpdate即滾動更新即可

1.4 可用性與高低水位

部署過程中最常被提到的可能就是可用性問題了,即在更新的過程中(RollingUpdate策略下)需要保證系統中可用的Pod在一個指定的水位,保證對應服務的可用性 image.png 高低水位(deployment並沒有這個詞)其實就是對應的上面的可用性來說的,Deployment通過一些引數讓我們可以自由控制在滾動更新的過程中,我們可以建立的Pod的最多數量(高水位)和可以刪除的最多的Pod(低水位), 從而達到可用性保護的目標

部署的概念就介紹到這裡, 接下來就一起看看Deployment中這些關鍵機制的具體實現

2. 核心實現

Deployment的實現上相對複雜一點,但是從場景上又可以簡單的分為:刪除、暫停、回滾、擴縮容、更新幾個大的場景

2.1 暫停部署

暫停部署是用於中斷Deployment更新流程的一種方式,但由於k8s中是基於事件驅動的最終一致性的系統,這裡的中斷僅僅意味著Deployment層不會進行的進行後續的副本變更,而底層的replicaSet此時如果還沒有達到目標的副本,則就需要繼續更新, 同時在暫停的過程中如果發現並沒有嘗試進行回滾到指定版本的操作,這時候還會進行一些副本的清理工作,即只保留最近的指定數量的歷史副本

2.2 回滾控制

回滾控制裡面的資訊跟其他引數有些不同,其主要是通過在Annotations中儲存的DeprecatedRollbackTo來進行指定版本的回滾 image.png 回滾的實現本質上就是從指定的Revisions中獲取對應的replicaset的Pod模板,去覆蓋當前的Deployment的Pod模板,並且更新Deployment即可, 那如果對應的版本不存在怎麼辦,如果是這種情況,其實就需要你自己去尋找歷史版本了,並且k8s會給新新增一個RollbackRevisionNotFound型別的事件提示你版本不存在 

2.3 擴縮容機制

擴縮容機制主要是指的Deployment的scale操作,在進行Deployment更新之前,會首先檢查對應Deployment的副本的期望是否得到滿足,只有期望的副本數得到滿足,才會進行更新操作,所以在k8s中如果之前進行了擴縮容操作,則在該操作完成之前,是不會進行模板更新的

2.4 Recreate策略

image.png Recreate部署策略在實現上通過兩種機制保證之前的Pod一定被刪除:所有活躍副本都為0和所有Pod都處於(PodFailed和PodSucceeded)兩種狀態下,然後才會建立新的副本,如果對應的副本完全就緒,還會進行清理歷史副本

2.5 RollingUpdate策略

RollingUpdate策略可能是最複雜的部分之一了,裡面很有多的引數控制,都作用於該策略,來一起看下

2.5.1 縮容過多的新版本

首先在更新的時候要做一致性檢測,如果發現新版本的ReplicaSet比當前的deployment設定的副本數目多,則首先幹掉這部分Pod, 同時會根據當前的Deployment的副本數來設定當前的期望副本數DesiredReplicasAnnotation, 並且根據maxSurge來計算當前最大的副本數量MaxReplicasAnnotation, 同時在這個同步ReplicaSet的minReadySeconds

2.5.2 擴容新副本

如果說新副本的數量不足,則就需要根據當前的maxSurege來設定,同時會再次計算當前的RS的所有Pod,如果發現Pod數量過多即超過Deployment的Replicas+maxSurge,則也不會進行操作

        // Find the total number of pods
        currentPodCount := GetReplicaCountForReplicaSets(allRSs)
        // 最大pod數量
        maxTotalPods := *(deployment.Spec.Replicas) + int32(maxSurge)
        // 當前pod數量》總的執行的pod數量
        if currentPodCount >= maxTotalPods {
            // Cannot scale up.
            return *(newRS.Spec.Replicas), nil
        }複製程式碼

否則則就會進行計算允許scale up的數量

        scaleUpCount := maxTotalPods - currentPodCount
        scaleUpCount = int32(integer.IntMin(int(scaleUpCount), int(*(deployment.Spec.Replicas)-*(newRS.Spec.Replicas))))複製程式碼

2.5.3 縮容舊的副本

縮容計數器的演算法計算主要是根據Deployment的Replcas和maxUnavailable(通過surge和maxUnavailable)共同計算而來,最終的公式其實如下,有了縮容的數量,就可以更新舊ReplicaSet的數量了

    minAvailable := *(deployment.Spec.Replicas) - maxUnavailable
    // 新副本不可用數量
    newRSUnavailablePodCount := *(newRS.Spec.Replicas) - newRS.Status.AvailableReplicas
    // 最大縮容大小=所有pod統計-最小不可用-新副本不可用副本
    maxScaledDown := allPodsCount - minAvailable - newRSUnavailablePodCount複製程式碼

整理如下

        最小可用副本     = Deployment的副本-最大不可用副本
        新副本不可用統計= 新副本數量-可用副本數量
        最大縮容數量   = 全部副本Pod計數-最小可用副本-新副本不可用統計複製程式碼

至此我們知道了Deployment擴縮容的核心的副本計算實現,也知道了擴縮容的流程,那還缺什麼呢?答案是狀態

2.5.4 Available狀態

Deployment的狀態主要是由新舊副本以及當前叢集中的Pod決定的,其計算公式如下, 則認為當前可用否則即為不可用

    前可用的副本數量計數>=Deployment的副本數量-最大不可用副本計數複製程式碼
    if availableReplicas >= *(deployment.Spec.Replicas)-deploymentutil.MaxUnavailable(*deployment) {
        minAvailability := deploymentutil.NewDeploymentCondition(apps.DeploymentAvailable, v1.ConditionTrue, deploymentutil.MinimumReplicasAvailable, "Deployment has minimum availability.")
        deploymentutil.SetDeploymentCondition(&status, *minAvailability)
    } else {
         noMinAvailability := deploymentutil.NewDeploymentCondition(apps.DeploymentAvailable, v1.ConditionFalse, deploymentutil.MinimumReplicasUnavailable, "Deployment does not have minimum availability.")
        deploymentutil.SetDeploymentCondition(&status, *noMinAvailability)
    }複製程式碼

2.5.5 Processing狀態

首先並不是所有的Deployment都有該狀態,只有設定了progressDeadlineSeconds引數的才會有該狀態,其主要實在Deployment未完成的時候,進行一些狀態決策,從而避免一個Deployment無期限的執行,其關鍵狀態有兩個即執行中與超時決策, 其流程實現上分為兩步 1)首先如果判斷是正在執行中,就更新LastTransitionTime

condition := util.NewDeploymentCondition(apps.DeploymentProgressing, v1.ConditionTrue, util.ReplicaSetUpdatedReason, msg)

condition.LastTransitionTime = currentCond.LastTransitionTime複製程式碼

2)超時檢測則及時通過記錄之前的轉換時間,然後決策是否超時

    from := condition.LastUpdateTime
    now := nowFn()
    delta := time.Duration(*deployment.Spec.ProgressDeadlineSeconds) * time.Second
    timedOut := from.Add(delta).Before(now)
複製程式碼

2.5.6 ReplicaFailure狀態

 該狀態相對簡單,檢測當前的所有的副本,如果發現有副本失敗,就取最新的一條失敗的資訊來填充Condition

3. 小結

image.png 更新機制的核心實現可能就這些,程式碼實現上還是相對複雜的,主要是集中在為了保證伸縮和更新時為了保證可用性而做了大量的計算,還有很多的邊界條件的處理,先就關注到這裡,裡面的具體的更細的細節,等出問題的時候,再詳細琢磨,今天就到這裡了

kubernetes學習筆記地址: www.yuque.com/baxiaoshi/t…

微訊號:baxiaoshi2020 關注公告號閱讀更多原始碼分析文章 圖解原始碼

相關文章