张海东, 多点生活(成都)云原生开发工程师。
本篇主要介绍 Pilot 源码中的 ServiceEntryStore 及其推送 xDS 的流程。
本文为 Istio Pilot 源码分析系列的第二篇文章。
了解了 Pilot
源码的基本结构和启动流程之后,我们可以深入探索 Pilot
究竟是怎么下发 xDS
协议的,以及协议的生成逻辑。相信大家都会有这些疑问:控制面与数据面详细的交互过程是什么?到底什么时候才会增量推送?增量推送判断的逻辑是什么?非 Kubernetes
原生的服务(如存在于虚拟机的服务、 Dubbo
服务等)到底是怎么注册并且经过一系列转化下发至数据面的?
带着这些问题,开始我们今天对 Pilot
的探索。
注:本文基于 istio release-1.7
分支分析,其他版本的代码结构会有所不同。
ServiceEntryStore
在多点落地 ServiceMesh
的过程中,大量的用到了 ServiceEntry
,每一个 Dubbo
服务都会映射一个 ServiceEntry
创建在 Kubernetes
里。 ServiceEntry
的作用就是将集群外部的服务注册到 Pilot
中,再统一由 ServiceController
进行管理。相应的,管理外部服务实例的对象为 WorkloadEntry
, ServiceEntry
可以通过 LabelSelector
筛选出自身对应的实例。
ServiceEntry
是作为 CR (Custome Resource) 保存在 Kubernetes
集群里的(也可以通过 MCP 服务直接发送给 Pilot
),暂时只讨论在集群中创建 CR 的情况。在上一篇源码分析中我们介绍到, Pilot
是通过 ConfigController
来监听创建在集群中的 CR 的, ServiceEntry
也不例外,保存这些 CR 的 ConfigStore
会被转化为 ServiceEntryStore
中的 store
(转化的详情见上一篇源码分析),这就是最终 Pilot
存储 ServiceEntry
的地方。当监听的资源推送更改的事件时,会触发 ServiceEntryStore
对应的 handler
处理后续的流程。
我们先来看一下 ServiceEntryStore
的结构和它提供的方法:
// istio/pilot/pkg/serviceregistry/serviceentry/servicediscovery.go:61
// ServiceEntryStore communicates with ServiceEntry CRDs and monitors for changes
type ServiceEntryStore struct {
XdsUpdater model.XDSUpdater // 用来接收 EnvouXdsServer 的接口,主要用来 Push 相应的 xDS 更新请求
store model.IstioConfigStore // 保存 ServiceEntry 实例的地方
storeMutex sync.RWMutex // 读写 store 时需要的锁
// 以 hostname/namespace 以及类型(是服务还是实例)等作为索引的服务实例表
instances map[instancesKey]map[configKey][]*model.ServiceInstance
// seWithSelectorByNamespace 保存了每个 namespace 里所有的 ServiceEntry,也是作为一个索引供 handler 使用
seWithSelectorByNamespace map[string][]servicesWithEntry
refreshIndexes bool
...
}
可以看到除了 XdsUpdater
和 store
两个必须的结构外,其余大部分都是些资源的缓存和索引(索引键不同),为后续 handler
处理事件时提供便利。除了结构,还需要关注两个比较重要的 handler
:
// WorkloadEntry 变化时的处理逻辑
func (s *ServiceEntryStore) workloadEntryHandler(old, curr model.Config, event model.Event) {}
// ServiceEntry 变化时的处理逻辑
func (s *ServiceEntryStore) serviceEntryHandler(old, curr model.Config, event model.Event) {}
这两个 handler
的业务逻辑后文中再详细讨论,先来回忆下 ServiceEntryStore
的初始化流程: