怎么用Prometheus监控十万container的Kubernetes集群

57次阅读
没有评论

共计 10120 个字符,预计需要花费 26 分钟才能阅读完成。

这篇文章主要讲解了“怎么用 Prometheus 监控十万 container 的 Kubernetes 集群”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着丸趣 TV 小编的思路慢慢深入,一起来研究和学习“怎么用 Prometheus 监控十万 container 的 Kubernetes 集群”吧!

Prometheus

Prometheus 依靠其强劲的单机性能,灵活的 PromSQL,活跃的社区生态,逐渐成为云原生时代最核心的监控组件,被全球各大产商用于监控他们的核心业务。

然而,面对大规模监控目标(数千万 series)时,由于原生 Prometheus 只有单机版本,不提供集群化功能,开发人员不得不通过不断增加机器的配置来满足 Prometheus 不断上涨的内存。

单机性能瓶颈

我们对单机 Prometheus 进行的压测,用以探测单个 Prometheus 分片的合理负载,压测的目标有两个。

确定 target 数目对 Prometheus 负载的关系

确定 series 数目和 Prometheus 负载的关系

target 相关性

我们保持总 series 为 100 万不变,通过改变 target 个数,观察 Prometheus 负载变动。压测结果

target 数量 CPU (core)mem (GB)1000.174.65000.194.210000.163.950000.34.6

从表中我们发现 target 数目的改动对 Prometheus 负载的影响并不是强相关的。在 target 数目增长 50 倍的情况下,CPU 消耗有小量增长,但是内存几乎不变。

series 相关性

我们保持 target 数目不变,通过改变总 series 数,观察 Prometheus 的负载变动。

压测结果

series 数量 (万)CPU (core)mem (GB) 查询 1000 series 15m 数据 (s)1000.1913.150.23000.93920.141.65002.02630.571.5

从表中,Prometheus 的负载受到 series 的影响较大,series 越多,资源消耗越大。

当 series 数据超过 300 万时,Prometheus 内存增长较为明显,需要使用较大内存的机器来运行。

压测过程中,我们使用了工具去生成预期数目的 series,工具生成的 series 每个 label 的长度及值的长度都较小,固定为 10 个字符左右。我们的目的是观察相对负载变化,实际生产中由于 label 长度不同,服务发现机制的消耗不同,相同的 series 数目所消耗的负载会比压测中高不少。

现有集群化方案

针对单机 Prometheus 在大规模数据监控时的性能瓶颈问题,社区目前已经存在一些分片化方案,主要包括以下几种。

hash_mod

Prometheus 官方支持通过 Relabel 机制,在配置文件中,对采集上来的数据进行 hash,通过在不同 Prometheus 实例的配置文件中指定不同的 moduleID 来进行分片化,然后通过联邦,Thanos 等方式将数据进行统一汇总,如下图所示,读者也可以直接参考【官方文档】。

配置文件分割

还有一种方法是根据业务进行 job 层面的分割,不同 Prometheus 使用完全独立的采集配置,其中包含了不同的 job,。

上述方案存在的问题

无论是 hash_mod 的方式,还是配置文件分割的方式,其本质都是将数据切分到多个采集配置中,由不同 Prometheus 进行采集。两者都存在以下几个缺点。

** 对预监控数据要有所了解:** 使用上述方法的前提是使用者必须对监控对象会上报的数据有所了解,例如必须知道监控对象会上报某个用于 hash_mod 的 label,或者必须知道不同 job 的整体规模,才能对 job 进行划分。

** 实例负载不均衡:** 虽然上述方案预期都是希望将数据打散到不同 Prometheus 实例上,但实际上通过某些 label 的值进行 hash_mod 的,或者干脆按 job 进行划分的方式并不能保证每个实例最终所采集的 series 数是均衡的,实例依旧存在内存占用过高的风险。

** 配置文件有侵入:** 使用者必须对原配置文件进行改造,加入 Relabel 相关配置,或者将一份配置文件划分成多份,由于配置文件不再单一,新增,修改配置难度大大增加。

** 无法动态扩缩容:** 上述方案中的由于配置是根据实际监控目标的数据规模来特殊制定的,并没有一种统一的扩缩容方案,可以在数据规模增长时增加 Prometheus 个数。当然,用户如果针对自己业务实际情况编写扩缩容的工具确实是可以的,但是这种方式并不能在不同业务间复用。

** 部分 API 不再正常:** 上述方案将数据打散到了不同实例中,然后通过联邦或者 Thanos 进行汇总,得到全局监控数据,但是在不额外处理的情况下会导致部分 Prometheus 原生 API 无法得到正确的值,最典型的是 /api/v1/targets,上述方案下无法得到全局 targets 值。

Kvass 的原理设计目标

