Controlador (Kubernetes) Controlador Análisis de expectativas

Prefacio

Para conocer algunos de los términos involucrados en este artículo, consulte la explicación de los términos . Aquí también se enfatizan dos conceptos: Controlador y Controlador. En este artículo, los dos tienen el mismo significado, que representan los objetos Xxx (como Implementación) administrados por XxxController (como DeploymentController). ControllerExpectations se pueden considerar como XxxExpectations, pero las interfaces de Expectations de varios Xxx son las mismas, por lo que se implementan de manera uniforme con ControllerExpectations.

Para comprender el controlador de Kubernetes, debe tener un conocimiento suficiente de la asincronía; de lo contrario, la implementación del código fuente de XxxController puede ser muy confusa. ControllerExpectations es la clave de Xxx para realizar la operación síncrona en XxxContrller, que es similar a sync.WaitGroup con la adición de la interfaz Wait () sin bloqueo. Tomando ReplicaSetController como ejemplo, N Pods se crean de acuerdo con la declaración de ReplicaSet. En este momento, ControllerExpectations se usa para registrar la cantidad de Pods que se espera que ReplicaSet cree como N (equivalente a sync.WaitGroup.Add (N )). ReplicaSetController monitorea (evento de Pod de SharedIndexInformer) hasta que se crea correctamente un Pod de ReplicaSet, notificará a ControllerExpectations para reducir la cantidad de Pods que se espera que se creen en uno (equivalente a sync.WaitGroup.Done ()). Hasta que la cantidad de pods que se crearán llegue a 0 (se crearán todos N pods), ReplicaSetController realizará el siguiente paso de acuerdo con el estado de ReplicaSet hasta que alcance el estado declarado por Xxx. Luego, desde la creación de N Pods en ReplicaSetController hasta que se confirma la creación de N Pods, en realidad hay muchas operaciones asincrónicas (como varios eventos de SharedIndexInformer) en todo el proceso. Debido a la existencia de ControllerExpectations, toda la operación es el Igual que las operaciones síncronas Esto también es Muchos XxxControllers tienen una función llamada syncXxx () por la razón de que es equivalente a procesar un objeto Xxx de forma síncrona. Hay muchas formas de lograr un funcionamiento sincrónico con un funcionamiento asincrónico, como sync.Cond.

Por supuesto, XxxController no solo crea objetos secundarios Xxx, sino que también elimina los objetos secundarios Xxx, por lo que hay dos recuentos en ControllerExpectations, que se utilizan para crear y eliminar la cantidad esperada de objetos secundarios.

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

Controlador Expectativas

Controlador Expectativas Interfaz

ControllerExpectationsInterface es una interfaz abstracta de ControllerExpectations. Esta interfaz abstracta solo se usa para pruebas unitarias (incluso si algunos XxxControllers ya han usado esta interfaz), por lo que los lectores pueden simplemente entenderla. Enlace de origen:  https://github.com/kubernetes/kubernetes/blob/release-1.20/pkg/controller/controller_utils.go#L144 .

type ControllerExpectationsInterface interface {
    // 获取Controller的期望,ControlleeExpectations后文有介绍,此处的controllerKey就是Xxx的唯一键,即NS/Name
    GetExpectations(controllerKey string) (*ControlleeExpectations, bool, error)
    // 判断Controller的期望是否达成
    SatisfiedExpectations(controllerKey string) bool
    // 删除Controller的期望
    DeleteExpectations(controllerKey string)
    // 设置Controller的期望,其中add是创建子对象的数量,del是删除子对象的数量
    SetExpectations(controllerKey string, add, del int) error
    // Controller期望创建adds个子对象
    ExpectCreations(controllerKey string, adds int) error
    // Controller期望删除dels个子对象
    ExpectDeletions(controllerKey string, dels int) error
    // 观测到Controller的一个子对象创建完成,此时Controller期望创建的子对象数量减一
    CreationObserved(controllerKey string)
    // 观测到Controller的一个子对象删除完成,此时Controller期望删除的子对象数量减一
    DeletionObserved(controllerKey string)
    // 提升Controller的期望,add和del分别是创建和删除子对象的增量
    RaiseExpectations(controllerKey string, add, del int)
    // 降低Controller的期望,add和del分别是创建和删除子对象的增量
    LowerExpectations(controllerKey string, add, del int)
}

ControlleeExpectations

