云原生kubernetes服务发现原理图解
概述
上节分析了
Prometheus
服务发现核心流程(如下图),
Discoverer
基于不同协议发现采集点,通过
channel
通知到
updater
协程,然后更新到
discoveryManager
结构体
trargets
字段中,最终由
sender
协程将
discoveryManager
的
targets
字段数据发送给
scrape
采集模块。
Discoverer
定义的接口类型,不同的服务发现协议基于该接口进行实现:
type Discoverer interface {
// Run hands a channel to the discovery provider (Consul, DNS, etc.) through which
// it can send updated target groups. It must return when the context is canceled.
// It should not close the update channel on returning.
Run(ctx context.Context, up chan<- []*targetgroup.Group)
}
k8s协议配置
Prometheus
本身就是作为云原生监控出现的,所以对云原生服务发现支持具有天然优势。
kubernetes_sd_configs
服务发现协议核心原理就是利用
API Server
提供的
Rest接口
获取到云原生集群中的
POD
、
Service
、
Node
、
Endpoints
、
Endpointslice
、
Ingress
等对象的元数据,并基于这些信息生成
Prometheus
采集点,并且可以随着云原生集群状态变更进行动态实时刷新。
❝
kubernetes
云原生集群的
POD
、
Service
、
Node
、
Ingress
等对象元数据信息都被存储到
etcd
数据库中,并通过
API Server
组件暴露的
Rest
接口方式提供访问或操作这些对象数据信息。
❞
**「
kubernetes_sd_configs
配置示例:」**
- job_name: kubernetes-pod
kubernetes_sd_configs:
- role: pod
namespaces:
names:
- 'test01'
api_server: https://apiserver.simon:6443
bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
tls_config:
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
配置说明:
api_server
指定API Server
地址,出于安全考虑,这些接口是带有安全认证的,bearer_token_file
和ca_file
则指定访问API Server
使用到的认证信息;role
指定基于云原生集群中哪种对象类型做服务发现,支持POD
、Service
、Node
、Endpoints
、Endpointslice
、Ingress
六种类型;namespaces
指定作用于哪个云原生命名空间下的对象,不配置则对所有的云原生命名空间生效;
「为什么没有配置api server信息也可以正常进行服务发现?」
很多时候我们并不需要配置
api server
相关信息也可以进行服务发现,如我们将上面示例简化如下写法:
- job_name: kubernetes-pod
kubernetes_sd_configs:
- role: pod
namespaces:
names:
- 'test01'
一般
Prometheus
部署在监控云原生集群上,从
Pod
使用
Kubernetes API
官方客户端库(
client-go
)提供了更为简便的方法:
rest.InClusterConfig()
。
API Server
地址是从
POD
的环境变量
KUBERNETES_SERVICE_HOST
和
KUBERNETES_SERVICE_PORT
构建出来,
token
以及
ca
信息从
POD
固定的文件中获取,因此这种场景下
kubernetes_sd_configs
中
api_server
和
ca_file
是不需要配置的。
❝
client-go
是
kubernetes
官方提供的
go
语言的客户端库,
go
应用使用该库可以访问
kubernetes
的
API Server
,这样我们就能通过编程来对
kubernetes
资源进行增删改查操作。
❞
Informer机制
从之前分析的服务发现协议接口设计得知,了解
k8s
服务发现协议入口在
discovery/kubernetes.go
的
Run
方法:
Run
方法中
switch
罗列出不同
role
的处理逻辑,刚好和配置示例中
role
支持的六种云原生对象类型对应,只是基于不同的对象进行服务发现,基本原理都是一致的。
云原生服务发现基本原理是访问
API Server
获取到云原生集群资源对象,
Prometheus
与
API Server
进行交互这里使用到的是
client-go
官方客户端里的
Informer
核心工具包。
Informer
底层使用
ListWatch
机制,在
Informer
首次启动时,会调用
List API
获取所有最新版本的资源对象,缓存在内存中,然后再通过
Watch API
来监听这些对象的变化,去维护这份缓存,降低
API Server
的负载。除了
ListWatch
,
Informer
还可以注册自定义事件处理逻辑,之后如果监听到事件变化就会调用对应的用户自定义事件处理逻辑,这样就实现了用户业务逻辑扩展。
Informer
机制工作流程如下图:
Informer
机制本身比较复杂,这里先暂时不太具体说明,只需要理解
Prometheus
使用
Informer
机制获取和监听云原生资源对象,即上图中只有「绿色框部分是自定义业务逻辑」,其它都是
client-go
框架
informer
工具包提供的功能。
这其中的关键就是注册自定义
AddFunc
、
DeleteFunc
和
UpdateFunc
三种事件处理器,分别对应增、删、改操作,当触发对应操作后,事件处理器就会被回调感知到。比如云原生集群新增一个
POD
资源对象,则会触发
AddFunc
处理器,该处理器并不做复杂的业务处理,只是将该对象的
key
放入到
Workqueue
队列中,然后
Process Item
组件作为消费端,不停从
Workqueue
中提取数据获取到新增
POD
的
key
,然后交由
Handle Object
组件,该组件通过
Indexer
组件提供的
GetByKey()
查询到该新增
POD
的所有元数据信息,然后基于该
POD
元数据就可以构建采集点信息,这样就实现
kubernetes
服务发现。
「为什么需要Workqueue队列?」
Resource Event Handlers
组件注册自定义事件处理器,获取到事件时只是把对象
key
放入到
Workerqueue
中这种简单操作,而没有直接调用
Handle Object
进行事件处理,这里主要是避免阻塞影响整个
informer
框架运行。如果
Handle Object
比较耗时放到
Resource Event Handlers
组件中直接处理,可能就会影响到④⑤功能,所以这里引入
Workqueue
类似于
MQ
功能实现解耦。
源码分析
熟悉了上面
Informer机制
,下面以
role=POD
为例结合
Prometheus
源码梳理下上面流程。
1、创建和
API Server
交互底层使用的
ListWatch
工具;
2、基于
ListWatch
创建
Informer
;
3、注册资源事件,分别对应资源创建、资源删除和资源更新事件处理;
❝
这里的
podAddCount
、
podDeleteCount
和
podUpdateCount
分别对应下面三个指标序列,指标含义也比较明显:
prometheus_sd_kubernetes_events_total(role="pod", event="add")
prometheus_sd_kubernetes_events_total(role="pod", event="delete")
prometheus_sd_kubernetes_events_total(role="pod", event="update")
role
标识资源类型,包括:
"endpointslice", "endpoints", "node", "pod", "service", "ingress"
五种类型;
event
标识事件类型,包括:
"add", "delete", "update"
三种类型。
❞
4、事件处理,
AddFunc
、
DeleteFunc
和
UpdateFunc
注册的事件处理逻辑一样,处理逻辑也比较简单:就是获取资源对象
key
,并将其写入到
Workqueue
中;
❝
对于
POD
资源,这里的
key
就是:
namespace/pod_name
格式,比如
key=test01/nginx-deployment-5ffc5bf56c-n2pl8
。
❞
5、给
Workqueue
注册一个无限循环处理逻辑,就能持续从
Workqueue
中取出
key
进行处理;
❝
针对
Pod
里的每个
Container
上的每个
port
,都会生成一个对应采集点
target
,其中
__address__
就是
PodIP
port
组合。
❞
6、最后启动
Informer
,让整个流程运转起来;
「更多云原生监控运维知识,请关注公众号:Reactor2020」
版权归原作者 Reactor2020 所有, 如有侵权,请联系我们删除。