美文网首页
浅谈 Kubernetes client-go 的 indexe

浅谈 Kubernetes client-go 的 indexe

作者: shinwing | 来源:发表于2021-01-29 12:09 被阅读0次

    不瞒你说,作为一个蠢笨的人,indexer部分我来来去去看了不下有三四遍。依然一脸懵逼,虽然知道它是做什么用的,但是让我给别人解释我就露出了南郭先生的笑。
    Indices、Index、Indexers、 IndexFunc。别的不说,单看看这些名词就有掀桌子的冲动。这两天模拟group by操作,抄了一遍代码,好像抓住了那么一点点头绪,赶紧记下来。

    感觉 k8s 这部分的功能就是一个数据库,底层的数据维护在 etcd 的 kv里面,上层基于kv的缓存做了一些group by的操作。因为它需要存的类型很多,所以这个部分做的比较灵活,代价就是有点绕。
    其实也没那么复杂,简单理解的话,可以先临时把这个indexer理解为一个map套map的结构,这样就行了。
    譬如,map<String, map<String, name>> 这样一个数据结构,就可以记录类似 <"namespace”, <"default", "Pod1">> 这样的数据。先有这样一个印象,然后稍微详细的说一下。

    先看 Indices 和 Index 这两个数据结构。其实就是嵌套的map了。

    // 这个地方的key就是索引的名字,类似于 "namespace", "node", "zone"
    type Indices map[string]Index
    // 这个地方记录的就是类似于 "default" -> "Pod1", "Pod2", "Pod3"
    type Index map[string]sets.String
    

    其他的数据结构都是维护这两个而存在的,就是这么简单。

    type IndexFunc func(obj interface{}) ([]string, error)
    

    使用的时候需要实现这个方法,譬如我想要用namespace来作为key的时候,就需要实现一个方法,传入类似于pod的资源,返回这个pod的namespace的名字。
    这里还有一个弯弯绕,困扰了我好久,就是为什么返回的是一个数组。
    大多数情况,其实使用的时候仅仅需要返回一个string就可以了。譬如ns名字啊,node名字啊,zone的名字啊之类的。
    但是,在某些比较特殊的业务上,譬如一台机器是GPU的,又同时负责了CPU的任务,那么可能就会给这个机器打一个标签,类似于pool='gpu,cpu',这时候我们实现方法的时候,就可以返回一个数组[]string{"gpu","cpu"} 这样的话,这个机器的名字就可以出现在两个索引里面了。(其实用到的地方应该不多,理解了就行了)

    只计算一个纬度肯定是不够的啊,最简单的,也需要查询一个node上有哪些pod,一个ns下有哪些pod,一个deploy下面有哪些pod吧。所以需要一个数据结构来记录,indexKey对应的获取indexValue的方法是啥,便于调用。

    type Indexers map[string]IndexFunc
    

    有了合适的数据结构,最后就是一个关键方法了,updateIndices 简单的说,就是pod在创建和更新的时候调用一下这个回调方法,更新一下索引

    // updateIndices 更新索引, 代码被我简单化了一下
    func (c *threadSafeMap) updateIndices(oldObj interface{}, newObj interface{}, key string) {
       // 如果有旧的对象,需要先从索引中删除这个对象
       if oldObj != nil {
            c.deleteFromIndices(oldObj, key)
       }
        // 循环所有的索引器,譬如 namespace 对应的方法, node 对应的方法,其实就是pod 的 getNamespace,getNode
       for name, indexFunc := range c.indexers {
          // 获取对象的索引键, 获取到 pod 实际的 namespace 和 nodename
          indexValues, err := indexFunc(newObj)
          // 得到当前索引器的索引, 从二级map里面获取到底层的map
          index := c.indices[name]
          // 循环所有的索引键,看起来是个数组,其实大部分情况下长度只有1
          for _, indexValue := range indexValues {
              // 得到索引键对应的对象键列表, 插入到set里面
             index[indexValue].Insert(key)
          }
       }
    }
    

    没了,就这么简单。通过上述的数据结构,k8s方便的完成了group的操作,可以从缓存里面便利的拿到想要的一组数据。
    对了,这部分的代码不单单看懂就完啦,我们可以自己实现IndexFunc的,从而可以便利的实现自己的索引。譬如我想要找一下所有被驱逐过的pod啊,所有属于某个业务的pod啊,就可以通过标签和注释来进行分类。数据结构里面保留的其实只是podname,最终pod的obj只存了一份,还是比较节省空间的。

    啥时候我才能不那么蠢笨呢。

    相关文章

      网友评论

          本文标题:浅谈 Kubernetes client-go 的 indexe

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