[k8s源碼分析][controller-manager] controller_ref_manager分析

1. 前言

轉(zhuǎn)載請說明原文出處, 尊重他人勞動成果!

源碼位置: https://github.com/nicktming/kubernetes/blob/tming-v1.13/pkg/controller/controller_ref_manager.go
分支: tming-v1.13 (基于v1.13版本)

本文將分析controller中的一些公共結(jié)構(gòu)體, 因?yàn)楹芏嗟?code>controller會共用這些結(jié)構(gòu)體, 所以在分析各種controller之前了解這些結(jié)構(gòu)體的作用很有必要. 本文將主要涉及pkg/controller/controller_ref_manager.go.

2. 接口

architecture.png

這里主要關(guān)注一下PodControllerRefManager, 該類是為ReplicaSet服務(wù)的, 另外具有類似功能的ReplicaSetControllerRefManager是為Deployment服務(wù)的. 所以主要介紹PodControllerRefManager即可.

這兩個類都繼承BaseControllerRefManager,定義了一些共同的邏輯.
由于PodControllerRefManager需要操作pod, 所以它有一個PodControllerInterface類型的成員變量.

3. BaseControllerRefManager

type BaseControllerRefManager struct {
    // 當(dāng)前這個controller
    Controller metav1.Object
    Selector   labels.Selector
    canAdoptErr  error
    canAdoptOnce sync.Once
    CanAdoptFunc func() error
}
// 判斷是否可以接收
func (m *BaseControllerRefManager) CanAdopt() error {
    m.canAdoptOnce.Do(func() {
        if m.CanAdoptFunc != nil {
            m.canAdoptErr = m.CanAdoptFunc()
        }
    })
    return m.canAdoptErr
}
// ClaimObject是為m.Controller尋找屬于它的obj
// 1. 如果孤兒obj與m.Controller匹配 則接收并返回true
// 2. 如果該obj以前屬于m.Controller, 但是現(xiàn)在不匹配了(有可能是obj發(fā)生改變也有可能是m.Controller某些屬性比如label發(fā)生改變),
//    則需要為該obj與m.Controller解綁
// 只有兩種情況下會返回true:
// 1. 孤兒obj與m.Controller匹配 則接收并返回true
// 2. 該obj以前屬于m.Controller并且現(xiàn)在也匹配 返回true
func (m *BaseControllerRefManager) ClaimObject(obj metav1.Object, match func(metav1.Object) bool, adopt, release func(metav1.Object) error) (bool, error) {
    // 獲得該obj所屬的controller
    controllerRef := metav1.GetControllerOf(obj)
    if controllerRef != nil {
        // 說明該obj已經(jīng)屬于另外一個controller
        if controllerRef.UID != m.Controller.GetUID() {
            // Owned by someone else. Ignore.
            return false, nil
        }
        // 說明該obj屬于當(dāng)前這個controller
        if match(obj) {
            // 如果匹配得上

            // We already own it and the selector matches.
            // Return true (successfully claimed) before checking deletion timestamp.
            // We're still allowed to claim things we already own while being deleted
            // because doing so requires taking no actions.
            return true, nil
        }
        // 說明該obj屬于當(dāng)前這個controller 但是沒有匹配上
        // (因?yàn)槿绻薷牧薱ontroller的Match Labels然后apply到集群中, 那就有可能不匹配)
        // 此時需要讓該obj與當(dāng)前controller解綁


        // Owned by us but selector doesn't match.
        // Try to release, unless we're being deleted.

        // 如果當(dāng)前controller正處于刪除 就不用解綁了
        if m.Controller.GetDeletionTimestamp() != nil {
            return false, nil
        }
        // 讓該obj與當(dāng)前controller解綁
        if err := release(obj); err != nil {
            // If the pod no longer exists, ignore the error.
            // 如果該obj已經(jīng)不存在了
            if errors.IsNotFound(err) {
                return false, nil
            }
            // Either someone else released it, or there was a transient error.
            // The controller should requeue and try again if it's still stale.
            return false, err
        }
        // Successfully released.
        // 成功解綁
        return false, nil
    }

    // 說明當(dāng)前這個obj是個孤兒obj, 不屬于任何controller
    // It's an orphan.

    // 1. 如果該controller或者當(dāng)前obj正在刪除或者與該obj與當(dāng)前controller不匹配 直接返回
    if m.Controller.GetDeletionTimestamp() != nil || !match(obj) {
        // Ignore if we're being deleted or selector doesn't match.
        return false, nil
    }
    if obj.GetDeletionTimestamp() != nil {
        // Ignore if the object is being deleted
        return false, nil
    }
    // 2. 嘗試將該obj與當(dāng)前controller綁定在一起
    // Selector matches. Try to adopt.
    if err := adopt(obj); err != nil {
        // If the pod no longer exists, ignore the error.
        // 如果該obj已經(jīng)不存在了 返回
        if errors.IsNotFound(err) {
            return false, nil
        }
        // Either someone else claimed it first, or there was a transient error.
        // The controller should requeue and try again if it's still orphaned.
        return false, err
    }
    // 3. 成功綁定
    // Successfully adopted.
    return true, nil
}

