PodControl analysis of Controller (Kubernetes)

Preface

Please refer to the explanation of terms before reading this article .

PodControl is the interface of XxxController to operate Pod, such as ReplicaSetController to create and delete Pod. Readers familiar with Clientset will definitely question whether it is necessary to define an interface to operate Pod? Isn’t it enough to directly use the interface provided by Clientset? The answer is definitely necessary, otherwise the author would not need to introduce it. Because many Controller's child objects are Pods, such as Job, Deamonset, ReplicaSet, and so on. These Controllers have similar operations for Pods, such as setting the owner of the Pod (this is different from the Pod created by kubectl create). So there is a PodControl interface for multiple XxxControllers to use.

For an introduction to the owner, please refer to ControllerRefManager .

The source code cited in this article is the release-1.20 branch of kubernetes.

PodControl

PodControlInterface

PodControlInterface defines the interface for operating Pod, the source code link: https://github.com/kubernetes/kubernetes/blob/release-1.20/pkg/controller/controller_utils.go#L449

type PodControlInterface interface {
    // 根据Pod模板创建Pod,这是XxxController创建Pod的共性,因为很多Workload都有Pod模板。
    CreatePods(namespace string, template *v1.PodTemplateSpec, object runtime.Object) error
    // CreatePodsOnNode()与CreatePods()一样根据Pod模板创建Pod,同时还指定了Pod运行的Node以及拥有者。
    // 遗憾的是CreatePodsOnNode()与CreatePods()都没有被XxxController所使用。
    CreatePodsOnNode(nodeName, namespace string, template *v1.PodTemplateSpec, object runtime.Object, controllerRef *metav1.OwnerReference) error
    // CreatePodsWithControllerRef()与CreatePods()一样根据Pod模板创建Pod,同时指定了拥有者。
    // 这是XxxController大量使用的接口,也证明了笔者前面的观点,即XxxController创建Pod是有共性的。
    CreatePodsWithControllerRef(namespace string, template *v1.PodTemplateSpec, object runtime.Object, controllerRef *metav1.OwnerReference) error
    // 删除一个Pod,这个接口与Clientset的接口没什么不同,无非多了个object参数。
    // 这也是XxxController操作Pod的另一个共性,就是为Pod的父对象记录操作Pod的事件。
    DeletePod(namespace string, podID string, object runtime.Object) error
    // Patch更新Pod。
    PatchPod(namespace, name string, data []byte) error
}

RealPodControl

RealPodControl implements (only one implementation) PodControlInterface interface, source code link: https://github.com/kubernetes/kubernetes/blob/release-1.20/pkg/controller/controller_utils.go#L464 .

type RealPodControl struct {
    // 利用Clientset操作Pod
    KubeClient clientset.Interface
    // 用于记录Pod父对象操作Pod的事件
    Recorder   record.EventRecorder
}

CreatePodXxx

Although PodControlInterface defines 3 interfaces for creating Pod, the author found that only CreatePodsWithControllerRef() is used a lot, and the other two are not used. Do not rule out the use in earlier versions, this is not a problem, because these three interfaces are implemented through a function. Source link: https://github.com/kubernetes/kubernetes/blob/release-1.20/pkg/controller/controller_utils.go#L521 .

// CreatePods()实现了PodControlInterface.CreatePods()接口。
func (r RealPodControl) CreatePods(namespace string, template *v1.PodTemplateSpec, object runtime.Object) error {
    // createPods()下面有注释。
    return r.createPods("", namespace, template, object, nil)
}

// CreatePodsWithControllerRef()实现了PodControlInterface.CreatePodsWithControllerRef()接口。
func (r RealPodControl) CreatePodsWithControllerRef(namespace string, template *v1.PodTemplateSpec, controllerObject runtime.Object, controllerRef *metav1.OwnerReference) error {
    // 校验controllerRef的合法性
    if err := validateControllerRef(controllerRef); err != nil {
        return err
    }
    return r.createPods("", namespace, template, controllerObject, controllerRef)
}

// CreatePodsOnNode()实现了PodControlInterface.CreatePodsOnNode()接口。
func (r RealPodControl) CreatePodsOnNode(nodeName, namespace string, template *v1.PodTemplateSpec, object runtime.Object, controllerRef *metav1.OwnerReference) error {
    if err := validateControllerRef(controllerRef); err != nil {
        return err
    }
    return r.createPods(nodeName, namespace, template, object, controllerRef)
}

// createPods()是所有创建Pod接口的最终实现。
func (r RealPodControl) createPods(nodeName, namespace string, template *v1.PodTemplateSpec, object runtime.Object, controllerRef *metav1.OwnerReference) error {
    // 根据模板创建Pod的API对象,GetPodFromTemplate()下面有注释。
    pod, err := GetPodFromTemplate(template, object, controllerRef)
    if err != nil {
        return err
    }
    // 是否指定了Node,对应于CreatePodsOnNode()接口。
    if len(nodeName) != 0 {
        pod.Spec.NodeName = nodeName
    }
    // 不能创建没有标签的Pod,为什么?因为Controller需要根据Pod标签匹配,如果是Controller创建的Pod必须都有标签。
    if len(labels.Set(pod.Labels)) == 0 {
        return fmt.Errorf("unable to create pods, no labels")
    }
    // 通过Clientset创建Pod,有没有发现PodControl创建Pod的接口都没有metav1.CreateOptions?
    // 这也算是PodControl存在的另一个价值,省去了没用的参数。
    newPod, err := r.KubeClient.CoreV1().Pods(namespace).Create(context.TODO(), pod, metav1.CreateOptions{})
    if err != nil {
        if !apierrors.HasStatusCause(err, v1.NamespaceTerminatingCause) {
            // 记录为object创建Pod失败事件
            r.Recorder.Eventf(object, v1.EventTypeWarning, FailedCreatePodReason, "Error creating: %v", err)
        }
        return err
    }
    // 获取object对象的meta来打印日志
    accessor, err := meta.Accessor(object)
    if err != nil {
        klog.Errorf("parentObject does not have ObjectMeta, %v", err)
        return nil
    }
    // 记录为object创建Pod成功事件。
    klog.V(4).Infof("Controller %v created pod %v", accessor.GetName(), newPod.Name)
    r.Recorder.Eventf(object, v1.EventTypeNormal, SuccessfulCreatePodReason, "Created pod: %v", newPod.Name)

    return nil
}

