本节说明 webhook 和 snp 组件,在 dubbo-cp 中:
- webhook 用于实现动态准入控制器的注入功能。
- snp(ServiceNameMapping)用于记录接口名 InterfaceName 到应用名 AppName 的映射并更新到 SNP CRD 中,通过 DDS 的通知各个 Dubbo 应用进行流量控制等数据平面的操作。
webhook
webhook 组件结构体定义如下,主要包括以下几个部分:
- WebhookServer:动态准入控制的 Webhook 服务器。
- JavaInjector:用于在创建 Pod 时注入信息。
1 | type WebhookServer struct { |
Webhook 服务器
Webhook 服务器用于监听 Kubernetes 的动态准入控制请求,进而修改创建 Pod 的 Spec 信息实现信息注入。HTTP Path 为 mutating-services,Handler 为 Mutate。
其中 Webhook.Paches 为注入函数,getCertificate 为 dubbo-cp 服务器证书的获取方式,服务器证书通过证书管理模块管理。
1
2
3
4
5 webhookServer.WebhookServer = webhook.NewWebhook(
func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
return rt.CertStorage().GetServerCert(info.ServerName), nil
})
webhookServer.WebhookServer.Init(rt.Config())
1 | type ( |
Mutate
Webhook 的 Mutate 方法用于动态准入控制,修改 Pod,注入信息。在 Mutate 执行的过程中,会依次执行 Patches 内所定义的函数,完成信息的注入。
更新 K8s webhook 配置
webhook 在启动时会更新 Kubernetes 动态准入 webhook 配置,更新的是 Kubernetes MutatingWebhookConfiguration 资源,用于指定:
- 在创建 Pod 时进行动态准入配置。
- 配置在进行动态准入控制 webhook 的地址为 dubbo-cp Kubernetes Service。
- 配置在 webhook 过程中所信任的 webhook 服务器 CA 证书。
在后续过程中,dubbo-cp 的证书管理模块用于更新动态准入 webhook 的所使用的证书。
注入信息
dubbo-cp 在创建 Pod 时,注入的信息目前包括两类:
为 Dubbo 应用 Pod 注入 Registry 信息。通过 dubboctl 安装的组件中,zk 和 nacos 会被分别打上
dubbo.apache.org/zookeeper: true
,dubbo.apache.org/nacos: true
的标签。- 当用户在 Pod 里面打上相应 labels 表示需要自动注入注册中心地址时,会将注册中心的地址(通过查找带有相应 label 的 Service 得到注册中心地址)注入到 DUBBO_REGISTRY_ADDRESS 环境变量中去,以供 Dubbo SDK 使用。
- 默认的注册中心优先级为 zookeeper > nacos > k8s。
- 当 Pod 的 DUBBO_REGISTRY_ADDRESS 环境变量已经有值时,不会进行注入。
注入 CA 信息。
当用户在 Pod 里面打上相应 labels 表示需要自动注入 CA 信息时,会自动注入 volumes 和环境变量。
当 Pod 中已经存在相关信息时,不会信息注入。
在注入 CA 信息时,包括将 ServiceAccount Token 注入到指定路径下,将 CA 证书注入到指定目录下。
在注入环境变量时,会注入 CA 地址(也就是 dubbo-cp Kubernetes Service 地址)、CA 证书目录、认证 Token、Token 类型以供 Dubbo 应用使用。
1 | func (s *JavaSdk) injectContainers(c *v1.Container) { |
snp
snp(ServiceNameMapping)用于记录接口名 InterfaceName 到应用名 AppName 的映射并更新到 SNP CRD 中,通过 DDS 的通知各个 Dubbo 应用进行流量控制等数据平面的操作。
接口定义
Dubbo 应用会向 SNP 接口上报 Service Name Mapping,即这个节点所在的 namespace、应用名,以及这个 dubbo 应用所拥有的接口名。
在 Dubbo 中,应用名对应于一个 Dubbo 应用,接口名对应于 Protobuf 文件中定义的 Service(一个应用中可能存在多个接口)。
1 | // Provides an service for reporting the mapping relationship between interface => cluster |
接口实现
在 SNP 服务器中,核心是一个队列(chan)。在服务器收到 dubbo 应用上报的 snp 信息后,将映射关系推送到队列中。
映射关系为 (namespace, InterfaceName) —> []ApplicationName
SNP CRD 的定义也是如此,其 spec 定义如下。一个 CRD 对象记录包含 InterfaceName 的所有 dubbo 应用。
1
2
3
4 message ServiceNameMapping {
string interfaceName = 1;
repeated string applicationNames = 2;
}
SNP 服务器会定期的检查队列进行合并(防止网络抖动)后,更改 ServiceNameMapping CRD,DDS 通过 Informer 机制检查到 CRD 变化后,通知各个 Dubbo 应用(数据平面)进行流量控制等数据平面的操作。
debounce
debounce 就是定期去检查队列,经过合并后通过 pushFn 推送更新(在这里为更新 CRD),这里借鉴了 istio 的 debounce 机制。
在还没有 pushFn 推送更新时,如果有新的 snp 信息到达,则会调用 Merge 方法,对 snp 信息进行合并。
1 | func (s *Snp) debounce(stopCh <-chan struct{}, pushFn func(req *RegisterRequest)) { |
pushFn
在 snp 中,推送更新实际上就是更新 ServiceNameMapping CRD。每一个 Interface 为一个对象,创建或者更新相映的对象。
1 | func tryRegister(kubeClient versioned.Interface, namespace, interfaceName string, newApps []string) error { |