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

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_utils.go.

2. ControllerExpectationsInterface

architecture.png

该接口会在controller中被用到, 比如在replicasetcontroller中会被用来决定是否需要重新计算replicas数与实际数目是否已经相等, 如果不相等, 则需要进行计算需要删除多少pod或者需要增加多少pod等等.

2.1 ControlleeExpectations

type ControllerExpectationsInterface interface {
    GetExpectations(controllerKey string) (*ControlleeExpectations, bool, error)
    SatisfiedExpectations(controllerKey string) bool
    DeleteExpectations(controllerKey string)
    SetExpectations(controllerKey string, add, del int) error
    ExpectCreations(controllerKey string, adds int) error
    ExpectDeletions(controllerKey string, dels int) error
    CreationObserved(controllerKey string)
    DeletionObserved(controllerKey string)
    RaiseExpectations(controllerKey string, add, del int)
    LowerExpectations(controllerKey string, add, del int)
}
type Expectations interface {
    Fulfilled() bool
}
// ControlleeExpectations track controllee creates/deletes.
type ControlleeExpectations struct {
    add       int64
    del       int64
    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)
}
func (exp *ControlleeExpectations) isExpired() bool {
    return clock.RealClock{}.Since(exp.timestamp) > ExpectationsTimeout
}

ControlleeExpectations: 用来维护某个keyadddel这两个变量. 另外Fulfilled的条件是这两个都已经小于等于0了.

2.2 ControllerExpectations

type ControllerExpectations struct {
    cache.Store
}
func NewControllerExpectations() *ControllerExpectations {
    return &ControllerExpectations{cache.NewStore(ExpKeyFunc)}
}
func (r *ControllerExpectations) SetExpectations(controllerKey string, add, del int) error {
    exp := &ControlleeExpectations{add: int64(add), del: int64(del), key: controllerKey, timestamp: clock.RealClock{}.Now()}
    klog.V(4).Infof("Setting expectations %#v", exp)
    return r.Add(exp)
}
func (r *ControllerExpectations) DeleteExpectations(controllerKey string) {
    if exp, exists, err := r.GetByKey(controllerKey); err == nil && exists {
        if err := r.Delete(exp); err != nil {
            klog.V(2).Infof("Error deleting expectations for controller %v: %v", controllerKey, err)
        }
    }
}
func (r *ControllerExpectations) GetExpectations(controllerKey string) (*ControlleeExpectations, bool, error) {
    if exp, exists, err := r.GetByKey(controllerKey); err == nil && exists {
        return exp.(*ControlleeExpectations), true, nil
    } else {
        return nil, false, err
    }
}

该结构体ControllerExpectations只有一个变量cache.Store, 用来存储的, 明显存储的是ControlleeExpectations类型.
SetExpectations: 构造了一个ControlleeExpectations方法进行存储起来.
DeleteExpectations: 删除这个key对应的ControlleeExpectations.
GetExpectations: 获取该controllerKey对应的ControlleeExpectations.

接下来看一下它的更新方法, 都是常规操作, 操作store里面的内容.

func (r *ControllerExpectations) ExpectCreations(controllerKey string, adds int) error {
    return r.SetExpectations(controllerKey, adds, 0)
}
func (r *ControllerExpectations) ExpectDeletions(controllerKey string, dels int) error {
    return r.SetExpectations(controllerKey, 0, dels)
}
func (r *ControllerExpectations) LowerExpectations(controllerKey string, add, del int) {
    if exp, exists, err := r.GetExpectations(controllerKey); err == nil && exists {
        exp.Add(int64(-add), int64(-del))
        // The expectations might've been modified since the update on the previous line.
        klog.V(4).Infof("Lowered expectations %#v", exp)
    }
}
func (r *ControllerExpectations) RaiseExpectations(controllerKey string, add, del int) {
    if exp, exists, err := r.GetExpectations(controllerKey); err == nil && exists {
        exp.Add(int64(add), int64(del))
        // The expectations might've been modified since the update on the previous line.
        klog.V(4).Infof("Raised expectations %#v", exp)
    }
}
func (r *ControllerExpectations) CreationObserved(controllerKey string) {
    r.LowerExpectations(controllerKey, 1, 0)
}
func (r *ControllerExpectations) DeletionObserved(controllerKey string) {
    r.LowerExpectations(controllerKey, 0, 1)
}

