中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

深入理解Kubelet核心執行框架

發布時間:2020-06-24 01:17:10 來源:網絡 閱讀:577 作者:阿里系統軟件技術 欄目:建站服務器



Kubernetes已然成為云環境中大規模部署容器化應用的事實標準,而Kubelet作為Kubernetes集群中的節點代理,即節點的守護者,會具體負責其所在節點的波德的生命周期管理,切實保證各個容器應用都按照預期的狀態平穩運行。它會首先獲取分配到本節點的波德的配置信息,再根據這些配置信息調用底層的容器運行時,例如多克爾或者PouchContainer,創建具體的波德,并對這些波德進行監控,保證節點上的所有Pod按照預期的狀態運行。本文將結合Kubelet源碼,對上述過程進行詳細的分析。


本文作者姚增增,花名里奇,Kubernetes社區的資深貢獻者,阿里集團開源富容器引擎PouchContainer維護者,主導并推進了PouchContainer容器技術中CRI接口的設計與實現。從事云原生相關的技術領域,關注容器技術,編排技術,操作系統內核。當前是浙江大學  SEL 實驗室的在讀研究生,個人推崇開源理念。

1.獲取Pod配置

Kubelet有多種途徑獲取本節點需要運行的吊艙的配置信息。最重要的自然是API服務器,其次還能通過指定配置文件的目錄以及訪問特定的HTTP端口獲取.Kubelet會定期對它們進行訪問,獲取波德配置的更新并及時調整位于本節點的Pod的運行狀態。

在Kubelet初始化的時候會創建一個PodConfig對象如下所示:

// kubernetes/pkg/kubelet/config/config.go
type PodConfig struct {
    pods *podStorage
    mux  *config.Mux
    // the channel of denormalized changes passed to listeners
    updates chan kubetypes.PodUpdate
    ...
}

PodConfig本質上是Pod配置信息的一個復用器。內含的mux會對各種Pod配置信息的源(包括apiserverfile以及http)進行監聽,定期同步各源當前的Pod配置狀態。在pods中則緩存了上次同步時各源的Pod配置狀態。mux將兩者進行對比之后,即可得到配置發生變化的Pod。接著,它會根據變化類型對不同的Pod進行分類,每個類型的Pod注入一個PodUpdate結構中:

// kubernetes/pkg/kubelet/types/pod_update.go
type PodUpdate struct {
    Pods   []*v1.Pod
    Op     PodOperation
    Source string
}

Op字段即定義了上述的Pod變化類型。例如,它的值可以為ADDREMOVE,表示對Pods中定義的Pod進行相應的增選。最后,各種類型的PodUpdate都會被注入到PodConfigupdates中。因此,我們只要對updates這個頻道進行監聽,就能得到所有有關本節點Pod的更新信息。

2.對Pod進行同步

當Kubelet初始化完成之后,最終會調用如下所示的syncLoop函數:

// kubernetes/pkg/kubelet/kubelet.go
// syncLoop is the main loop for processing changes. It watches for changes from
// three channels (file, apiserver, and http) and creates a union of them. For
// any new change seen, will run a sync against desired state and running state. If
// no changes are seen to the configuration, will synchronize the last known desired
// state every sync-frequency seconds. Never returns.
func (kl *Kubelet) syncLoop(updates <-chan kubetypes.PodUpdate, handler SyncHandler){
    ...
    for {
        if !kl.syncLoopIteration(...) {
            break
        }        
    }
    ...
}

正如它的注釋所表明的,syncLoop函數是Kubelet的主循環。它會對updates進行監聽,獲取吊艙的最新配置,并在當前狀態(運行狀態)和期望狀態(所需狀態)之間進行同步,使本節點的pod都按預期狀態運行。事實上,syncLoop僅僅對對syncLoopIteration的封裝,每一次具體的同步操作都將交由syncLoopIteration完成。

// kubernetes/pkg/kubelet/kubelet.go
func (kl *Kubelet) syncLoopIteration(configCh <-chan kubetypes.PodUpdate ......) bool {
    select {
    case u, open := <-configCh:
        switch u.Op {
        case kubetypes.ADD:
            handler.HandlePodAdditions(u.Pods)
        case kubetypes.UPDATE:
            handler.HandlePodUpdates(u.Pods)
        ...
        }
    case e := <-plegCh:
        ...
        handler.HandlePodSyncs([]*v1.Pod{pod})
        ...
    case <-syncCh:
        podsToSync := kl.getPodsToSync()
        if len(podsToSync) == 0 {
            break
        }
        handler.HandlePodSyncs(podsToSync)
    case update := <-kl.livenessManager.Updates():
        if update.Result == proberesults.Failure {
            ...
            handler.HandlePodSyncs([]*v1.Pod{pod})
        }
    case <-housekeepingCh:
         ...
        handler.HandlePodCleanups()
        ...
    }
}

