引言
TKE 团队负责私有云,公有云场景下近万个集群,数百万核节点的运维管理工作。为了监控规模如此宏大的集群联邦,TKE 团队在原生 Prometheus 的根底上进行了大量摸索与改良,研发出一套可扩大,高可用且兼容原生配置的 Prometheus 集群零碎,实践上可反对有限的 series 数目和存储容量,反对纳管 TKE 集群,EKS 集群以及自建 K8s 集群的监控诉求。
本文从 TKE 的架构登程,逐渐介绍了整个监控零碎的演进过程,包含晚期的计划和遇到的问题,社区计划的瓶颈,咱们的改良原理等。
TKE 架构简介
为了让读者更好了解咱们的场景,咱们首先简略介绍一下 TKE 的基础架构。
TKE 团队是私有云界首家采纳 Kubernetes in Kubernetes 进行集群联邦治理的 Kubernetes 经营团队,其核心思想就是用一个 Meta Cluster 来托管其余集群的 apiserver,controller-manager,scheduler,监控套件等非业务组件,在 Meta Cluster 中的组件对用户而言是暗藏的,如下图所示。
上图 Meta Cluster 中的组件对于用户而言都是暗藏的。撑持环境服务用于间接解决来至 TKE 控制台的申请。
- Meta Cluster 用于治理集群的控制面板组件,如 apiserver 等
- Meta Cluster 中还与一些暗藏的性能组件,例如监控组件
- 撑持服务用于接管来至控制台的申请,并连贯到用户集群进行实际操作
晚期的监控计划
需要
TKE 晚期监控计划不反对用户增加业务相干的监控指标,只包含集群运维关注的监控,次要心愿监控的指标如下:
- 每个用户集群的外围组件监控,如 apiserver, scheduler, controller-manager 等
- 每个用户集群的根底资源监控,如 Pod 状态,Deployment 负载,集群总负载等
- Meta Cluster 中所有组件的监控,蕴含 Cluster-monitor 本身,一些 Meta Cluster 本身的 addon 组件等
- 撑持环境组件的监控,如反对 web server 服务解决成功率,内部接口调用成功率等
架构
集群级别
在上一节的 TKE 架构图中,咱们在 Meta Cluster 中看到每个集群有一套 Cluster-monitor 组件,该组件就是单集群级别的监控采集套件。Cluster-monitor 蕴含了以 Prometheus 为外围的一系列组件,其基本功能就是采集每个用户集群的根底监控数据,例如 Pod 负载,Deployment 负载,Node CPU 使用率等,采集到的数据将间接写到云监控团队提供的 Argus 零碎中存储于告警。外围组件如下图。
Barad:云监控提供的多维监控零碎,是云上其余服务次要应用的监控零碎,其绝对成熟稳固,然而不灵便,指标和 label 都须要提前在零碎上设置好。
Argus:云监控团队提供的多维业务监控零碎,其特点是反对较为灵便的指标上报机制和弱小的告警能力。这是 TKE 团队次要应用的监控零碎。
数据流:
- Prometheus 从 kubelet 采集 container 负载信息,从 kube-state-metrics 采集集群元数据,比方 Pod 状态,Node 状态等。数据在 Prometheus 进行聚合,产生 固定的聚合指标,如 container 级别指标,Pod 级别指标。采集到的数据写往两个中央,一部分数据写往 Argus 零碎,这部分数据用于撑持 TKE 管制台上的监控面板及告警,另外一部分数据会写往 Barad 零碎,这是因为更早期间的 TKE 反对在 Barad 控制台配置容器相干的告警,这份数据是为了使旧版告警能持续应用。
- 另外一条数据流是 Barad-importer 组件会从 Barad(云监控)处拉取节点相干的数据,比方 CPU 使用率,内存使用率等,并将数据导入 Argus 零碎,从而使得 Argus 也能进行节点相干的数据展现和告警。这里没有抉择社区支流的 node-exporter 来收集节点数据是因为 node-exporter 须要在用户集群内部署 Daemonset,而咱们心愿整个监控数据采集系统对用户是暗藏的。
这部分数据将通过控制台输入给用户
地区级别
胜利采集到了属于每个用户集群的数据,然而,对于一些地区级别的监控,包含
- Meta Cluster 中的治理组件
- Cluster-monitor 组件本身
- 整个地区级别的汇合信息,如总集群数,集群均匀节点数,均匀创立工夫等数据
通过单个 Cluster-monitor 无奈采集。须要构建更上一级的地区级别监控。
Region Prometheus 不仅拉取如 meta cluster operator,meta cluster service controller 等外围组件的数据外,还通过 Prometheus 联邦接口拉取 Cluster-monitor 中的单集群数据进行二次聚合,产生地区级别集群的数据。地区级别数据间接存在本地,不写往 Argus,因为这部分数据须要对接 Grafana,由团队外部应用。
全网级别
咱们在单地区监控的根底上又构建了一层全网级别的监控。用于监控
- 撑持环境组件监控
- 所有地区的 Region Prometheus 数据再聚合失去全网级别指标
全网数据也是给内部人员查看。
架构总览
逐步暴露出的问题
上述介绍的架构尽管解决了咱们对于大规模集群联邦的根本监控诉求,然而仍旧存在几点有余。
Prometheus 性能有余
原生 Prometheus 并不反对高可用,也不能做横向扩缩容,当集群规模较大时,繁多 Prometheus 会呈现性能瓶颈,无奈失常采集数据,咱们将在后续章节中给出 Prometheus 的压测数据。
采集周期过长
目前采集周期是 1m,咱们心愿能升高到 15s。
原始数据存储时长过短
因为云监控所能提供的 Argus 零碎的聚合能力无限,咱们并没有将 Cluster-monitor 采集到的数据间接输入到 Argus, 而是将数据按预约的指标进行聚合,只发送聚合过的数据,TKE 控制台在数据展现时只做工夫上的聚合。而原始数据咱们只保留 15 分钟。如果加长工夫进行本地存储,咱们须要为每个 Cluster-monitor 部署云硬盘,因为 TKE 存在局部空集群(节点个数为 0),这会产生资源节约。
不反对跨集群查问
因为每个集群的数据都是本地落盘,Region Prometheus 因为性能无限的起因,只采集了局部聚合指标,使得无奈进行跨集群原始数据的聚合查问,而这类查问对于获取单用户多集群的综合数据是很有帮忙的。
运维难度大
每一级 Prometheus 都是独自治理的,不足全局管理工具。
设计现实模型
怎么的监控零碎,能够同时解决上述几个问题呢?咱们先构思一个现实模型,称之为Kvass。
采集【高性能】
先看采集,咱们采集侧遇到的问题次要就是性能问题,即咱们心愿 Kvass 领有以下能力
- 高性能:有有限性能的采集器。
- 原生:反对原生 Prometheus 支流的配置形式,包含 Prometheus operator 所反对的 ServiceMonitor,PodMonitor 等。
存储【长期存储】
存储侧,咱们遇到的问题是存储时长,以及资源利用率,咱们心愿 Kvass 的存储领有以下能力
- 时长可能达到 1 年
- 存储资源利用率高
展现【全局视图】
展现侧,咱们遇到的问题是无奈失去全局视图,所以,对于理想化的展现,咱们心愿 Kvass 的展现领有以下能力
- 能对接 Grafana
- 能够跨集群聚合查问
- 反对原生 Prometheus 语句
告警【原生】
告警侧,咱们心愿能反对原生 Prometheus 的告警配置。
运维【便捷】
咱们心愿 Kvass 没有过于简单的配置项,且零碎领有一套残缺的运维工具,能应用 Kubernetes 原生形式进行治理。
整体模型
假如咱们有了这么一个模型,那么咱们的监控就能够变成上面这种架构,在这种模型下,咱们领有了单个地区下所有咱们要的原始数据。
- 去掉了 Cluster-monitor 中的 Prometheus
- 去掉了 Region Prometheus
高性能采集
这一节介绍咱们是如何实现理想模型中的高性能采集器的
Prometheus 采集原理
各模块的关系
首先咱们先理解一下 Prometheus 的采集原理,为前面批改 Prometheus 实现高可用分片打下基础。下图展现了 Prometheus 采集时各模块的关系
- 配置管理模块:该模块负责接管配置更新动作,所有依赖配置文件的模块,在初始化的时候都会向配置管理模块注册配置更新监听函数。
- 服务发现模块:当 job 配置了服务发现时,target 的个数是动态变化的,该模块负责做服务发现并生成 target 的变动信息,并告诉抓取模块。
- 存储模块:该模块有两局部组成,一个是本地 TSDB 模块,一个是近程存储模块,该模块负责将 target 采集到的数据进行本地存储,同时也治理近程存储的发送过程。
- 抓取模块:该模块是抓取的外围模块,其负责依据配置文件以及服务发现模块给出的 target 信息,生成多个 job 对象,每个 job 对象蕴含多个 target scaper 对象,每个 target scraper 对象都会启动一个协程,周期性地对指标进行指标抓取,并发送到存储模块。
内存占用
咱们曾经从 Prometheus 在理论中的体现晓得 Prometheus 对内存应用会随着采集指标的规模增长而增长,那 Prometheus 的内存到底用在哪了?
存储模块
- Prometheus 的存储不是将每个采集到的点都间接落盘,而是会先写入 wal 文件,采集一段时间后,将 wal 压缩成块。在这期间,存储模块须要缓存所有 series 的 label 信息,并且在压缩的时候,也须要产生较大的长期内存耗费。
- 近程存储的原理是通过监听 wal 文件的变动,将 wal 文件中的点逐渐发送到远端,在一个 wal 文件被齐全发送完之前,近程存储管理器也会缓存所有发现的 series 的 label 信息,并且保护多个发送队列,这也是内存耗费比拟大的中央。
抓取模块
- 对于每个 target, 每个 series 只有第一次被存储的时候才会把 series 的 label 信息传给存储模块,存储模块会返回一个 id,target scraper 就会将 series 进行 hash 并与 id 对应,后续抓取时,本 series 只需将 id 和值通知存储模块即可。hash 与 id 的对应表也比拟占内存。
Prometheus 性能压测
压测目标
剖析了 Prometheus 的采集原理后,咱们能够想确定以下几个事件
- target 数目对 Prometheus 负载的关系
- series 规模和 Prometheus 负载的关系
target 相关性
压测办法
压测数据
压测论断
- target 个数对 Prometheus 的整体负载影响不大
series 规模压测
压测办法
压测数据
官网大规模集群各个资源产生的 series
以下表格中的资源个数为 Kubenetes 官网给出的大规模集群应该蕴含的资源数 series 个数通过统计 cadvisor 和 kube-state-metrics 的指标得出
总计 5118w series。
压测论断
- 当 series 数目高于 300w 时,Prometheus 内存将暴增
- 按等比例换算,单 Prometheus 采集 300 节点以上的集群时会内存会呈现较大涨幅
实现可分片高可用 Prometheus
有大量节点数目高于 300 的集群,通过后面的压测,单个 Prometheus 的确存在性能瓶颈。那咱们依据后面的采集原理,尝试批改 Prometheus 让其反对横向扩缩容。
设计准则
无论怎么批改,咱们心愿放弃以下个性
- 扩缩容时一直点
- 负载平衡
- 100% 兼容原来的配置文件及采集能力
外围原理
再来回顾一下上边的采集原理图,看看咱们应该在哪个中央进行批改。
从上图中,咱们发现,负载产生的源泉是 target scraper,如果缩小 target scraper 个数,就能缩小整体采集到的 series,从而升高负载。
假如咱们有多个 Prometheus 共享雷同的配置文件,那么实践上他们产生进去的 target scraper 该当是截然不同的。如果多个 Prometheus 之间可能互相协调,依据每个 target scraper 抓取的指标数据量状况,调配这些 target scraper,就是实现负载的均摊。如下图所示。
实现动静打散
- 为了实现上述计划,咱们须要一个独立于所有 Prometheus 的负载协调器,协调器周期性(15s) 进行负载计算,该协调器负责收集所有 target scraper 的信息,以及所有 Prometheus 的信息,随后通过调配算法,为每个 Prometheus 调配一些 target scraper,最初将后果同步给所有 Prometheus。
- 相应的,每个 Prometheus 须要增加一个本地协调模块,该模块负责和独立的协调器进行对接,上报本 Prometheus 通过服务发现发现的所有 target,以及上一次采集获知的 target 的数据量,另外该模块也承受协调器下发的采集工作信息,用于管制本 Prometheus 应该开启哪些 target scraper。
targets 调配算法
当协调器收集到所有 target 信息后,须要将 target 调配给所有 Prometheus 在调配时,咱们放弃以下准则
- 优先调配到正在采集该 target 的 Prometheus
- 负载尽可能平衡
咱们最终采纳了如下算法来调配 target
- 规定 target 负载 = series * 每分钟采集次数。
- 将各个 Prometheus 的 target 信息进行汇总,失去全局信息,假如为 global_targets,并全副标记为未调配。
- 计算每个 Prometheus 实践上均匀应该负责的采集负载,设为 avg_load。
- 针对每个 Prometheus,尝试将其正在采集的 target 调配给他,前提是该 Prometheus 负载不超过 avg_load,并将胜利调配的 target 在 global_targets 中标记为已调配。
- 遍历 global_targets,针对步骤 3 剩下的 target,有以下几种状况
4.1 如果之前没有采集过,则随机调配个一个 Prometheus。
4.2 如果原来采集的 Prometheus 负载未超过 avg_load, 则调配给他。
4.3 找到所有 Prometheus 中负载最低的实例,如果该实例目前的负载总和加上以后 target 的负载仍旧小于 avg_load,则调配他给,否则调配给原来的采集的 Prometheus。
咱们还能够用伪代码来示意这个算法:
func load(t target) int {return t.series * (60 / t.scrape_interval)
}
func reBalance(){
global_targets := 所有 Prometheus 的 targets 信息汇总
avg_load = avg(global_targets)
for 每个 Prometheus {
p := 以后 Prometheus
for 正在采集的 target{
t := 以后 target
if p.Load <= avg_load {p.addTarget(t)
global_targets[t] = 已调配
p.Load += load(t)
}
}
}
for global_targets{
t := 以后 target
if t 已调配{continue}
p := 正在采集 t 的 Prometheus
if p 不存在 {p = 随机 Prometheus}else{
if p.Load > avg_load {
exp := 负载最轻的 Prometheus
if exp.Load + load(t) <= avg_load{p = exp}
}
}
p.addTarget(t)
p.Load += load(t)
}
}
targets 交接
当一个 Prometheus 上的 target 抓取工作被调配到另外一个 Prometheus 时,须要减少一种平滑转移机制,确保转移过程中不掉点。这里咱们容忍反复点,因为咱们将在前面将数据去重。
target 交接的实现非常简单,因为各个 Prometheus 的 target 更新简直是同时产生的,所以只须要让第一个 Prometheus 的发现抓取工作被转移后,提早 2 个抓取周期结束任务即可。
扩容
协调器会在每个协调周期计算所有 Prometheus 的负载,确保均匀负载不高于一个阈值,否则就会减少 Prometheus 个数,在下个协调周期采纳上边介绍的 targets 交接办法将一部分 targets 调配给它。
缩容
思考到每个 Prometheus 都有本地数据,缩容操作并不能间接将多余的 Prometheus 删除。咱们采纳了以下办法进行缩容
- 将多余的 Prometheus 标记为闲置,并记录以后工夫。
- 闲置的 Prometheus 上的 target 会全副被转移,并且不再参加后续任务分配。
- 当闲置 Prometheus 所有数据已上报远端(后续将介绍),将实例删除。
- 特地的,如果在闲置过程中,呈现了扩容操作,则将闲置最久的实例从新勾销闲置,持续参加工作。
高可用
在上述介绍的计划中,当某个 Prometheus 的服务不可用时,协调器会第一工夫把 target 转移到其余 Prometheus 上持续采集,在协调周期很短(5s)的状况下,呈现断点的几率其实是非常低的。然而如果须要更高的可用性,更好的办法是进行数据冗余,即每个 targets 都会被调配给多个 Prometheus 实例,从而达到高可用的成果。
对于存储的问题
到目前为止,咱们尽管将 Prometheus 的采集性能胜利分片化,然而,各个 Prometheus 采集到的数据是扩散的,咱们须要一个对立的存储机制,将各个 Prometheus 采集到的数据进行整合。
对立存储
在上一节最初,咱们引出,咱们须要一个对立的存储来将分片化的 Prometheus 数据进行存储。业界在这方面有不少优良的开源我的项目,咱们选取了知名度最高的两个我的项目,从架构,接入形式,社区活跃度,性能等各方面做了调研。
Thanos vs Cortex
整体比拟
Thanos 简介
Thanos 是社区非常风行的 Prometheus 高可用解决方案,其设计如图所示
从采集侧看,Thanos, 利用 Prometheus 边上的 Thanos sidecar,将 Prometheus 落在本地的数据盘上传至对象存储中进行近程存储,这里的 Prometheus 能够有多个,各自上报各自的数据。
查问时,优先从各 Prometheus 处查问数据,如果没查到,则从对象存储中查问历史数据,Thanos 会将查问到的数据进行去重。Thanos 的设计非常合乎咱们后面的采集计划提到的对立存储。接入后如图所示。
Cortex 简介
Cortex 是 Weavework 公司开源的 Prometheus 兼容的 TSDB,其原生反对多租户,且官网宣传其具备十分弱小的性能,能存储高达 2500 万级别的 series,其架构如图所示
从架构图不难发现,Cortex 比 Thanos 要简单得多,内部依赖也多,预计整体运维难度的比拟大。Cortex 不再应用 Prometheus 自带的存储,而是让 Prometheus 通过 remote write 将数据全副写到 Cortex 零碎进行对立的存储。Cortex 通过可分片接收器来接收数据,随后将数据块存储到对象存储中,而将数据索引存储到 Memcache 中。
- 从架构上来看,Cortex 仿佛更加简单,运维难度也高
- 从接入形式看,Thanos 对原来的 Prometheus 配置文件没有改变,属于无侵入形式,而 Cortex 须要在配置文件中退出 remote write, 另外目前版本的 Prometheus 无奈通过参数敞开本地存储,所以即便只应用 remote write 存储到 Cortex, Prometheus 本地还是会有数据。
社区现状
- 从社区活跃度上看,Thanos 体现更加优良
性能压测
上文从架构角度对两个我的项目进行了一番比照,然而理论应用中,他两体现如何呢,咱们进行性能压测:
压测形式
咱们放弃两个零碎 series 总量总是领有雷同的变动,从查问性能,零碎负载等多方面,去评估他们之前的优劣
压测后果
- 稳定性:不同数据规模下,组件是否失常工作
从数据上看 Thanos 更加稳固一些。
- 查问性能:不同数据规模下,查问的效率
从数据上看,Thanos 的查问效率更高。
- 未启用 Ruler 资源耗费:没有启动 Ruler 状况下,各组件的负载
就采集和查问而言,Thanos 的资源耗费要比 Cortex 低很多。
在整个压测过程中,咱们发现 Cortex 的性能远没有官网声称的好,当然也可能是咱们的调参不合理,然而这也反馈出 Cortex 的应用难度极高,运维十分复杂(上百的参数),整体应用体验十分差。反观 Thanos 整体体现和官网介绍的较为相近,运维难度也比拟低,零碎较好把控。
选型
从后面的剖析比照来看,Thanos 无论是从性能还是从社区活跃度,还是从接入形式上看,较 Cortex 都有比拟大的劣势。所以咱们抉择采纳 Thanos 计划来作为对立存储。
Kvass 零碎整体实现
到目前为止,咱们通过实现可分片 Prometheus 加 Thanos,实现了一套与原生 Prometheus 配置 100% 兼容的高性能可伸缩的 Kvass 监控零碎。组件关系如图:
接入多个 k8s 集群
上图咱们只画了一套采集端(即多个共享同一份配置文件的 Prometheus,以及他们的协调器),实际上零碎反对多个采集端,即一个零碎可反对多个 Kubernetes 集群的监控,从而失去多集群全局数据视图。
Kvass-operator
回顾旧版本监控在运维办法的有余,咱们心愿咱们的新监控零碎有用欠缺的管理工具,且能用 Kubernetes 的形式进行治理。咱们决定应用 operator 模式进行治理,Kvass-operator 就是整个零碎的管理中心,它蕴含如下三种自定义资源
- Thanos:定义了 Thanos 相干组件的配置及状态,全局惟一。
- Prometheus: 每个 Prometheus 定义了一个 Prometheus 集群的配置,例如其关联的 Kubernetes 集群根底信息,协调算法的一些阈值等
- Notification: 定义了告警渠道,Kvass-operator 负责依据其定义去更新云上告警配置
Prometheus-operator 及集群内采集配置管理
因为 Prometheus 配置文件治理比较复杂,CoreOS 开源了一个 Prometheus-operator 我的项目,用于治理 Prometheus 及其配置文件,它反对通过定义 ServiceMonitor,PodMonitor 这两种相比于原生配置文件具备更优可读性的自定义类型,帮助用户生成最终的采集配置文件。
咱们心愿实现一种虚构 Prometheus 机制,即每个 user cluster 可能在本人集群外部治理其所对应的 Prometheus 采集配置文件,进行 ServiceMonitor 和 PodMonitor 的增删改查,也就是说,Prometheus 就如同部署在本人集群外面一样。
为了达到这种成果,咱们引入并批改了 Prometheus-operator。新版 Prometheus-operator 会连贯上用户集群进行 ServiceMonitor 和 PodMonitor 的监听,并将配置文件生成在采集侧。
另外咱们将协调器和 Prometheus-operator 放在了一起。
基于 Kvass 的 TKE 监控计划
通过一步一步改良,咱们最终领有了一套反对多集群采集,并反对扩缩容的高可用监控零碎,咱们用其替换原来监控计划中的 Cluster-monitor + Region Prometheus。实现了文章之初的诉求。
最后版本
新计划
咱们上边介绍的计划,曾经能够整体替换晚期计划中的 Region Prometheus 及 Cluster-monitor。当初咱们再退出一套 Thanos, 用于将全网数据进行整合。
相比于旧版本监控的指标预约义,新版本监控零碎因为 Prometheus 是可扩缩容的,所以是能够反对用户上报自定义数据的。
总结
我的项目思路
Kvass 的设计不是天马行空拍脑袋决定的,而是在以后场景下一些问题的解决思路所组成的产物。
主观对待旧版本
尽管咱们整篇文章就是在介绍一种用于取代旧版本监控的新零碎,然而这并不意味着咱们感觉旧版本监控设计得差劲,只是随着业务的倒退,旧版本监控零碎所面临的场景相较于设计之初有了较大变动,过后正当的一些决策,在以后场景下变得不再实用而已。与其说是替换,不如称为为演进。
先设计模型
相比于间接开始零碎落地,咱们更偏向于先设计零碎模型,以及确定设计准则,零碎模型用于理清咱们到底是要解决什么问题,咱们的零碎应该由哪几个外围模块组件,每个模块最外围要解决的问题是什么,有了零碎模型,就等于有了设计蓝图和思路。
确定设计准则
在零碎设计过程中,咱们尤为器重设计的准则,即无论咱们采纳什么模式,什么计划,哪些个性是新零碎必须要有的,对于 Kvass 而言,原生兼容是咱们首要的设计准则,咱们心愿无论咱们怎么设计,对用户集群而言,就是个 Prometheus。
落地
在整体研发过程中,咱们也踩了不少坑。Cortex 的架构设计相比于 thaos 而言,采纳了索引与数据拆散的形式,设计上的确更加正当,实践上对于大规模数据,读取性能会更优,并且 Cortex 因为原生就反对多租户,实现了大量参数用于限度用户的查问规模,这点是 Thanos 有待增强的中央。咱们最后的计划也尝试采纳 Cortex 来作为对立存储,然而在理论应用时,发现 Cortex 存在内存占用高,调参简单等问题,而 Thanos 相比而言,性能较为稳固,也更加切近咱们的场景,咱们再联合压测报告,抉择将存储切换为 Thanos。
产品化
因为 Kvass 零碎所以解决的问题具备肯定普适性,TKE 决定将其作为一个子产品对用户裸露,为用户提供基于 Kvass 的云原生零碎,该产品目前已凋谢内测。
【腾讯云原生】云说新品、云研新术、云游新活、云赏资讯,扫码关注同名公众号,及时获取更多干货!!