ClaimObject的作用是為m.Controller尋找屬于它的obj.
1. 如果孤兒objm.Controller匹配, 則接收并返回true.
2. 如果該obj以前屬于m.Controller, 但是現(xiàn)在不匹配了(有可能是obj發(fā)生改變也有可能是m.Controller某些屬性比如label發(fā)生改變), 則需要為該objm.Controller解綁.

claimObject.png

可以看到只有兩種情況下會返回true:
1. 孤兒objm.Controller匹配, 則接收并返回true.
2.obj以前屬于m.Controller并且現(xiàn)在也匹配, 返回true.

4. PodControllerRefManager

type PodControllerRefManager struct {
    BaseControllerRefManager
    controllerKind schema.GroupVersionKind
    podControl     PodControlInterface
}
func NewPodControllerRefManager(
    podControl PodControlInterface,
    controller metav1.Object,
    selector labels.Selector,
    controllerKind schema.GroupVersionKind,
    canAdopt func() error,
) *PodControllerRefManager {
    return &PodControllerRefManager{
        BaseControllerRefManager: BaseControllerRefManager{
            Controller:   controller,
            Selector:     selector,
            CanAdoptFunc: canAdopt,
        },
        controllerKind: controllerKind,
        podControl:     podControl,
    }
}

這里很常規(guī), 傳入了controllerpodControl, 那說明就是該controller要來接受pod.

4.1 方法

AdoptPod 和 ReleasePod
func (m *PodControllerRefManager) AdoptPod(pod *v1.Pod) error {
    // 在接收前最后一次再判斷一下是否可以接收
    if err := m.CanAdopt(); err != nil {
        return fmt.Errorf("can't adopt Pod %v/%v (%v): %v", pod.Namespace, pod.Name, pod.UID, err)
    }
    // Note that ValidateOwnerReferences() will reject this patch if another
    // OwnerReference exists with controller=true.
    addControllerPatch := fmt.Sprintf(
        `{"metadata":{"ownerReferences":[{"apiVersion":"%s","kind":"%s","name":"%s","uid":"%s","controller":true,"blockOwnerDeletion":true}],"uid":"%s"}}`,
        m.controllerKind.GroupVersion(), m.controllerKind.Kind,
        m.Controller.GetName(), m.Controller.GetUID(), pod.UID)
    // 發(fā)的Patch請求 局部更新 比如用ReplicaSet中的Template創(chuàng)建的pod在metadata都有一個ownerReferences表示它屬于哪一個replicaset實(shí)例
    return m.podControl.PatchPod(pod.Namespace, pod.Name, []byte(addControllerPatch))
}
func (m *PodControllerRefManager) ReleasePod(pod *v1.Pod) error {
    klog.V(2).Infof("patching pod %s_%s to remove its controllerRef to %s/%s:%s",
        pod.Namespace, pod.Name, m.controllerKind.GroupVersion(), m.controllerKind.Kind, m.Controller.GetName())
    deleteOwnerRefPatch := fmt.Sprintf(`{"metadata":{"ownerReferences":[{"$patch":"delete","uid":"%s"}],"uid":"%s"}}`, m.Controller.GetUID(), pod.UID)
    // 發(fā)Patch請求局部更新metadata.ownerReferences字段中的內(nèi)容
    err := m.podControl.PatchPod(pod.Namespace, pod.Name, []byte(deleteOwnerRefPatch))
    if err != nil {
        if errors.IsNotFound(err) {
            return nil
        }
        if errors.IsInvalid(err) {
            return nil
        }
    }
    return err
}