针对上述问题,我们希望设计一种无侵入的集群化方案,它对使用者表现出来的,是一个与原生 Prometheus 配置文件一致,API 兼容,可扩缩容的虚拟 Prometheus。具体而言,我们有以下设计目标。

** 无侵入,单配置文件:** 我们希望使用者看到的,修改的都是一份原生的配置文件,不用加任何特殊的配置。

无需感知监控对象:我们希望使用者不再需要预先了解采集对象,不参与集群化的过程。

** 实例负载尽可能均衡:** 我们希望能根据监控目标的实际负载来划分采集任务,让实例尽可能均衡。

** 动态扩缩容:** 我们希望系统能够根据采集对象规模的变化进行动态扩缩容,过程中数据不断点,不缺失。

** 兼容核心 PrometheusAPI:** 我们希望一些较为核心的 API,如上边提到的 /api/v1/target 接口是正常的。

架构

Kvass 由多个组件构成,下图给出了 Kvass 的架构图,我们在架构图中使用了 Thanos,实际上 Kvass 并不强依赖于 Thanos,可以换成其他 TSDB。

Kvass sidecar: 用于接收 Coordinator 下发的采集任务,生成新的配置文件给 Prometheus,也服务维护 target 负载情况。

Kvass coordinator: 该组件是集群的中心控制器,负责服务发现,负载探测,targets 下发等。

Thanos 组件: 图中只使用了 Thanos sidecar 与 Thanos query,用于对分片的数据进行汇总,得到统一的数据视图。

Coordinator

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 维护,我们将在后面章节介绍。

target 分配与扩容

在 Prometheus 单机性能瓶颈那一节,我们介绍过 Prometheus 的内存和 series 相关,确切来说,Prometheus 的内存和其 head series 直接相关。Prometheus 会将最近(默认为 2 小时)采集到的数据的 series 信息缓存在内存中,我们如果能控制好每个分片内存中 head series 的数目,就能有效控制每个分片的内存使用量,而控制 head series 实际就是控制分片当前采集的 target 列表。

基于上边的思路,Kvass coordinaor 会周期性的对每个分片当前采集的 target 列表进行管理:分配新 target,删除无效 target。

在每个周期,Coordinaor 会首先从所有分片获得当前运行状态,其中包括分片当前内存中的 series 数目及当前正在抓取的 target 列表。随后针对从服务发现模块得到的全局 target 信息进行以下处理

如果该 target 已经被某个分片抓取,则继续分配给他,分片的 series 数不变。

如果该 target 没有任何分片抓取,则从负载探测模块获得其 series(如果还未探测完则跳过,下个周期继续),从分片中挑一个目前内存中 series 加上该 target 的 series 后依然比阈值低的,分配给他。

如果当前所有分片没法容纳所有待分配的 targets,则进行扩容,扩容数量与全局 series 总量成正比。

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 并预期有相同的负载。

/api/v1/targets 接口

上文提到 Coordinator 根据配置文件做了服务发现,得到了 target 列表,所以 Coordinator 实际上可以得到 /api/v1/targets 接口所需要的返回结果集合,但是由于 Coordinator 只做了服务发现,并不进行实际采集,所以 target 的采集状态(例如健康状态,上一次采集时间等)都无法直接得知。

当 Coordinator 接收到 /api/v1/targets 请求时,他会基于服务发现得到的 target 集合,结合向 Sidecar(如果 target 已分配)或向探测模块(target 还未分配)询问 target 采集状态,综合后将正确的 /api/v1/targets 结果返回。

Sidecar

上一节介绍了 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,包括

target 的地址,

target 预估的 series 值

target 的 hash 值

处理完 relabel 之后的 label 集合。

Sidecar 根据从 Coordinator 得到的 target 信息,结合原始配置文件,生成一个新的配置文件给 Prometheus 使用,这个新的配置文件做了如下改动。

将所有服务发现机制改为 static_configs 模式,并直接写入 target 列表,每个 target 包含经过 relabel 之后的 label 值

由于现在 target 已经 relabel 过了,所以删除 job 配置中的 relabel_configs 项,但是依旧保留 metrics_rebale_configs

将 target 的 label 中的 scheme 字段全部替换成 http,并将原 schme 以请求参数的形式加入到 label 集合中

将 target 的 job_name 以请求参数的形式加入到 label 集合中 * 注入 proxy_url 将所有抓取请求代理到 Sidecar

我们来看一个例子,假如原来的配置是一个 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

hash:target 的 hash 值,用于 Sidecar 识别是哪个 target 的抓取请求,hash 值由 Coordinator 根据 target 的 label 集合进行计算获得并传递给 Sidecar。

jobName:是哪个 job 下的抓取请求,用于 Sidecar 根据原配置文件中 job 的请求配置(如原 proxy_url,证书等)对抓取目标发起真正的请求。

