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

1. 前言

转载请说明原文出处, 尊重他人劳动成果!

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

本文将分析controller中的一些公共结构体, 因为很多的controller会共用这些结构体, 所以在分析各种controller之前了解这些结构体的作用很有必要. 本文将主要涉及pkg/controller/controller_ref_manager.go.

2. 接口

architecture.png

这里主要关注一下PodControllerRefManager, 该类是为ReplicaSet服务的, 另外具有类似功能的ReplicaSetControllerRefManager是为Deployment服务的. 所以主要介绍PodControllerRefManager即可.

这两个类都继承BaseControllerRefManager,定义了一些共同的逻辑.
由于PodControllerRefManager需要操作pod, 所以它有一个PodControllerInterface类型的成员变量.

3. BaseControllerRefManager

type BaseControllerRefManager struct {
    // 当前这个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, 但是现在不匹配了(有可能是obj发生改变也有可能是m.Controller某些属性比如label发生改变),
//    则需要为该obj与m.Controller解绑
// 只有两种情况下会返回true:
// 1. 孤儿obj与m.Controller匹配 则接收并返回true
// 2. 该obj以前属于m.Controller并且现在也匹配 返回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已经属于另外一个controller
        if controllerRef.UID != m.Controller.GetUID() {
            // Owned by someone else. Ignore.
            return false, nil
        }
        // 说明该obj属于当前这个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属于当前这个controller 但是没有匹配上
        // (因为如果修改了controller的Match Labels然后apply到集群中, 那就有可能不匹配)
        // 此时需要让该obj与当前controller解绑


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

        // 如果当前controller正处于删除 就不用解绑了
        if m.Controller.GetDeletionTimestamp() != nil {
            return false, nil
        }
        // 让该obj与当前controller解绑
        if err := release(obj); err != nil {
            // If the pod no longer exists, ignore the error.
            // 如果该obj已经不存在了
            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
    }

    // 说明当前这个obj是个孤儿obj, 不属于任何controller
    // It's an orphan.

    // 1. 如果该controller或者当前obj正在删除或者与该obj与当前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与当前controller绑定在一起
    // Selector matches. Try to adopt.
    if err := adopt(obj); err != nil {
        // If the pod no longer exists, ignore the error.
        // 如果该obj已经不存在了 返回
        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, 但是现在不匹配了(有可能是obj发生改变也有可能是m.Controller某些属性比如label发生改变), 则需要为该objm.Controller解绑.

claimObject.png

可以看到只有两种情况下会返回true:
1. 孤儿objm.Controller匹配, 则接收并返回true.
2.obj以前属于m.Controller并且现在也匹配, 返回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,
    }
}

这里很常规, 传入了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)
    // 发的Patch请求 局部更新 比如用ReplicaSet中的Template创建的pod在metadata都有一个ownerReferences表示它属于哪一个replicaset实例
    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)
    // 发Patch请求局部更新metadata.ownerReferences字段中的内容
    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发的是Patch请求, 因为该pod实际上是已经运行了的, 这是只是更新它的metadata.ownerReferences字段, 用以辨别该pod是属于某个controller的.
ReleasePod: 很显然该方法是用来解绑的, 也是更新metadata.ownerReferences字段, 只是里面的内容显示是被某个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选出属于当前m.Controllerpods. 调用的方法都已经在上面介绍过了.

5. 总结

理解了PodControllerRefManager之后, ReplicaSetControllerRefManager就很好理解了, 明显是为Deployment准备的, 用来接收属于它的ReplicaSet.

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,558评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,002评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,024评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,144评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,255评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,295评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,068评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,478评论 1 305
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,789评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,965评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,649评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,267评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,982评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,800评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,847评论 2 351

推荐阅读更多精彩内容