这里都是更新方法, 方法很简单, 但是每个方法背后的意义还是需要结合真正的controller来进行说明. (后面分析controller的时候会有说明)

这里一个比较重要的方法就是SatisfiedExpectations方法

// 返回true时 代表需要去同步
func (r *ControllerExpectations) SatisfiedExpectations(controllerKey string) bool {
    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() {
            // 已经过期了 需要去同步
            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 {
        // When a new controller is created, it doesn't have expectations.
        // When it doesn't see expected watch events for > TTL, the expectations expire.
        //  - In this case it wakes up, creates/deletes controllees, and sets expectations again.
        // When it has satisfied expectations and no controllees need to be created/destroyed > TTL, the expectations expire.
        //  - In this case it continues without setting expectations till it needs to create/delete controllees.
        klog.V(4).Infof("Controller %v either never recorded expectations, or the ttl expired.", controllerKey)
    }
    // Trigger a sync if we either encountered and error (which shouldn't happen since we're
    // getting from local store) or this controller hasn't established expectations.
    return true
}

该方法会在分析replicasetcontroller时进行说明.

2.3 UIDTrackingControllerExpectations

type UIDTrackingControllerExpectations struct {
    ControllerExpectationsInterface
    uidStoreLock sync.Mutex
    uidStore cache.Store
}
func NewUIDTrackingControllerExpectations(ce ControllerExpectationsInterface) *UIDTrackingControllerExpectations {
    return &UIDTrackingControllerExpectations{ControllerExpectationsInterface: ce, uidStore: cache.NewStore(UIDSetKeyFunc)}
}
type UIDSet struct {
    sets.String
    key string
}

该结构体的设计是为了期望删除的时候需要知道删除哪些元素, 因为ControllerExpectationsInterface中只能用存储的del属性只能是一个数字, 并不能知道具体要删除哪些对象.
所以在此基础上加入UIDSet表明该key具体要删除哪些对象. 因此除了维护ControlleeExpectations之外还要维护UIDSet.

看一下如何存储的, 保存了ControlleeExpectationsUIDSet.

func (u *UIDTrackingControllerExpectations) ExpectDeletions(rcKey string, deletedKeys []string) error {
    u.uidStoreLock.Lock()
    defer u.uidStoreLock.Unlock()
    // 如果以前存在
    if existing := u.GetUIDs(rcKey); existing != nil && existing.Len() != 0 {
        klog.Errorf("Clobbering existing delete keys: %+v", existing)
    }
    expectedUIDs := sets.NewString()
    for _, k := range deletedKeys {
        expectedUIDs.Insert(k)
    }
    klog.V(4).Infof("Controller %v waiting on deletions for: %+v", rcKey, deletedKeys)
    // 维护一个UIDSet
    if err := u.uidStore.Add(&UIDSet{expectedUIDs, rcKey}); err != nil {
        return err
    }
    // 维护ControlleeExpectations
    return u.ControllerExpectationsInterface.ExpectDeletions(rcKey, expectedUIDs.Len())
}
func (u *UIDTrackingControllerExpectations) GetUIDs(controllerKey string) sets.String {
    if uid, exists, err := u.uidStore.GetByKey(controllerKey); err == nil && exists {
        return uid.(*UIDSet).String
    }
    return nil
}

具体删除某一个

func (u *UIDTrackingControllerExpectations) DeletionObserved(rcKey, deleteKey string) {
    u.uidStoreLock.Lock()
    defer u.uidStoreLock.Unlock()

    uids := u.GetUIDs(rcKey)
    if uids != nil && uids.Has(deleteKey) {
        klog.V(4).Infof("Controller %v received delete for pod %v", rcKey, deleteKey)
        //维护ControlleeExpectations
        u.ControllerExpectationsInterface.DeletionObserved(rcKey)
        // 维护UIDSet
        uids.Delete(deleteKey)
    }
}

删除全部某个controllerKey的全部内容.

func (u *UIDTrackingControllerExpectations) DeleteExpectations(rcKey string) {
    u.uidStoreLock.Lock()
    defer u.uidStoreLock.Unlock()

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

推荐阅读更多精彩内容