scheme:这里的 scheme 是 target 通过 relabel 操作之后最终得到的协议值,虽然在 job 配置文件中已经有 scheme 字段,但 Prometheus 配置文件依旧支持通过 relabel 指定某个 target 的请求协议。在上述生成新配置过程中,我们将真实的 scheme 保存到这个参数里,然后将 scheme 全部设置成 http。

有了上述几个参数,Sidecar 就可以对抓取目标发起正确的请求,并得到监控数据,在统计的 target 这次抓取的 series 规模后,Sidecar 会将监控数据拷贝一份给 Prometheus。怎么用 Prometheus 监控十万 container 的 Kubernetes 集群

API 代理

由于 Sidecar 的存在,部分发往 Prometheus 的 API 请求需要被特殊处理,包括

/-/reload:由于 Prometheus 真正使用的配置文件由 Sidecar 生成,针对该接口,需要由 Sidecar 去处理并在处理成功后调用 Prometheus 的 /-/reload 接口。

/api/v1/status/config:该接口需要由 Sidecar 处理并把原配置文件返回。

其他接口直接发往 Prometheus。

全局数据视图

由于我们将采集目标分散到了不同分片中,导致每个分片的数据都只是全局数据的一部分,所以我们需要使用额外的组件来将所有数据进行汇总并去重(多副本的情况下),得到全局数据视图。

以 thanos 为例

thanos 是一个非常好的方案,通过加入 thanos 组件,可以很方便得得到 kvass 集群的全局数据视图。当然我们也可以通过加入 remote writer 配置来使用其他 TSDB 方案,例如 influxdb,M3 等等。怎么用 Prometheus 监控十万 container 的 Kubernetes 集群

使用例子

这一节我们通过一个部署例子,来直观感受一下 Kvass 的效果,相关 yaml 文件可以在这里找到 https://github.com/tkestack/kvass/tree/master/examples 读者可以将项目 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

现在我们部署基于 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

为了得到全局数据,我们需要部署一个 thanos-query

kubectl create -f thanos-query.yaml

查看结果

根据上述计算,监控目标总计 6 个 target, 60270 series,根据我们设置每个分片不能超过 30000 series,则预期需要 3 个分片。

我们发现,Coordinator 成功将 StatefulSet 的副本数改成了 3。怎么用 Prometheus 监控十万 container 的 Kubernetes 集群

我们看下单个分片内存中的 series 数目,发现只有 2 个 target 的量 怎么用 Prometheus 监控十万 container 的 Kubernetes 集群

我们再通过 thanos-query 来查看全局数据,发现数据是完整的(其中 metrics0 为指标生成器生成的指标名)怎么用 Prometheus 监控十万 container 的 Kubernetes 集群

怎么用 Prometheus 监控十万 container 的 Kubernetes 集群

云原生监控

腾讯云容器团队在 Kvass 的设计思想上进一步优化,构建了高性能支持多集群云原生监控服务,产品目前已正式公测。

大集群监控

这一节我们就直接使用云原生监控服务来监控一个规模较大的真实集群,测试一下 Kvass 监控大集群的能力。怎么用 Prometheus 监控十万 container 的 Kubernetes 集群

集群规模

我们关联的集群规模大致如下

1060 个节点

64000+ Pod

96000+ container

采集配置

我们直接使用云原生监控服务在关联集群默认添加的采集配置,目前已包含了社区主流的监控指标:

kube-state-metrics

node-exporer

kubelet

cadvisor

kube-apiserver

kube-scheduler

kube-controler-manager

怎么用 Prometheus 监控十万 container 的 Kubernetes 集群

怎么用 Prometheus 监控十万 container 的 Kubernetes 集群

测试结果

怎么用 Prometheus 监控十万 container 的 Kubernetes 集群

总计 3400+target, 2700+ 万 series

总计扩容了 17 个分片

每个分片 series 稳定在 200w 以下

每个分片消耗内存在 6 -10G 左右

云原生监控所提供的默认 Grafana 面板也能正常拉取 怎么用 Prometheus 监控十万 container 的 Kubernetes 集群

targets 列表也能正常拉取 怎么用 Prometheus 监控十万 container 的 Kubernetes 集群

多集群监控

值得一提的是,云原生监控服务不仅支持监控单个大规模集群,还可以用同个实例监控多个集群,并支持采集和告警模板功能,可一键将采集告警模板下发至各地域各个集群,彻底告别了每个集群重复添加配置的问题。怎么用 Prometheus 监控十万 container 的 Kubernetes 集群

感谢各位的阅读,以上就是“怎么用 Prometheus 监控十万 container 的 Kubernetes 集群”的内容了,经过本文的学习后,相信大家对怎么用 Prometheus 监控十万 container 的 Kubernetes 集群这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是丸趣 TV,丸趣 TV 小编将为大家推送更多相关知识点的文章,欢迎关注!

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-08-16发表,共计10120字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)