// 根据Pod模板创建Pod的API对象。
func GetPodFromTemplate(template *v1.PodTemplateSpec, parentObject runtime.Object, controllerRef *metav1.OwnerReference) (*v1.Pod, error) {
    // 从模板中提取Labels、Finalizers和Annotations
    desiredLabels := getPodsLabelSet(template)
    desiredFinalizers := getPodsFinalizers(template)
    desiredAnnotations := getPodsAnnotationSet(template)
    accessor, err := meta.Accessor(parentObject)
    // 获取Pod父对象的meta
    if err != nil {
        return nil, fmt.Errorf("parentObject does not have ObjectMeta, %v", err)
    }
    // 利用父对象的名字+'-'作为Pod的前缀,这也是为什么我们看到Deployment创建出来的Pod都是xxx-yyy-zzz格式。
    // 其中xxx是Deployment名字,yyy是ReplicaSet名字,zzz是Pod名字,也就是Deployment创建了ReplicatSet,ReplicatSet创建了Pod
    prefix := getPodsPrefix(accessor.GetName())

    // 常见Pod的API对象
    pod := &v1.Pod{
        ObjectMeta: metav1.ObjectMeta{
            Labels:       desiredLabels,
            Annotations:  desiredAnnotations,
            GenerateName: prefix,
            Finalizers:   desiredFinalizers,
        },
    }
    // 如果指定了Pod的Controller引用,将其追加到Pod的拥有者引用中
    if controllerRef != nil {
        pod.OwnerReferences = append(pod.OwnerReferences, *controllerRef)
    }
    // 从Pod模板深度拷贝PodSpec。
    pod.Spec = *template.Spec.DeepCopy()
    return pod, nil
}

DeletePod

There is nothing to explain about this interface, just upload the code, the source code link: https://github.com/kubernetes/kubernetes/blob/release-1.20/pkg/controller/controller_utils.go#L599 .

// DeletePod()实现了PodControlInterface.DeletePod()接口。
func (r RealPodControl) DeletePod(namespace string, podID string, object runtime.Object) error {
    获取Pod父对象的meta,单纯为了打印日志,这应该是也是XxxController操作Pod的共性吧。
    accessor, err := meta.Accessor(object)
    if err != nil {
        return fmt.Errorf("object does not have ObjectMeta, %v", err)
    }
    klog.V(2).InfoS("Deleting pod", "controller", accessor.GetName(), "pod", klog.KRef(namespace, podID))
    // 通过Clientset删除Pod
    if err := r.KubeClient.CoreV1().Pods(namespace).Delete(context.TODO(), podID, metav1.DeleteOptions{}); err != nil {
        if apierrors.IsNotFound(err) {
            klog.V(4).Infof("pod %v/%v has already been deleted.", namespace, podID)
            return err
        }
        // 为object记录删除Pod失败的事件
        r.Recorder.Eventf(object, v1.EventTypeWarning, FailedDeletePodReason, "Error deleting: %v", err)
        return fmt.Errorf("unable to delete pods: %v", err)
    }
    // 为object记录删除Pod成功的事件
    r.Recorder.Eventf(object, v1.EventTypeNormal, SuccessfulDeletePodReason, "Deleted pod: %v", podID)

    return nil
}

PatchPod

The Patch update interface is more concise than the Clientset interface, and there are fewer unintelligible parameters. This is also the value of PodControl. https://github.com/kubernetes/kubernetes/blob/release-1.20/pkg/controller/controller_utils .go#L539 .

// PatchPod()实现了PodControlInterface.PatchPod()接口。
func (r RealPodControl) PatchPod(namespace, name string, data []byte) error {
    // PatchPod()接口实现比较简单,直接用Clientset接口实现Patch更新。但是要求所有的XxxController都使用策略性合并(types.StrategicMergePatchType).
    // 为什么不为Patch更新Pod记录事件?这一点笔者也在思考。
    // 关于Patch类型的说明请参看:https://kubernetes.io/zh/docs/tasks/manage-kubernetes-objects/update-api-object-kubectl-patch/
    _, err := r.KubeClient.CoreV1().Pods(namespace).Patch(context.TODO(), name, types.StrategicMergePatchType, data, metav1.PatchOptions{})
    return err
}

to sum up

  1. PodControl is a re-encapsulation of the Pod interface for Clientset operation, but some parameters that are "useless" for XxxController are removed from the interface, such as metav1.PatchOptions, metav1.DeleteOptions, metav1.CreateOptions, etc., while adding parameters required by XxxController, such as the parent object , ControllerRef, etc.;
  2. PodControl can record the events of operating Pod for the parent object of Pod, avoiding every Controller to implement similar code;

Guess you like

Origin blog.csdn.net/weixin_42663840/article/details/115275349