k8s Informer 中 Indexer 的解析
1. Indexer 概述
Indexer 是 Kubernetes client-go 提供的一种本地存储机制,用于对资源对象进行索引和缓存。它通过与 ETCD 数据保持同步,允许客户端直接从本地缓存中获取资源对象,而无需每次都向 APIServer 发起请求。这种方式有效减轻了 APIServer 和 etcd 的压力。
在 Informer 的工作流程中,DeltaFIFO 队列中的资源对象会被弹出并交由 HandleDeltas 方法处理。该方法将资源对象同步到 Indexer 中。
// k8s.io/client-go/tools/cache/shared_informer.go
func (s *sharedIndexInformer) HandleDeltas(obj interface{}) error {
s.blockDeltas.Lock()
defer s.blockDeltas.Unlock()
if deltas, ok := obj.(Deltas); ok {
return processDeltas(s, s.indexer, s.transform, deltas)
}
return errors.New("invalid object type")
}
// k8s.io/client-go/tools/cache/controller.go
func processDeltas(handler ResourceEventHandler, store Store, transform TransformFunc, deltas Deltas) error {
for _, delta := range deltas {
obj := delta.Object
if transform != nil {
var err error
obj, err = transform(obj)
if err != nil {
return err
}
}
switch delta.Type {
case Sync, Replaced, Added, Updated:
if oldObj, exists, err := store.Get(obj); err == nil && exists {
if err := store.Update(obj); err != nil {
return err
}
handler.OnUpdate(oldObj, obj)
} else {
if err := store.Add(obj); err != nil {
return err
}
handler.OnAdd(obj)
}
case Deleted:
if err := store.Delete(obj); err != nil {
return err
}
handler.OnDelete(obj)
}
}
return nil
}
2. Indexer 接口定义
Indexer 接口继承自 Store 接口,并扩展了索引相关功能。
type Indexer interface {
Store
Index(indexName string, obj interface{}) ([]interface{}, error)
IndexKeys(indexName, indexedValue string) ([]string, error)
ListIndexFuncValues(indexName string) []string
ByIndex(indexName, indexedValue string) ([]interface{}, error)
GetIndexers() Indexers
AddIndexers(newIndexers Indexers) error
}
type Store interface {
Add(obj interface{}) error
Update(obj interface{}) error
Delete(obj interface{}) error
List() []interface{}
ListKeys() []string
Get(obj interface{}) (item interface{}, exists bool, err error)
GetByKey(key string) (item interface{}, exists bool, err error)
Replace([]interface{}, string) error
Resync() error
}
2.1 Store 结构体
Store 的实现基于 cache 结构体,其中包含一个线程安全的存储接口 ThreadSafeStore 以及用于生成唯一键值的函数 KeyFunc。
type cache struct {
cacheStorage ThreadSafeStore
keyFunc KeyFunc
}
2.2 ThreadSafeStore 接口
ThreadSafeStore 接口定义了操作本地缓存的核心方法,包括增删改查以及索引功能。
type ThreadSafeStore interface {
Add(key string, obj interface{})
Update(key string, obj interface{})
Delete(key string)
Get(key string) (item interface{}, exists bool)
List() []interface{}
ListKeys() []string
Replace(map[string]interface{}, string)
Index(indexName string, obj interface{}) ([]interface{}, error)
IndexKeys(indexName, indexedValue string) ([]string, error)
ListIndexFuncValues(name string) []string
ByIndex(indexName, indexedValue string) ([]interface{}, error)
GetIndexers() Indexers
Resync() error
}
2.3 threadSafeMap 实现
threadSafeMap 是 ThreadSafeStore 接口的具体实现,支持资源对象的存储和索引功能。
type threadSafeMap struct {
lock sync.RWMutex
items map[string]interface{}
indexers Indexers
indices Indices
}
3. 索引功能详解
Indexer 的核心功能是提供快速查询能力,这依赖于 threadSafeMap 结构体中的 indexers 和 indices 属性。
3.1 MetaNamespaceIndexFunc
这是一个常用的默认索引函数,基于对象的命名空间进行索引。
func MetaNamespaceIndexFunc(obj interface{}) ([]string, error) {
meta, err := meta.Accessor(obj)
if err != nil {
return []string{""}, fmt.Errorf("object has no metadata: %v", err)
}
return []string{meta.GetNamespace()}, nil
}
3.2 ByIndex 函数
ByIndex 方法根据索引器名称和索引键值查找对应的对象列表。
func (c *threadSafeMap) ByIndex(indexName, indexedValue string) ([]interface{}, error) {
c.lock.RLock()
defer c.lock.RUnlock()
indexFunc := c.indexers[indexName]
if indexFunc == nil {
return nil, fmt.Errorf("index with name %s does not exist", indexName)
}
index := c.indices[indexName]
set := index[indexedValue]
result := make([]interface{}, 0, len(set))
for key := range set {
result = append(result, c.items[key])
}
return result, nil
}
4. 使用示例
pods, err := index.ByIndex("namespace", "default")
if err != nil {
panic(err)
}
for _, pod := range pods {
fmt.Println(pod.(*v1.Pod).Name)
}
fmt.Println("------")
pods, err = index.ByIndex("nodename", "node1")
if err != nil {
panic(err)
}
for _, pod := range pods {
fmt.Println(pod.(*v1.Pod).Name)
}
输出结果
pod-1
pod-2
------
pod-1