美文网首页Docker容器k8s那点事儿
[k8s源码分析][controller-manager] co

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

作者: nicktming | 来源:发表于2019-10-22 22:15 被阅读0次

    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.

    相关文章

      网友评论

        本文标题:[k8s源码分析][controller-manager] co

        本文链接:https://www.haomeiwen.com/subject/iryfvctx.html