TKE团队负责公有云,私有云场景下近万个集群,数百万核节点的运维管理工做。为了监控规模如此庞大的集群联邦,TKE团队在原生Prometheus的基础上进行了大量探索与改进,研发出一套可扩展,高可用且兼容原生配置的Prometheus集群系统,理论上可支持无限的series数目和存储容量,支持纳管TKE集群,EKS集群以及自建K8s集群的监控诉求。node
本文从TKE的架构出发,逐步介绍了整个监控系统的演进过程,包括早期的方案和遇到的问题,社区方案的瓶颈,咱们的改进原理等。web
为了让读者更好理解咱们的场景,咱们首先简单介绍一下TKE的基础架构。算法
TKE团队是公有云界首家采用Kubernetes in Kubernetes进行集群联邦管理的Kubernetes运营团队,其核心思想就是用一个Meta Cluster来托管其余集群的apiserver,controller-manager,scheduler,监控套件等非业务组件,在Meta Cluster中的组件对用户而言是隐藏的,以下图所示。api
上图Meta Cluster中的组件对于用户而言都是隐藏的。支撑环境服务用于直接处理来至TKE控制台的请求。缓存
TKE早期监控方案不支持用户添加业务相关的监控指标,只包括集群运维关注的监控,主要但愿监控的目标以下:架构
在上一节的TKE架构图中,咱们在Meta Cluster中看到每一个集群有一套Cluster-monitor组件,该组件就是单集群级别的监控采集套件。Cluster-monitor包含了以Prometheus为核心的一系列组件,其基本功能就是采集每一个用户集群的基础监控数据,例如Pod负载,Deployment负载,Node CPU使用率等,采集到的数据将直接写到云监控团队提供的Argus系统中存储于告警。核心组件以下图。并发
Barad:云监控提供的多维监控系统,是云上其余服务主要使用的监控系统,其相对成熟稳定,可是不灵活,指标和label都须要提早在系统上设置好。负载均衡
Argus:云监控团队提供的多维业务监控系统,其特色是支持较为灵活的指标上报机制和强大的告警能力。这是TKE团队主要使用的监控系统。运维
数据流:ide
这部分数据将经过控制台输出给用户
成功采集到了属于每一个用户集群的数据,可是,对于一些地域级别的监控,包括
经过单个Cluster-monitor没法采集。须要构建更上一级的地域级别监控。
Region Prometheus不只拉取如meta cluster operator,meta cluster service controller等核心组件的数据外,还经过Prometheus联邦接口拉取Cluster-monitor中的单集群数据进行二次聚合,产生地域级别集群的数据。地域级别数据直接存在本地,不写往Argus,由于这部分数据须要对接Grafana,由团队内部使用。
咱们在单地域监控的基础上又构建了一层全网级别的监控。用于监控
全网数据也是给内部人员查看。
上述介绍的架构虽然解决了咱们对于大规模集群联邦的基本监控诉求,可是依旧存在几点不足。
原生Prometheus并不支持高可用,也不能作横向扩缩容,当集群规模较大时,单一Prometheus会出现性能瓶颈,没法正常采集数据,咱们将在后续章节中给出Prometheus的压测数据。
目前采集周期是1m,咱们但愿能下降到15s。
因为云监控所能提供的Argus系统的聚合能力有限,咱们并无将Cluster-monitor采集到的数据直接输出到Argus,而是将数据按预约的指标进行聚合,只发送聚合过的数据,TKE控制台在数据展现时只作时间上的聚合。而原始数据咱们只保存15分钟。若是加长时间进行本地存储,咱们须要为每一个Cluster-monitor部署云硬盘,因为TKE存在部分空集群(节点个数为0),这会产生资源浪费。
因为每一个集群的数据都是本地落盘,Region Prometheus因为性能有限的缘由,只采集了部分聚合指标,使得没法进行跨集群原始数据的聚合查询,而这类查询对于获取单用户多集群的综合数据是颇有帮助的。
每一级Prometheus都是单独管理的,缺少全局管理工具。
怎样的监控系统,能够同时解决上述几个问题呢?咱们先构思一个理想模型,称之为Kvass。
先看采集,咱们采集侧遇到的问题主要就是性能问题,即咱们但愿Kvass拥有如下能力
存储侧,咱们遇到的问题是存储时长,以及资源利用率,咱们但愿Kvass的存储拥有如下能力
展现侧,咱们遇到的问题是没法获得全局视图,因此,对于理想化的展现,咱们但愿Kvass的展现拥有如下能力
告警侧,咱们但愿能支持原生Prometheus的告警配置。
咱们但愿Kvass没有过于复杂的配置项,且系统拥有一套完整的运维工具,能使用Kubernetes原生方式进行管理。
假设咱们有了这么一个模型,那么咱们的监控就能够变成下面这种架构,在这种模型下,咱们拥有了单个地域下全部咱们要的原始数据。
这一节介绍咱们是如何实现理想模型中的高性能采集器的
首先咱们先了解一下Prometheus的采集原理,为后面修改Prometheus实现高可用分片打下基础。下图展现了Prometheus采集时各模块的关系
咱们已经从Prometheus在实际中的表现知道Prometheus对内存使用会随着采集目标的规模增加而增加,那Prometheus的内存到底用在哪了?
存储模块
抓取模块
分析了Prometheus的采集原理后,咱们能够想肯定如下几个事情
如下表格中的资源个数为Kubenetes官方给出的大规模集群应该包含的资源数 series个数经过统计cadvisor 和kube-state-metrics的指标得出
总计 5118w series。
有大量节点数目高于300的集群,经过前面的压测,单个Prometheus确实存在性能瓶颈。那咱们根据前面的采集原理,尝试修改Prometheus让其支持横向扩缩容。
不管怎么修改,咱们但愿保持如下特性
再来回顾一下上边的采集原理图,看看咱们应该在哪一个地方进行修改。
从上图中,咱们发现,负载产生的源泉是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。
当协调器收集到全部target信息后,须要将target分配给全部Prometheus在分配时,咱们保持如下原则
咱们最终采用了以下算法来分配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) } }
当一个Prometheus上的target抓取任务被分配到另一个Prometheus时,须要增长一种平滑转移机制,确保转移过程当中不掉点。这里咱们容忍重复点,由于咱们将在后面将数据去重。
target交接的实现很是简单,因为各个Prometheus的target更新几乎是同时发生的,因此只须要让第一个Prometheus的发现抓取任务被转移后,延迟2个抓取周期结束任务便可。
协调器会在每一个协调周期计算全部Prometheus的负载,确保平均负载不高于一个阈值,不然就会增长Prometheus个数,在下个协调周期采用上边介绍的targets交接方法将一部分targets分配给它。
考虑到每一个Prometheus都有本地数据,缩容操做并不能直接将多余的Prometheus删除。咱们采用了如下方法进行缩容
在上述介绍的方案中,当某个Prometheus的服务不可用时,协调器会第一时间把target转移到其余Prometheus上继续采集,在协调周期很短(5s)的状况下,出现断点的概率实际上是很是低的。可是若是须要更高的可用性,更好的方法是进行数据冗余,即每一个targets都会被分配给多个Prometheus实例,从而达到高可用的效果。
到目前为止,咱们虽然将Prometheus的采集功能成功分片化,可是,各个Prometheus采集到的数据是分散的,咱们须要一个统一的存储机制,将各个Prometheus采集到的数据进行整合。
在上一节最后,咱们引出,咱们须要一个统一的存储来将分片化的Prometheus数据进行存储。业界在这方面有很多优秀的开源项目,咱们选取了知名度最高的两个项目,从架构,接入方式,社区活跃度,性能等各方面作了调研。
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中。
社区现状
上文从架构角度对两个项目进行了一番对比,可是实际使用中,他两表现如何呢,咱们进行性能压测:
咱们保持两个系统series总量老是拥有相同的变化,从查询性能,系统负载等多方面,去评估他们以前的优劣
稳定性:不一样数据规模下,组件是否正常工做
从数据上看 Thanos 更加稳定一些。
查询性能:不一样数据规模下,查询的效率
从数据上看,Thanos的查询效率更高。
未启用Ruler资源消耗:没有启动Ruler状况下,各组件的负载
就采集和查询而言,Thanos的资源消耗要比Cortex低不少。
在整个压测过程当中,咱们发现Cortex的性能远没有官方宣称的好,固然也多是咱们的调参不合理,可是这也反应出Cortex的使用难度极高,运维十分复杂(上百的参数),总体使用体验很是差。反观Thanos总体表现和官方介绍的较为相近,运维难度也比较低,系统较好把控。
从前面的分析对比来看,Thanos不管是从性能仍是从社区活跃度,仍是从接入方式上看,较Cortex都有比较大的优点。因此咱们选择采用Thanos方案来做为统一存储。
到目前为止,咱们经过实现可分片Prometheus加Thanos,实现了一套与原生Prometheus配置100%兼容的高性能可伸缩的Kvass监控系统。组件关系如图:
上图咱们只画了一套采集端(即多个共享同一份配置文件的Prometheus,以及他们的协调器),实际上系统支持多个采集端,即一个系统可支持多个Kubernetes集群的监控,从而获得多集群全局数据视图。
回顾旧版本监控在运维方法的不足,咱们但愿咱们的新监控系统有用完善的管理工具,且能用Kubernetes的方式进行管理。咱们决定使用operator模式进行管理,Kvass-operator就是整个系统的管理中心,它包含以下三种自定义资源
因为Prometheus配置文件管理比较复杂,CoreOS开源了一个Prometheus-operator项目,用于管理Prometheus及其配置文件,它支持经过定义ServiceMonitor,PodMonitor这两种相比于原生配置文件具备更优可读性的自定义类型,协助用户生成最终的采集配置文件。
咱们但愿实现一种虚拟Prometheus机制,即每一个user cluster可以在本身集群内部管理其所对应的Prometheus采集配置文件,进行ServiceMonitor和PodMonitor的增删改查,也就是说,Prometheus就好像部署在本身集群里面同样。
为了达到这种效果,咱们引入并修改了Prometheus-operator。新版Prometheus-operator会链接上用户集群进行ServiceMonitor和PodMonitor的监听,并将配置文件生成在采集侧。
另外咱们将协调器和Prometheus-operator放在了一块儿。
经过一步一步改进,咱们最终拥有了一套支持多集群采集,并支持扩缩容的高可用监控系统,咱们用其替换原来监控方案中的Cluster-monitor + Region Prometheus。实现了文章之初的诉求。
最第一版本
新方案
咱们上边介绍的方案,已经能够总体替换早期方案中的Region Prometheus及Cluster-monitor。如今咱们再加入一套Thanos,用于将全网数据进行整合。
相比于旧版本监控的指标预约义,新版本监控系统因为Prometheus是可扩缩容的,因此是能够支持用户上报自定义数据的。
Kvass的设计不是天马行空拍脑壳决定的,而是在当前场景下一些问题的解决思路所组成的产物。
虽然咱们整篇文章就是在介绍一种用于取代旧版本监控的新系统,可是这并不意味着咱们以为旧版本监控设计得差劲,只是随着业务的发展,旧版本监控系统所面临的场景相较于设计之初有了较大变化,当时合理的一些决策,在当前场景下变得再也不适用而已。与其说是替换,不如称为为演进。
相比于直接开始系统落地,咱们更倾向于先设计系统模型,以及肯定设计原则,系统模型用于理清咱们究竟是要解决什么问题,咱们的系统应该由哪几个核心模块组件,每一个模块最核心要解决的问题是什么,有了系统模型,就等于有了设计蓝图和思路。
在系统设计过程当中,咱们尤其重视设计的原则,即不管咱们采用什么形式,什么方案,哪些特性是新系统必需要有的,对于Kvass而言,原生兼容是咱们首要的设计原则,咱们但愿不管咱们怎么设计,对用户集群而言,就是个Prometheus。
在总体研发过程当中,咱们也踩了很多坑。Cortex的架构设计相比于thaos而言,采用了索引与数据分离的方式,设计上确实更加合理,理论上对于大规模数据,读取性能会更优,而且Cortex因为原生就支持多租户,实现了大量参数用于限制用户的查询规模,这点是Thanos有待增强的地方。咱们最初的方案也尝试采用Cortex来做为统一存储,可是在实际使用时,发现Cortex存在内存占用高,调参复杂等问题,而Thanos相比而言,性能较为稳定,也更加切近咱们的场景,咱们再结合压测报告,选择将存储切换为Thanos。
因为Kvass系统因此解决的问题具备必定普适性,TKE决定将其做为一个子产品对用户暴露,为用户提供基于Kvass的云原生系统,该产品目前已开放内测。
【腾讯云原生】云说新品、云研新术、云游新活、云赏资讯,扫码关注同名公众号,及时获取更多干货!!