不久前,咱们在文章《如何扩展单个Prometheus实现近万Kubernetes集群监控?》中详细介绍了TKE团队大规模Kubernetes联邦监控系统Kvass的演进过程,其中介绍了针对规模较大的集群,咱们是如何经过修改Prometheus代码来实现横向扩缩容的。通过方案上的改进,Kvass目前已经支持以Sidecar的方式实现Prometheus集群化,而不是修改Prometheus代码。因为方案对社区有必定价值,团队决定将项目开源出来,分享给社区。项目地址node
本文首先将给出Prometheus的单机性能瓶颈,以及现有的社区集群化方案,随后将详细介绍开源版Kvass的设计思想,使用案例及压测结果。linux
另外,腾讯云容器团队在Kvass的设计思想上进一步优化,构建了支持多集群的高性能云原生监控服务,产品目前已正式公测,欢迎读者试用。传送门: https://console.cloud.tencent...git
后续章节咱们也将直接使用该产品来展现Kvass对大规模集群的监控能力。github
Prometheus依靠其强劲的单机性能,灵活的PromSQL,活跃的社区生态,逐渐成为云原生时代最核心的监控组件,被全球各大产商用于监控他们的核心业务。golang
然而,面对大规模监控目标(数千万series)时,因为原生Prometheus只有单机版本,不提供集群化功能,开发人员不得不经过不断增长机器的配置来知足Prometheus不断上涨的内存。api
咱们对单机Prometheus进行的压测,用以探测单个Prometheus分片的合理负载,压测的目标有两个。缓存
咱们保持总series为100万不变, 经过改变target个数,观察Prometheus负载变更。
压测结果架构
target数量 | CPU (core) | mem (GB) |
---|---|---|
100 | 0.17 | 4.6 |
500 | 0.19 | 4.2 |
1000 | 0.16 | 3.9 |
5000 | 0.3 | 4.6 |
咱们保持target数目不变,经过改变总series数,观察Prometheus的负载变更。并发
压测结果app
series数量 (万) | CPU (core) | mem (GB) | 查询1000 series 15m数据(s) |
---|---|---|---|
100 | 0.191 | 3.15 | 0.2 |
300 | 0.939 | 20.14 | 1.6 |
500 | 2.026 | 30.57 | 1.5 |
压测过程当中,咱们使用了工具去生成预期数目的series,工具生成的series每一个label的长度及值的长度都较小,固定为10个字符左右。咱们的目的是观察相对负载变化,实际生产中因为label长度不一样,服务发现机制的消耗不一样,相同的series数目所消耗的负载会比压测中高很多。
针对单机Prometheus在大规模数据监控时的性能瓶颈问题,社区目前已经存在一些分片化方案,主要包括如下几种。
Prometheus官方支持经过Relabel机制,在配置文件中,对采集上来的数据进行hash,经过在不一样Prometheus实例的配置文件中指定不一样的moduleID来进行分片化,而后经过联邦,Thanos等方式将数据进行统一汇总,以下图所示,读者也能够直接参考【官方文档】。
还有一种方法是根据业务进行job层面的分割,不一样Prometheus使用彻底独立的采集配置,其中包含了不一样的job,。
不管是hash_mod的方式,仍是配置文件分割的方式,其本质都是将数据切分到多个采集配置中,由不一样Prometheus进行采集。二者都存在如下几个缺点。
针对上述问题,咱们但愿设计一种无侵入的集群化方案,它对使用者表现出来的,是一个与原生Prometheus配置文件一致,API兼容,可扩缩容的虚拟Prometheus。具体而言,咱们有如下设计目标。
Kvass由多个组件构成,下图给出了Kvass的架构图,咱们在架构图中使用了Thanos,实际上Kvass并不强依赖于Thanos,能够换成其余TSDB。
Kvass coordinaor 首先会代替Prometheus对采集目标作服务发现,实时得到须要采集的target列表。
针对这些target,Kvass coordinaor会负责对其作负载探测,评估每一个target的series数,一旦target负载被探测成功,Kvass coordinaor 就会在下个计算周期将target分配给某个负载在阈值如下的分片。
Kvass coordinaor 还负责对分片集群作扩缩容。
Kvass coordinaor引用了原生Prometheus的服务发现代码,用于实现与Prometheus 100%兼容的服务发现能力,针对服务发现获得的待抓取targets,Coordinaor会对其应用配置文件中的relabel_configs进行处理,获得处理以后的targets及其label集合。服务发现后获得的target被送往负载探测模块进行负载探测。
负载探测模块从服务发现模块得到处理以后的targets,结合配置文件中的抓取配置(如proxy,证书等)对目标进行抓取,随后解析计算抓取结果,得到target的series规模。
负载探测模块并不存储任何抓取到的指标数据,只记录target的负载,负载探测只对target探测一次,不维护后续target的负载变化,长期运行的target的负载信息由Sidecar维护,咱们将在后面章节介绍。
在Prometheus单机性能瓶颈那一节,咱们介绍过Prometheus的内存和series相关,确切来讲,Prometheus的内存和其head series直接相关。Prometheus 会将最近(默认为2小时)采集到的数据的series信息缓存在内存中,咱们若是能控制好每一个分片内存中head series的数目,就能有效控制每一个分片的内存使用量,而控制head series实际就是控制分片当前采集的target列表。
基于上边的思路,Kvass coordinaor会周期性的对每一个分片当前采集的target列表进行管理:分配新target,删除无效target。
在每一个周期,Coordinaor会首先从全部分片得到当前运行状态,其中包括分片当前内存中的series数目及当前正在抓取的target列表。随后针对从服务发现模块获得的全局target信息进行如下处理
在系统运行过程当中,target有可能会被删除,若是某个分片的target被删除且超过2小时,则该分片中的head series就会下降,也就是出现了部分空闲,由于target分配到了不一样分片,若是有大量target被删除,则会出现不少分片的内存占用都很低的状况,这种状况下,系统的资源利用率很低,咱们须要对系统进行缩容。
当出现这种情时,Coordinaor会对target进行迁移,即将序号更大的分片(分片会从0进行编号)中的target转移到序号更低的分片中,最终让序号低的分片负载变高,让序号高的分片彻底空闲出来。若是存储使用了thanos,并会将数据存储到cos中,则空闲分片在通过2小时候会删除(确保数据已被传到cos中)。
Kvass的分片当前只支持以StatefulSet方式部署。
Coordinator将经过label selector来得到全部分片StatefulSet,每一个StatefulSet被认为是一个副本,StatefulSet中编号相同的Pod会被认为是同一个分片组,相同分片组的Pod将被分配相同的target并预期有相同的负载。
上文提到Coordinator根据配置文件作了服务发现,获得了target列表,因此Coordinator实际上能够获得/api/v1/targets接口所须要的返回结果集合,可是因为Coordinator只作了服务发现,并不进行实际采集,因此target的采集状态(例如健康状态,上一次采集时间等)都没法直接得知。
当Coordinator接收到/api/v1/targets请求时,他会基于服务发现获得的target集合,结合向Sidecar(若是target已分配)或向探测模块(target还未分配)询问target采集状态,综合后将正确的/api/v1/targets结果返回。
上一节介绍了Kvass coordinaor的基本功能,要想系统正常运行,还须要Kvass sidecar的配合,其核心思想是将配置文件中全部服务发现模式所有改为static_configs并直接将已经relabel过的target信息写入配置中,来达到消除分片服务发现和relabel行为,只采集部分target的效果。
每一个分片都会有一个Kvass sidecar,其核心功能包括从Kvass coordinator接受本分片负责的target列表,生成新的配置文件给该分片的Prometheus使用。另外,Kvass sidecar还会劫持抓取请求,维护target最新负载。Kvass sidecar还做为PrometheusAPI的网关,修正部分请求结果。
Coordinaor通过服务发现,relabel及负载探测后,会将target分配给某个分片,并将target信息下发给Sidecar,包括
Sidecar根据从Coordinator获得的target信息,结合原始配置文件,生成一个新的配置文件给Prometheus使用,这个新的配置文件作了以下改动。
咱们来看一个例子,假如原来的配置是一个kubelet的采集配置
global: evaluation_interval: 30s scrape_interval: 15s scrape_configs: - job_name: kubelet honor_timestamps: true metrics_path: /metrics scheme: https kubernetes_sd_configs: - role: node bearer_token: xxx tls_config: insecure_skip_verify: true relabel_configs: - separator: ; regex: __meta_kubernetes_node_label_(.+) replacement: $1 action: labelmap
经过注入将生成一个新的配置文件
global: evaluation_interval: 30s scrape_interval: 15s scrape_configs: - job_name: kubelet honor_timestamps: true metrics_path: /metrics scheme: https proxy_url: http://127.0.0.1:8008 # 全部抓取请求代理到Sidecar static_configs: - targets: - 111.111.111.111:10250 labels: __address__: 111.111.111.111:10250 __metrics_path__: /metrics __param__hash: "15696628886240206341" __param__jobName: kubelet __param__scheme: https # 保存原始的scheme __scheme__: http # 设置新的scheme,这将使得代理到Sidecar的抓取请求都是http请求 # 如下是通过relabel_configs处理以后获得的label集合 beta_kubernetes_io_arch: amd64 beta_kubernetes_io_instance_type: QCLOUD beta_kubernetes_io_os: linux cloud_tencent_com_auto_scaling_group_id: asg-b4pwdxq5 cloud_tencent_com_node_instance_id: ins-q0toknxf failure_domain_beta_kubernetes_io_region: sh failure_domain_beta_kubernetes_io_zone: "200003" instance: 172.18.1.106 job: kubelet kubernetes_io_arch: amd64 kubernetes_io_hostname: 172.18.1.106 kubernetes_io_os: linux
上边新生成的配置文件是Prometheus真正使用的配置文件,Sidecar经过Coordinator下发的target列表来生成配置,就可让Prometheus有选择性得进行采集。
在上边的配置生成中,咱们会将proxy注入到job的配置中,而且target的label中,scheme会被设置成http,因此Prometheus全部的抓取请求都会被代理到Sidecar,之因此要这么作,是由于Sidecar须要维护每一个target新的series规模,用于Coordinator查阅后做为target迁移的参考。
从上边配置生成咱们能够看到,有如下几个额外的请求参数会被一并发送到Sidecar
有了上述几个参数,Sidecar就能够对抓取目标发起正确的请求,并获得监控数据,在统计的target此次抓取的series规模后,Sidecar会将监控数据拷贝一份给Prometheus。
因为Sidecar的存在,部分发往Prometheus的API请求须要被特殊处理,包括
因为咱们将采集目标分散到了不一样分片中,致使每一个分片的数据都只是全局数据的一部分,因此咱们须要使用额外的组件来将全部数据进行汇总并去重(多副本的状况下),获得全局数据视图。
thanos是一个很是好的方案,经过加入thanos组件,能够很方便得获得kvass集群的全局数据视图。固然咱们也能够经过加入remote writer配置来使用其余TSDB方案,例如influxdb,M3等等。
这一节咱们经过一个部署例子,来直观感觉一下Kvass的效果,相关yaml文件能够在这里找到https://github.com/tkestack/k...
读者能够将项目clone到本地,并进入examples。
git clone https://github.com/tkestack/kvass.git cd kvass/examples
咱们提供了一个metrics数据生成器,能够指定生成必定数量的series,在本例子中,咱们将部署6个metrics生成器副本,每一个会生成10045 series (其中45 series为golang的metrics)。
kubectl create -f metrics.yaml
如今咱们部署基于Kvass的Prometheus集群,用以采集这6个metrics生成器的指标。
首先咱们部署rbac相关配置
kubectl create -f kvass-rbac.yaml
接着部署一个Prometheus config文件,这个文件就是咱们的原始配置,咱们在这个配置文件中,使用kubernetes_sd来作服务发现
kubectl create -f config.yaml
配置以下
global: scrape_interval: 15s evaluation_interval: 15s external_labels: cluster: custom scrape_configs: - job_name: 'metrics-test' kubernetes_sd_configs: - role: pod relabel_configs: - source_labels: [__meta_kubernetes_pod_label_app_kubernetes_io_name] regex: metrics action: keep - source_labels: [__meta_kubernetes_pod_ip] action: replace regex: (.*) replacement: ${1}:9091 target_label: __address__ - source_labels: - __meta_kubernetes_pod_name target_label: pod
如今咱们来部署Kvass coordinator
kubectl create -f coordinator.yaml
咱们在Coordinator的启动参数中设置每一个分片的最大head series数目不超过30000
--shard.max-series=30000
咱们如今就能够部署带有Kvass sidecar的Prometheus了,这里咱们只部署单个副本
kubectl create -f prometheus-rep-0.yaml
为了获得全局数据,咱们须要部署一个thanos-query
kubectl create -f thanos-query.yaml
根据上述计算,监控目标总计6个target, 60270 series,根据咱们设置每一个分片不能超过30000 series,则预期须要3个分片。
咱们发现,Coordinator成功将StatefulSet的副本数改为了3。
咱们看下单个分片内存中的series数目,发现只有2个target的量
咱们再经过thanos-query来查看全局数据,发现数据是完整的(其中metrics0为指标生成器生成的指标名)
腾讯云容器团队在Kvass的设计思想上进一步优化,构建了高性能支持多集群云原生监控服务,产品目前已正式公测。
这一节咱们就直接使用云原生监控服务来监控一个规模较大的真实集群,测试一下Kvass监控大集群的能力。
咱们关联的集群规模大体以下
咱们直接使用云原生监控服务在关联集群默认添加的采集配置,目前已包含了社区主流的监控指标:
云原生监控所提供的默认Grafana面板也能正常拉取
targets列表也能正常拉取
值得一提的是,云原生监控服务不只支持监控单个大规模集群,还能够用同个实例监控多个集群,并支持采集和告警模板功能,可一键将采集告警模板下发至各地域各个集群,完全告别了每一个集群重复添加配置的问题。
本文从问题分析,设计目标,原理剖析,使用案例等方面详细介绍了一种开源Prometheus集群化技术,可在不修改Prometheus代码的前提下使其支持横向扩缩容,从而监控单机Prometheus没法监控的大规模集群。
【腾讯云原生】云说新品、云研新术、云游新活、云赏资讯,扫码关注同名公众号,及时获取更多干货!!
![]()