本文介绍了高可用、持久存储、可动态调整的Kubernetes监控方案的实现过程。
上篇文章回顾: 记一次kubernetes集群异常:kubelet链接apiserver超时
小米的弹性调度平台(Ocean)以及容器平台主要基于开源容器自动化管理平台kubernetes(简称k8s)来提供服务,完善的监控系统提升容器服务的质量的前提。不一样于传统物理主机,每一个容器至关于一个主机,致使一台物理主机上的系统指标数量成本增加,总的监控指标规模至关庞大(经线上统计,每node指标达到10000+)。此外,为了不重复造轮,须要最大限度的利用公司的监控报警系统,须要把k8s的监控和报警融入其中。在小米现有的基础设施之上,落地该监控,是一个不小的挑战。node
为了更方便的管理容器,k8s对container进行了封装,拥有了Pod、Deployment、Namespace、Service等众多概念。与传统集群相比,k8s集群监控更加复杂:算法
(1)监控维度更多,除了传统物理集群的监控,还包括核心服务监控(apiserver, etcd等)、容器监控、Pod监控、Namespace监控等。数据库
(2)监控对象动态可变,在集群中容器的销毁建立十分频繁,没法提早预置。json
(3)监控指标随着容器规模爆炸式增加,如何处理及展现大量监控数据。后端
(4)随着集群动态增加,监控系统必须具有动态扩缩的能力。api
除了k8s集群监控自己的特性外,具体监控方案的实现要考虑公司内部的实际状况:性能优化
(1)目前弹性调度计算平台提供的k8s集群包括:融合云容器集群、部分Ocean集群以及CloudML集群,拥有十余个集群,1000+机器。不一样k8s集群的部署方式,网络模式,存储方式等不尽相同,监控方案须要兼顾各类差别。bash
(2)Open-Falcon是公司内通用的监控报警系统,有完善的数据收集,展现和报警机制,可是Open-Falcon并不支持k8s这种拉的采集方案。此外,k8s里的各类资源,有自然的层次关系,这就决定了监控数据的整合须要强大而灵活的聚合能力,Falcon在这些方面不太能知足需求。但咱们并不想重复造轮子,须要最大限度利用公司既有基础设施,从而节约开发和运维成本。网络
(3)对于监控的持久化存储,如何结合公司内的数据库,实现监控数据的长期存储,都是须要考虑的问题。架构
现有业界针对k8s监控也有一些成熟的方案:
(1)Heapster/Metrics-Server+ InfluxDB + Grafana
Heapster是k8s原生的集群监控方案(现已废弃,转向metrics-server),从节点上的 cadvisor获取计算、存储、网络等监控数据,而后将这些数据输出到外部存储(backend),如InfluxDB,最后再经过相应的UI界面进行可视化展现,如grafana。此方案部署简单,但采集数据单一,不合适k8s集群总体监控,只适用于监控集群中各容器的资源信息,如做为k8s dashboard的数据展现源。
(2)Exporter+Prometheus+Adapter
Prometheus 是一套开源的系统监控报警框架,具备多维数据模型、灵活强大的查询语句、性能良好等特色。Prometheus能够经过各类exporter,如node-exporter、kube-state-metrics、cadivsor等采集监控metrics监控数据,此外Prometheus能够动态发现k8s集群中的pod,node等对象。经过Prometheus采集各个维度的数据,进行聚合并提供报警,而后利用adapter能够将数据写到远程储存中(如OpenTSDB,InfluxDB )等实现持久化存储。但因为数据采集可能会有丢失,因此 Prometheus 不适用于对采集数据要 100% 准确的情形,例如实时监控等。
前期,为了尽快实现k8s的落地,监控系统借助Falcon还有内部开发的exporter,仅实现了对于核心监控数据的采集,如Pod的cpu,内存,网络等资源使用状况,具体架构以下图所示。
经过实现cadvisor-exporter采集cadvisor的容器监控数据;kube-state-exporter采集k8s关键Pod指标;Falcon-agent采集物理节点数据。初始方案仅采集了核心监控数据,初步实现对核心资源使用状况的监控,缺少更全面的数据监控,例如apiserver,etcd等。因为Falcon目前不支持对于容器的监控,因此须要手动实现各类exporter来知足k8s的监控需求。并且监控数据没有实现持久化存储,不支持长期查询。
因为初始监控系统的不足,通过调研对比最终选用Prometheus做为k8s的监控方案,主要考虑一下几点缘由:
(1)原生支持k8s监控,具备k8s对象服务发现能力,并且k8s的核心组件提供了Prometheus的采集接口
(2)强大的性能,单个Prometheus能够每秒抓取10万的metrics,能够知足必定规模下k8s集群的监控需求
(3)良好的查询能力:Prometheus 提供有数据查询语言 PromQL。PromQL 提供了大量的数据计算函数,大部分状况下用户均可以直接经过 PromQL 从 Prometheus 里查询到须要的聚合数据。
基于Prometheus的k8s监控系统的架构以下图所示:
数据源:node-exporter采集物理节点指标;kube-state-metrics采集k8s相关指标,包括资源使用状况,以及各类对象的状态信息;cadvisor采集容器相关指标;apiserver, etcd, scheduler, k8s-lvm,gpu等核心组件的监控数据;其余自定义metrics,经过在pod yaml文件annotations添加 prometheus.io/scrape: "true" 可实现自动抓取提供的metrics。
Prometheus数据处理模块:Prometheus以Pod方式部署在k8s上,Pod中含有Prometheus、Prom-Reloader。Prometheus负责采集聚合数据;prom-config为监控的聚合规则与抓取配置,以ConfigMap存储;Prom-Reloader实现监控配置的热更新,实时监控配置文件,无需重启应用便可动态加载最新配置。
存储后端:Falcon与OpenTSDB。
Open-Falcon是公司统一的监控报警系统,提供了完善的数据采集、报警、展现、历史数据存储功能以及权限功能。因为Falcon设计较早,没有对于容器相关指标提供监控,而prometheus原生支持了k8s,可是其报警功能只能静态配置且须要实现与公司相关帐号打通以方便用户配置监控,且有些k8s的指标,须要暴露给容器用户。基于此考虑,咱们使用Falcon做为k8s监控的报警和对外展现平台。经过实现Falcon-Adapter,将监控数据转发到Falcon以实现报警与展现。根据k8s服务对象将监控目标分为多个层次:cluster, node, namespace, deployment, pod,将关键报警指标经过Falcon-Agent打到Falcon,用户可自行在配置报警查看指标。
原生Prometheus的监控数据放在本地(使用tsdb时区数据库),默认保存15天数据。监控数据不止用于监控与报警,后续的运营分析和精细化运维都须要以这些运营数据做为基础,所以须要数据的持久化。在Prometheus社区中也提供了部分读写方案,如Influxdb、Graphite、OpenTSDB等。而小米正好有OpenTSDB团队,OpenTSDB将时序数据存储在HBase中,咱们公司的HBase也有稳定的团队支持。基于此经过OpenTSDB为监控数据提供远程存储。实现了OpenTSDB-Adapter,将监控数据转发到时序数据库OpenTSDB以实现数据的持久存储,知足长期查询以及后期数据分析的须要。
系统监控的核心系统所有经过Deployment/Daemonset形式部署在k8s集群中,以保证监控服务的可靠性。所有配置文件使用ConfigMap存储并实现了自动更新。
存储方式
Prometheus的存储包括本地存储与远程存储,本地存储只保存短时间内的监控数据,按照两个小时为一个时间窗口,将两小时内产生的数据存储在一个块(Block)中,每个块中包含该时间窗口内的全部样本数据(chunks),元数据文件(meta.json)以及索引文件(index)。因为各集群提供存储类型的不行,目前已经实现多种存储方式的部署包括pvc、lvm、本地磁盘等。
远程存储经过实现prometheus的远程读写接口实现对OpenTSDB的操做,方便对于长期数据的查询。
为了保持Prometheus的简单性,Prometheus并无尝试在自身中解决以上问题,而是经过定义两个标准接口(remote_write/remote_read),让用户能够基于这两个接口对接将数据保存到任意第三方的存储服务中,这种方式在Promthues中称为Remote Storage。
如上图所示,能够在Prometheus配置文件中指定Remote Write(远程写)的URL地址,一旦设置了该配置项,Prometheus将采集到的样本数据经过HTTP的形式发送给适配器(Adapter)。而用户则能够在适配器中对接外部任意的服务。外部服务能够是真正的存储系统,公有云的存储服务,也能够是消息队列等任意形式。一样地,Promthues的Remote Read(远程读)也经过了一个适配器实现。在远程读的流程当中,当用户发起查询请求后,Promthues将向remote_read中配置的URL发起查询请求(matchers,time ranges),Adapter根据请求条件从第三方存储服务中获取响应的数据。同时将数据转换为Promthues的原始样本数据返回给Prometheus Server。当获取到样本数据后,Promthues在本地使用PromQL对样本数据进行二次处理。启用远程读设置后,只在数据查询时有效,对于规则文件的处理,以及Metadata API的处理都只基于Prometheus本地存储完成。
远程存储现已支持公司内部的Falcon与OpenTSDB,经过Falcon方便用户查看监控数据以及配置报警。写到OpenTSDB已实现持久化存储,而且支持经过Prometheus对其进行远程读写。
目前基于Prometheus的监控方案已在各集群部署,但随着集群规模的增加逐渐暴露出一些问题。
其一,是随着容器增加监控指标激增,对Falcon-agent与transfer形成必定压力,导致常常形成Falcon-agent拥堵以及部分监控数据延迟、丢失等问题。在线上测试当经过单个Falcon-agent发送超过150000/m时,常常性出现数据丢失,现已关闭部分监控数据的发送。根本缘由是prometheuse集中的数据聚合和推送,把分散在各集群的指标汇聚到了一台主机,从而带来了超常的压力。
其二,是在规模较大的集群,Prometheus占用CPU与内存资源都较多(下表中为线上集群Prometheus的运行状况),偶尔会出现某些metrics抓取不到的状况,随着集群规模的扩大单个Prometheus将会遇到性能瓶颈。
针对单个Prometheus监控方案的不足,须要对其进行扩展已知足大规模k8s集群监控,并适配Falcon系统agent的性能。经过调研,发现Prometheus支持集群联邦。这种分区的方式增长了Prometheus自身的可扩展性,同时,也能够分散对单个Falcon agent的压力。
联邦功能是一个特殊的查询接口,容许一个prometheus抓取另外一个prometheus的metrics,已实现分区的目的。以下所示:
常见分区两种方式:
其一是功能分区,联邦集群的特性能够帮助用户根据不一样的监控规模对Promthues部署架构进行调整,能够在各个数据中心中部署多个Prometheus Server实例。每个Prometheus Server实例只负责采集当前数据中心中的一部分任务(Job),例如能够将不一样的监控任务分配到不一样的Prometheus实例当中,再由中心Prometheus实例进行聚合。
其二是水平扩展,极端状况下,单个采集任务的Target数也变得很是巨大。这时简单经过联邦集群进行功能分区,Prometheus Server也没法有效处理时。这种状况只能考虑继续在实例级别进行功能划分。将同一任务的不一样实例的监控数据采集任务划分到不一样的Prometheus实例。经过relabel设置,咱们能够确保当前Prometheus Server只收集当前采集任务的一部分实例的监控指标。
针对k8s的实际状况,分区方案架构以下:
Prometheus分区包括master Prometheus 与 slave Prometheus以及 kube state Prometheus:因为大量指标的采集来源于node上的服务,如kubelet, node-exporter, cadvisor等是以node为单位采集的,因此按照node节点来划分不一样job,slave Prometheus 按照node切片采集node,pod级别数据;
kube-state-metrics暂时没法切片,单独做为一个kube-state Prometheus,供master Prometheus采集;其余etcd, apiserver,自定义指标等可经过master Prometheus直接采集。
Prometheus master对于其余Prometheus slave的抓取可经过以下配置:
- job_name: federate-slave honor_labels: true metrics_path: '/federate' params: 'match[]': - '{__name__=~"pod:.*|node:.*"}' kubernetes_sd_configs: - role: pod namespaces: names: - kube-system relabel_configs: - source_labels: - __meta_kubernetes_pod_label_app action: keep regex: prometheus-slave.*复制代码
Prometheus slave的对于抓取任务的分区经过Prometheus提供的hashmod方法来实现:
- job_name: kubelet scheme: https kubernetes_sd_configs: - role: node namespaces: names: [] tls_config: insecure_skip_verify: true relabel_configs: - source_labels: [] regex: __meta_kubernetes_node_label_(.+) replacement: "$1" action: labelmap - source_labels: [__meta_kubernetes_node_label_kubernetes_io_hostname] modulus: ${modulus} target_label: __tmp_hash action: hashmod - source_labels: [__tmp_hash] regex: ${slaveId} action: keep复制代码
部署方式
master Prometheus 与 kube-state Prometheus经过deployment部署。slave Prometheus可有多个pod,但因为每一个pod的配置不一样(配置中的${slaveId}不一样),每一个slave prometheus须要在配置中体现分区编号,而原生的deployment/statefulset/daemonset都不支持同一Pod模板挂载不一样的ConfigMap配置。为了方便管理Slave Prometheus经过statefulset来部署slave,因为statefulset会将每一个pod按顺利编号如slave-0,slave-1等。经过Prom-Reloader得到到Pod名称,持续监听Prometheus配置变化,而后生成带有编号的配置以区分不一样的分区模块。
测试验证
测试包括两方面,一是针对分区后的监控方案进行功能测试是否符合预期,二是对于其性能进行测试
在功能测试中,验证分区方案的聚合规则正常,特别对于分区先后的数据进行校验,经过对一周内的数据进行对比,取一小时内平均的差值比率,以下图:
经统计,超过95%的时间序列对比偏差在1%之内,个别指标瞬时波动较大(如网络使用率),但随着时间增长会抵消差别。
在性能测试中,针对不一样分区监控不一样负载下进行测试,验证其性能情况。
在测试集群上建立1000个虚拟node,建立不一样数量pod测试Prometheus分区性能:
对于Prometheus master与Prometheus kube-state在1分钟抓取时间内最多可支持8w pod,主要瓶颈在于kube-state-metrics随着pod增长,数据量激增,一次抓取耗时不断增加。
对于Prometheus slave因为采集部分数据,压力较小,单个Prometheus可抓取超过400个节点(60 pod/node)。以下图所示在开启remote write后抓取时间不断增长,后续将不断增长Remote-Storage-Adapter的性能。
通过在k8s测试集群验证,Prometheus分区监控架构最多支持8w的pod,能够知足预期集群增加需求。
目前分区监控方案已在部分集群部署,具备高可用、持久存储、可动态调整等特色。另外,咱们将来将持续改进:实现监控的自动扩容,针对kube-state-metrics的性能优化(目前不支持分区);在部署方式上,借助prometheus-operator与helm等实现更简洁的配置管理与部署;在监控数据的利用上,能够应用特定算法对数据进行深度挖掘以提供有价值的信息,如利用监控数据提供扩容预测,寻找合适的扩容时机。经过不断优化,以确保更好地为k8s提供稳定可靠智能的监控服务。
本文首发于公众号“小米云技术”,转载请注明做者及出处,点击查看原文。