ControllerExpectations maneja las expectativas de todos los objetos Xxx (como ReplicaSet) y ControlleeExpectations es la expectativa de un determinado objeto Xxx. ControllerExpectations puede entenderse simplemente como la estructura map [string] ControlleeExpectations. La clave del mapa es la única clave del objeto Xxx (como NS / Name). Enlace de origen:  https://github.com/kubernetes/kubernetes/blob/release-1.20/pkg/controller/controller_utils.go#L266 .

type ControlleeExpectations struct {
    // 期望创建/删除子对象(比如Pod)的数量
    add       int64
    del       int64
    // Controller的唯一键
    key       string
    // 创建时间
    timestamp time.Time
}

// 增加期望值,参数是创建和删除的增量值
func (e *ControlleeExpectations) Add(add, del int64) {
    atomic.AddInt64(&e.add, add)
    atomic.AddInt64(&e.del, del)
}

// 期望是否达成
func (e *ControlleeExpectations) Fulfilled() bool {
    // 如何才算是期望达成?就是期望创建和删除的子对象是否归零,如果二者全部归零说明没有任何期望了,也就是期望达成了
    return atomic.LoadInt64(&e.add) <= 0 && atomic.LoadInt64(&e.del) <= 0
}

// 获取期望值,需要返回创建和删除子对象的期望值
func (e *ControlleeExpectations) GetExpectations() (int64, int64) {
    return atomic.LoadInt64(&e.add), atomic.LoadInt64(&e.del)
}

// 判断期望是否过期(超时),这是一个比较有用的接口,一旦有什么异常造成期望长期无法达成,Controller就会一直没有进展。
// 利用超时机制可以解决此类异常(超时真是分布式系统中非常好用的方法),期望过期等于期望达成(无非可能是一个0值期望),XxxController会继续根据Xxx当前的状态进一步调整以达到Xxx声明的状态。
// 这种异常很常见?笔者举个不恰当的例子,只是为了方便理解: 比如删除一个Pod,Pod僵死一直不退出,那么期望删除一个Pod就会一直无法达成,Xxx的状态就一直没有进展。
func (exp *ControlleeExpectations) isExpired() bool {
    // 从创建期望到现在超过ExpectationsTimeout(5分钟)则认为过期
    return clock.RealClock{}.Since(exp.timestamp) > ExpectationsTimeout
}

Controlador Expectativas

XxxController usa ControllerExpectations para manejar las expectativas de todos los objetos Xxx. El enlace del código fuente:  https://github.com/kubernetes/kubernetes/blob/release-1.20/pkg/controller/controller_utils.go#L158 .

// ControllerExpectations实现了ControllerExpectationsInterface。
type ControllerExpectations struct {
    // cache包是client-go的cache,基本等同于map,不了解的读者可以阅读笔者关于client-go的Cache的文章
    cache.Store
}

// SatisfiedExpectations实现了ControllerExpectationsInterface.SatisfiedExpectations()接口
func (r *ControllerExpectations) SatisfiedExpectations(controllerKey string) bool {
    // 获取Controller的期望
    if exp, exists, err := r.GetExpectations(controllerKey); exists {
        if exp.Fulfilled() {
            // 期望已达成
            klog.V(4).Infof("Controller expectations fulfilled %#v", exp)
            return true
        } else if exp.isExpired() {
            // 期望已过期,返回true,这样XxxController可以根据Xxx最新的状态进行调整
            klog.V(4).Infof("Controller expectations expired %#v", exp)
            return true
        } else {
            // 期望未达成且未过期,继续等待期望达成
            klog.V(4).Infof("Controller still waiting on expectations %#v", exp)
            return false
        }
    } else if err != nil {
        // 获取期望错误
        klog.V(2).Infof("Error encountered while checking expectations %#v, forcing sync", err)
    } else {
        // 期望不存在
        klog.V(4).Infof("Controller %v either never recorded expectations, or the ttl expired.", controllerKey)
    }
    // 获取期望错误或者期望不存在,对于Controller来说等同于期望达成,否则Controller将不会有任何进展。
    // 什么是期望达成?就是Xxx上一次的操作已经完成,告知XxxController可以对Xxx执行下一步操作了,关键在于可以开始下一步工作了。
    // 所以在必要(比如出现异常)的时候,也可以看做是期望达成,可能此时期望值是0。
    return true
}

