細(xì)讀Kubernetes源碼(scheduler之Pod的綁定)

Kubernetes 的開始起源于谷歌,是一個谷歌花了很多力氣來為你的工作和服務(wù)創(chuàng)建的新管理工具。它在谷歌系統(tǒng)中有自己的起源: Borg 和 Omega 。無論是公有云還是私有云甚至混合云,Kubernetes將作為一個為任何應(yīng)用,任何環(huán)境的容器管理框架無處不在。正因為如此, 受到各大巨頭及初創(chuàng)公司的青睞,如IBM,Microsoft、VMWare、Red Hat、CoreOS、Mesos等,紛紛加入給Kubernetes貢獻代碼。Kubernetes毫無疑問坐擁容器編排的霸主地位。

關(guān)于Kubernetes的起源,架構(gòu),原理,組件的介紹網(wǎng)上太多了,感興趣的可以自行g(shù)oogle,但是我發(fā)現(xiàn)關(guān)于源碼的解讀比較少,即使有,也比較泛泛。大多是從某個函數(shù)入口講起,然后講到包,結(jié)構(gòu)體,屬性,方法結(jié)束,關(guān)于每個方法的作用要么是略過,要么是一句話解釋,對Kubernetes的一個組件的解讀盡然一篇文章就寫完了,大師的思維太快,跟不上~~~

架構(gòu)是大師們的事情,我們凡夫俗子能做到清楚就很不容易了!我打算在接下來的文章里,每一篇文章之讀一個關(guān)鍵方法。細(xì)細(xì)品味大神們的杰作。

我想好多用過Kubernetes的都有一個疑問,Pod是如何通過層層篩選被綁定到一個節(jié)點的。接下來就分析這個方法實現(xiàn):scheduleOne()。

關(guān)于scheduler的predicateprioirity設(shè)計請參考devel/scheduler.md,關(guān)于它的算法請參考devel/scheduler_algorithm.md,讀完之后,你就會對Kubernetes Master三大組件之一的scheduler有了一個基本的印象,甚至感覺好簡單啊~~~ 那么,恭喜你,你與大神的差距又縮短了一厘米~~~

好,現(xiàn)在我們從 kubernetes/plugin/pkg/scheduler/scheduler.go
中摘出scheduleOne方法,我會把解讀放到每一行代碼的前面。

// scheduleOne does the entire scheduling workflow for a single pod.  It is serialized on the scheduling algorithm's host fitting.
func (sched *Scheduler) scheduleOne() {
    // 獲取一個待調(diào)度的Pod, NextPod()方法是阻塞的
    pod := sched.config.NextPod()
    //首先,判斷這個Pod是否正在被刪除中,如果是,則返回,跳過調(diào)度。
    if pod.DeletionTimestamp != nil {
        //記錄發(fā)生的事件,可以通過kubectl describe pods命令看到此處的事件打印信息
        sched.config.Recorder.Eventf(pod, v1.EventTypeWarning, "FailedScheduling", "skip schedule deleting pod: %v/%v", pod.Namespace, pod.Name)
        glog.V(3).Infof("Skip schedule deleting pod: %v/%v", pod.Namespace, pod.Name)
        return
    }
       // 開始嘗試調(diào)度pod,以命名空間和pod名稱區(qū)分
    glog.V(3).Infof("Attempting to schedule pod: %v/%v", pod.Namespace, pod.Name)

    // Synchronously attempt to find a fit for the pod.
    start := time.Now()
    // 此處的schedule()方法中,會調(diào)用注冊的初選和優(yōu)選的算法,最終返回一個節(jié)點的名稱存放到suggestedHost變量中
    suggestedHost, err := sched.schedule(pod)
    // 此處,會調(diào)用go的一個客戶端,原子地的更新觀測總和。關(guān)于它的具體功能,我們下次再細(xì)讀。
    metrics.SchedulingAlgorithmLatency.Observe(metrics.SinceInMicroseconds(start))
    if err != nil {
        return
    }

    // Tell the cache to assume that a pod now is running on a given node, even though it hasn't been bound yet.
    // This allows us to keep scheduling without waiting on binding to occur.

    //即使該pod還未真正綁定到節(jié)點上,我們先假設(shè)這個pod已經(jīng)在指定的節(jié)點上運行了。這是為了更新shcedulerCache, 與綁定操作(需要花費一些時間)以異步方式進行。 
    err = sched.assume(pod, suggestedHost)
    if err != nil {
        return
    }

    // bind the pod to its host asynchronously (we can do this b/c of the assumption step above).
    // 創(chuàng)建一個協(xié)程goruntime,異步地綁定pod。(注意,該shceduleOne方法也是在一個協(xié)程中)
    go func() {

        // 執(zhí)行綁定方法,把該pod的命名空間,名稱和UID綁定到指定的節(jié)點(suggestedHost)上,到此,一個pod綁定到一個節(jié)點的過程就完成了。
        err := sched.bind(pod, &v1.Binding{
            ObjectMeta: metav1.ObjectMeta{Namespace: pod.Namespace, Name: pod.Name, UID: pod.UID},
            Target: v1.ObjectReference{
                Kind: "Node",
                Name: suggestedHost, // 分配的節(jié)點
            },
        })

        // 與上面類似,會在后面單獨分析,它們的實現(xiàn)在vendor/github.com/prometheus/client_golang/prometheus/histogram.go,可以自行看一下
        metrics.E2eSchedulingLatency.Observe(metrics.SinceInMicroseconds(start))
        if err != nil {
            glog.Errorf("Internal error binding pod: (%v)", err)
        }
    }()
}

是不是感覺很簡單?確實,大神的代碼讓你感覺邏輯清晰,代碼簡潔。但是,還有許多問題未解決,比如,如果綁定失敗,retrying 是如何觸發(fā)的呢?retrying的篩選邏輯,使用的算法是否和之前一致呢?如果調(diào)度過程中節(jié)點掛了怎么辦?scheduler是如何拿到各個節(jié)點的資源信息的?綁定之后,Kubelet是如何接收Pod的?如何添加自己定制的算法?等等。下一篇文章會分析retrying機制,敬請期待。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容