Análisis de PodControl de Controller (Kubernetes)

Prefacio

Consulte la explicación de términos antes de leer este artículo .

PodControl es la interfaz de XxxController para operar Pod, como ReplicaSetController para crear y eliminar Pod. Los lectores familiarizados con Clientset definitivamente se preguntarán si es necesario definir una interfaz para operar Pod. ¿No es suficiente utilizar directamente la interfaz proporcionada por Clientset? La respuesta es definitivamente necesaria, de lo contrario, el autor no necesitaría presentarla. Porque muchos objetos secundarios de Controller son Pods, como Job, Deamonset, ReplicaSet, etc. Estos controladores tienen operaciones similares para los pods, como configurar el propietario del pod (esto es diferente del pod creado por kubectl create). Por tanto, existe una interfaz PodControl para que la utilicen varios XxxControllers.

Para una introducción al propietario, consulte ControllerRefManager .

El código fuente citado en este artículo es la rama release-1.20 de kubernetes.

PodControl

PodControlInterface

PodControlInterface define la interfaz para operar Pod, el enlace del código fuente: 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 implementa (solo una implementación) la interfaz PodControlInterface, enlace de código fuente: 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

Aunque PodControlInterface define 3 interfaces para crear Pod, el autor descubrió que solo CreatePodsWithControllerRef () se usa mucho y las otras dos no se usan. No descarte el uso en versiones anteriores, esto no es un problema, porque estas tres interfaces se implementan a través de una función. Enlace de origen: 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

No hay nada que explicar sobre esta interfaz, solo cargue el código, el enlace del código fuente: 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

La interfaz de actualización del parche es más concisa que la interfaz Clientset y hay menos parámetros ininteligibles. Este también es el valor de 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
}

para resumir

  1. PodControl es una reencapsulación de la interfaz de Pod para la operación de Clientset, pero algunos parámetros que son "inútiles" para XxxController se eliminan de la interfaz, como metav1.PatchOptions, metav1.DeleteOptions, metav1.CreateOptions, etc., mientras se agregan parámetros requerido por XxxController, como el objeto principal, ControllerRef, etc .;
  2. PodControl puede registrar los eventos de operación de Pod para el objeto principal de Pod, evitando que cada Controlador implemente un código similar;

Supongo que te gusta

Origin blog.csdn.net/weixin_42663840/article/details/115275349
Recomendado
Clasificación