引言

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

  1. 规定target负载 = series * 每分钟采集次数。
  2. 将各个Prometheus的target信息进行汇总,失去全局信息,假如为global_targets,并全副标记为未调配。
  3. 计算每个Prometheus实践上均匀应该负责的采集负载,设为avg_load。
  4. 针对每个Prometheus,尝试将其正在采集的target调配给他,前提是该Prometheus负载不超过avg_load,并将胜利调配的target在global_targets中标记为已调配。
  5. 遍历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的云原生零碎,该产品目前已凋谢内测。

【腾讯云原生】云说新品、云研新术、云游新活、云赏资讯,扫码关注同名公众号,及时获取更多干货!!