AdoptPod: 用來綁定某個podcontroller, 也就是這個pod是屬于該controller的. 注意podController發(fā)的是Patch請求, 因?yàn)樵?code>pod實(shí)際上是已經(jīng)運(yùn)行了的, 這是只是更新它的metadata.ownerReferences字段, 用以辨別該pod是屬于某個controller的.
ReleasePod: 很顯然該方法是用來解綁的, 也是更新metadata.ownerReferences字段, 只是里面的內(nèi)容顯示是被某個controller接綁的.

ClaimPods
func (m *PodControllerRefManager) ClaimPods(pods []*v1.Pod, filters ...func(*v1.Pod) bool) ([]*v1.Pod, error) {
    var claimed []*v1.Pod
    var errlist []error
    // 分別定義match, adopt, release方法
    match := func(obj metav1.Object) bool {
        pod := obj.(*v1.Pod)
        // Check selector first so filters only run on potentially matching Pods.
        if !m.Selector.Matches(labels.Set(pod.Labels)) {
            return false
        }
        for _, filter := range filters {
            if !filter(pod) {
                return false
            }
        }
        return true
    }
    adopt := func(obj metav1.Object) error {
        return m.AdoptPod(obj.(*v1.Pod))
    }
    release := func(obj metav1.Object) error {
        return m.ReleasePod(obj.(*v1.Pod))
    }
    // 分別嘗試每個pod
    for _, pod := range pods {
        ok, err := m.ClaimObject(pod, match, adopt, release)
        if err != nil {
            errlist = append(errlist, err)
            continue
        }
        if ok {
            // 如果成功
            claimed = append(claimed, pod)
        }
    }
    return claimed, utilerrors.NewAggregate(errlist)
}

ClaimPods的作用是從傳入的pods選出屬于當(dāng)前m.Controllerpods. 調(diào)用的方法都已經(jīng)在上面介紹過了.

5. 總結(jié)

理解了PodControllerRefManager之后, ReplicaSetControllerRefManager就很好理解了, 明顯是為Deployment準(zhǔn)備的, 用來接收屬于它的ReplicaSet.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子涤久,更是在濱河造成了極大的恐慌年缎,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件皂岔,死亡現(xiàn)場離奇詭異蹋笼,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)凤薛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進(jìn)店門姓建,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人缤苫,你說我怎么就攤上這事速兔。” “怎么了活玲?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵涣狗,是天一觀的道長谍婉。 經(jīng)常有香客問我,道長镀钓,這世上最難降的妖魔是什么穗熬? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮丁溅,結(jié)果婚禮上唤蔗,老公的妹妹穿的比我還像新娘。我一直安慰自己窟赏,他們只是感情好妓柜,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著涯穷,像睡著了一般棍掐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上拷况,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天作煌,我揣著相機(jī)與錄音,去河邊找鬼赚瘦。 笑死粟誓,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的起意。 我是一名探鬼主播努酸,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼杜恰!你這毒婦竟也來了获诈?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤心褐,失蹤者是張志新(化名)和其女友劉穎舔涎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體逗爹,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡亡嫌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了掘而。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片挟冠。...
    茶點(diǎn)故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖袍睡,靈堂內(nèi)的尸體忽然破棺而出知染,到底是詐尸還是另有隱情,我是刑警寧澤斑胜,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布控淡,位于F島的核電站嫌吠,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏掺炭。R本人自食惡果不足惜辫诅,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一涨享、第九天 我趴在偏房一處隱蔽的房頂上張望坎炼。 院中可真熱鬧扶欣,春花似錦肛鹏、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽庭呜。三九已至譬嚣,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間钞它,已是汗流浹背拜银。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留遭垛,地道東北人尼桶。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像锯仪,于是被迫代替她去往敵國和親泵督。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評論 2 359

推薦閱讀更多精彩內(nèi)容