syncLoopIteration函數的處理邏輯很簡單,它會對多個渠道進行監聽,一旦從某個渠道中獲取到了某類事件,就調用相應的處理函數對其進行處理。下面,我們對各類事件做一個簡單的敘述:

  1. configCh中莢電子雜志配置信息的改變,并根據改變的類型,調用對應的處理函數。例如有新的吊艙綁定到本節點上時,調用就會HandlePodAdditions在本。節點上新建這些吊艙。如果某些莢的配置發生了改變,則會調用HandlePodUpdates對這些pod進行更新。

  2. 若莢中有容器的狀態發生了變化,例如有新的容器創建并運行,則會向plegCh這個通道發送PodlifecycleEvent這樣一個事件,其中包含了事件類型ContainerStarted,該容器的ID,以及它所屬Pod的ID,接著syncLoopIteration會調用HandlePodSyncs對該pod進行同步。

  3. syncCh其實是一個定時器,Kubelet默認每隔一秒它就會觸發一次,對當前節點上所有需要同步的pod進行同步。

  4. Kubelet在初始化過程中會創建livenessManager,它會對進行了相關配置的吊艙進行健康檢查。一旦檢測到吊艙的運行狀態出錯,同樣會調用HandlePodSyncs對相應的吊艙進行同步。關于這部分的內容,我們將在下文中詳細描述。

  5. houseKeepingCh同樣是一個定時器,Kubelet默認每隔兩秒它就會觸發一次并調用處理函數HandlePodCleanups。簡單地說,這就是一個定時清理的機制,每隔一段時間對那些已經結束運行的pod的相關資源進行回收。

深入理解Kubelet核心執行框架
如上圖所示,大多數處理函數的執行路徑是非常類似的。

不管是HandlePodAdditionsHandlePodUpdates還是HandlePodSyncs都會在完成自己特有的一些操作之后調用dispatchWork函數。而dispatchWork函數如果確認了要同步的吊艙處于不Terminated狀態,調用就會podWokersUpdate方法對莢果進行更新。事實上,不管是新建莢,還是對莢的更新,同步,我們都可以將其統一為從當前狀態(運行狀態)到目標狀態(所需狀態)過渡的過程。這樣的解釋對于pod的更新和同步是很直觀的。而對于新建pod,則可以認為它的當前狀態為空,那么我們也能將其納入這個框架中。因此,無論我們是要創建,更新還是同步莢,我們名單最終調用只要統一的Update函數,就能讓指定的吊艙從當前狀態轉換到目標狀態。

podWorkers會在Kubelet初始化的過程中被創建,如下所示:

// kubernetes/pkg/kubelet/pod_workers.go
type podWorkers struct {
    ...
    podUpdates map[types.UID]chan UpdatePodOptions

    isWorking map[types.UID]bool

    lastUndeliveredWorkUpdate map[types.UID]UpdatePodOptions

    workQueue queue.WorkQueue

    syncPodFn syncPodFnType

    podCache kubecontainer.Cache
    ...
}

當Kubelet每創建一個新的吊艙,都會為其配置一個專有的莢工人。每個莢工人其實就是一個夠程,它會創建一個緩存大小為1,類型為UpdatePodOptions(一個UpdatePodOptions就是一個莢更新事件)的信道,并不斷對其監聽來獲取pod的更新事件并調用podWorkerssyncPodFn字段指定的同步函數進行具體的同步工作。

同時,pod worker會將該通道注冊到podWorkers中的podUpdates這個地圖中,從而可以讓外部將指定的更新事件發送到對應的pod worker,讓它進行處理。

如果pod worker正在處理某個更新,這時候又來了另外一個更新事件怎么辦?podWorkers會將其中最新的一個緩存到lastUndeliveredWorkUpdate并在pod worker處理完當前更新事件之后馬上對其進行處理。

最后,pod worker每處理完一次更新,都會將pod加入podWorkersworkQueue隊列,而且會附加一個時延,只有時延消耗完了,才能將pod從隊列中再次取出,進行下一次的同步。在上文中我們提到,每過1秒就會觸發一次syncCh,收集本節點上需要進行同步的豆莢調用再HandlePodSyncs進行同步。事實上,那些莢從正是workQueue中的電子雜志,在當前時間節點,時延到期的吊艙。由此,整個pod的同步過程,如下所示,形成了一個閉環。

深入理解Kubelet核心執行框架

Kubelet在創建podWorkers對象的時候,會用自己的syncPod方法syncPodFn初始化。不過該方法所做的工作也僅僅是真正進行同步前的一些準備工作。例如將pod的最新狀態上傳給Apiserver,創建pod的專屬目錄,獲取莢的拉動秘密等等。最終,Kubelet調用會所屬其的containerRuntimeSyncPod方法進行同步工作。containerRuntime是Kubelet對底層容器運行時的一種抽象,定義了各種容器運行時需要滿足的接口SyncPod方法就是這些接口中的一個。

Kubelet并不會進行任何具體的容器相關的操作,所謂pod的同步,本質上還是對相關容器狀態的改變,而要做到這一點,最終必然只能調用例如PouchContainer這樣的底層容器運行時來完成。