// ControllerExpectations其他接口的实现读者自己看就行了,非常简单,都是利用ControlleeExpectations实现的。

UIDTrackingControllerExpectations

UIDTrackingControllerExpectations hereda ControllerExpectations del nombre y también tiene la capacidad de rastrear UID. La guía de UID aquí es el UID del objeto API. Entonces, la expectativa no es solo la cantidad, sino también los objetos específicos. Esto puede indicar que la expectativa es con estado, porque tiene más UIDs especificados que ControllerExpectations. En otras palabras, ya sea que se creen o eliminen objetos secundarios, deben ser los especificados, de lo contrario no se logrará la expectativa. Entonces, UIDTrackingControllerExpectations se usa en StatefulSetController. https://github.com/kubernetes/kubernetes/blob/release-1.20/pkg/controller/controller_utils.go#L319 .

type UIDTrackingControllerExpectations struct {
    // 继承了ControllerExpectationsInterface
    ControllerExpectationsInterface
    // 用cache.Store加锁管理跟踪的UID,需要注意的是跟踪的UID只用于期望删除的Pod,期望创建的Pod不需要跟踪UID。
    uidStoreLock sync.Mutex
    uidStore cache.Store
}

// 获取Controller跟踪的UID,是一个字符串集合
func (u *UIDTrackingControllerExpectations) GetUIDs(controllerKey string) sets.String {
    // 函数没有上锁,所以调用此函数的地方需要加锁保护
    if uid, exists, err := u.uidStore.GetByKey(controllerKey); err == nil && exists {
        // UIDSet是一个结构体,包括string.Set(String)字段和一个key,读者可以自己看一下
        return uid.(*UIDSet).String
    }
    return nil
}

// ExpectDeletions覆盖了ControllerExpectationsInterface.ExpectDeletions(),参数从dels整数变成了字符串slice。
// 这一点可以看出跟踪UID是删除子对象的UID。
func (u *UIDTrackingControllerExpectations) ExpectDeletions(rcKey string, deletedKeys []string) error {
    // 字符串slice转set
    expectedUIDs := sets.NewString()
    for _, k := range deletedKeys {
        expectedUIDs.Insert(k)
    }
    klog.V(4).Infof("Controller %v waiting on deletions for: %+v", rcKey, deletedKeys)
    u.uidStoreLock.Lock()
    defer u.uidStoreLock.Unlock()

    // 如果已存在并跟踪了一些UID,就写错误日志,也就是说理论上不应该发生这种情况
    if existing := u.GetUIDs(rcKey); existing != nil && existing.Len() != 0 {
        klog.Errorf("Clobbering existing delete keys: %+v", existing)
    }
    // 记录新的UID集合
    if err := u.uidStore.Add(&UIDSet{expectedUIDs, rcKey}); err != nil {
        return err
    }
    // 设置期望删除子对象的数量
    return u.ControllerExpectationsInterface.ExpectDeletions(rcKey, expectedUIDs.Len())
}

// DeletionObserved()覆盖了ControllerExpectationsInterface.DeletionObserved(),增加了已删除子对象的UID(deleteKey)。
func (u *UIDTrackingControllerExpectations) DeletionObserved(rcKey, deleteKey string) {
    u.uidStoreLock.Lock()
    defer u.uidStoreLock.Unlock()

    // deleteKey必须是跟踪的UID,否则不会影响期望值
    uids := u.GetUIDs(rcKey)
    if uids != nil && uids.Has(deleteKey) {
        klog.V(4).Infof("Controller %v received delete for pod %v", rcKey, deleteKey)
        u.ControllerExpectationsInterface.DeletionObserved(rcKey)
        uids.Delete(deleteKey)
    }
}

// DeleteExpectations()覆盖了ControllerExpectationsInterface.DeleteExpectations()。
func (u *UIDTrackingControllerExpectations) DeleteExpectations(rcKey string) {
    u.uidStoreLock.Lock()
    defer u.uidStoreLock.Unlock()

    // 删除Controller的期望,同时删除跟踪的UID(如果存在的话)
    u.ControllerExpectationsInterface.DeleteExpectations(rcKey)
    if uidExp, exists, err := u.uidStore.GetByKey(rcKey); err == nil && exists {
        if err := u.uidStore.Delete(uidExp); err != nil {
            klog.V(2).Infof("Error deleting uid expectations for controller %v: %v", rcKey, err)
        }
    }
}

 

Supongo que te gusta

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