下面,將我們展示進入containerRuntimeSyncPod方法,展示真正的同步工作:

// kubernetes/pkg/kubelet/kuberuntime/kuberuntime_manager.go
func (m *kubeGenericRuntimeManager) SyncPod(pod *v1.Pod, _ v1.PodStatus, podStatus *kubecontainer.PodStatus, pullSecrets []v1.Secret, backOff *flowcontrol.Backoff) (result kubecontainer.PodSyncResult)

該函數首先會調用computePodActions(pod, podStatus),對pod的當前狀態podStatus和pod的目標狀態pod進行比較,從而計算出我們要進行哪些具體的同步工作。計算結束之后,返回一個PodActions對象如下所示:

// kubernetes/pkg/kubelet/kuberuntime/kuberuntime_manager.go
type podActions struct {
    KillPod bool

    CreateSandbox bool

    SandboxID string

    Attempt uint32

    ContainersToKill map[kubecontainer.ContainerID]containerToKillInfo

    NextInitContainerToStart *v1.Container

    ContainersToStart []int
}

事實上,PodActions就是一個操作列表:

  1. KillPodCreateSandbox的值一般情況下是一致的,表示是否殺死當前Pod的Sandbox(若創建一個新Pod,則該操作為空)而創建一個新的

  2. SandboxID用于對pod的創建操作進行標識,若它的值為空,表示第一次創建pod,否則表示殺死原有的sandbox而創建一個新的

  3. Attempt表示pod重新創建sandbox的次數,第一次創建pod時,該值為0,作用和SandboxID是類似的

  4. ContainersToKill指定了我們需要殺死的pod中的一些容器,之所以要刪除它們,可能是因為容器的配置已經發生了變化,或者對它的健康檢查失敗了

  5. 如果pod的init容器還沒有全部運行完成或者在運行過程中出現了問題,NextInitContainerToStart表示下一個要創建的init container,創建并啟動它,此次同步結束

  6. 若莢的沙箱已經創建完成,init container也都運行完畢,則根據ContainersToStart啟動pod中還未正常運行的普通容器

有了這樣一份操作列表之后,SyncPod剩下的操作就異常簡單了,無非是根據配置,按部就班地調用底層容器運行時的相應接口,進行具體的容器增刪工作,完成同步。

總的來說,對于pod的同步可以簡單歸結為:當莢的目標狀態發生改變,或者每隔一個同步周期,都會觸發對相應pod的同步,而同步的具體內容就是將容器的目標狀態和當前狀態進行比對計算,生成一張容器的啟停清單,根據該清單調用底層的容器運行時接口完成相應容器的啟停工作。

總結

如果簡單地將容器類比為一個進程的話,那么Kubelet本質上就是一個面向容器的進程監視器。它的任務就是不斷地促成本節點pod的運行狀態向目標狀態轉換。轉換的方式也非常簡單粗暴,如果有不符合要求的容器就直接刪除,再根據新的配置重建一個,并不存在對一個已有容器反復修改啟停的情況。到此為止,Kubelet核心的處理邏輯闡述完畢。

注:

  1. 文中源碼對應的Kubernetes版本為v1.9.4,commit:bee2d1505c4fe820744d26d41ecd3fdd4a3d6546

  2. Kubernetes詳細的源碼注釋參加我的github

參考文獻

  • Kubernetes源碼

    https://github.com/YaoZengzeng/kubernetes

  • 什么甚至是一個kubelet?

    http://kamalmarhubi.com/blog/2015/08/27/what-even-is-a-kubelet/

深入理解Kubelet核心執行框架

深入理解Kubelet核心執行框架


阿里百萬級規模開源容器PouchContainer GA版本已發布。此版本延續以往的節奏,繼續在Cloud Native(Kubernetes)生態的支持,以及容器安全隔離等方面做了持續性增強,同時開始孵化PouchContainer的插件機制,使得生態用戶可以更加友好便捷地通過自研插件實現容器功能的擴展


PouchContainer發布GA版本之前,已在阿里巴巴數據中心得到大規模的驗證; GA版本發布之后,相信其一系列的突出特性同樣可以服務于行業,作為一種開箱即用的系統軟件技術,幫助行業服務在推進云原生架構轉型上占得先機。


為了分享并促進社區的進步,邀請大家參加2018年9月9日(周日)上海PouchContainer Meetup,掃描上圖二維碼或者點擊閱讀原文立即報名


向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

和顺县| 澎湖县| 彭阳县| 陵川县| 北京市| 远安县| 仪征市| 奉节县| 阜宁县| 原阳县| 湟源县| 新竹市| 呼和浩特市| 天全县| 石嘴山市| 芮城县| 鄄城县| 乡城县| 方山县| 龙游县| 建德市| 南京市| 正蓝旗| 中超| 观塘区| 通海县| 当阳市| 新郑市| 襄汾县| 惠水县| 江北区| 从化市| 根河市| 武宣县| 柳河县| 太仆寺旗| 北碚区| 克什克腾旗| 凌源市| 金乡县| 德惠市|