共计 21831 个字符,预计需要花费 55 分钟才能阅读完成。
_原文:https://tinyurl.com/y2al3hed
监控零碎的历史悠久,是一个很成熟的方向,而 Prometheus 作为新生代的开源监控零碎,缓缓成为了云原生体系的事实标准,也证实了其设计很受欢迎。本文次要分享在 Prometheus 实际中遇到的一些问题和思考,如果你对 Kubernetes 监控体系或 Prometheus 的设计还不太理解,能够先看下容器监控系列。
几点准则
- 监控是基础设施,目标是为了解决问题,不要只朝着大而全去做,尤其是不必要的指标采集,节约人力和存储资源(To B 商业产品例外)。
- 须要解决的告警才收回来,收回来的告警必须失去解决。
- 简略的架构就是最好的架构,业务零碎都挂了,监控也不能挂。Google SRE 外面也说防止应用 Magic 零碎,例如机器学习报警阈值、主动修复之类。这一点见仁见智吧,感觉很多公司都在搞智能 AI 运维。
Prometheus 的局限
- Prometheus 是基于 Metric 的监控,不适用于日志(Logs)、事件(Event)、调用链(Tracing)。
- Prometheus 默认是 Pull 模型,正当布局你的网络,尽量不要转发。
- 对于集群化和程度扩大,官网和社区都没有银弹,须要正当抉择 Federate、Cortex、Thanos 等计划。
- 监控零碎个别状况下可用性大于一致性,容忍局部正本数据失落,保障查问申请胜利。这个前面说 Thanos 去重的时候会提到。
- Prometheus 不肯定保证数据精确,这里的不精确一是指 rate、histogram_quantile 等函数会做统计和推断,产生一些反直觉的后果,这个前面会具体开展。二来查问范畴过长要做降采样,势必会造成数据精度失落,不过这是时序数据的特点,也是不同于日志零碎的中央。
Kubernetes 集群中罕用的 Exporter
Prometheus 属于 CNCF 我的项目,领有残缺的开源生态,与 Zabbix 这种传统 Agent 监控不同,它提供了丰盛的 Exporter 来满足你的各种需要。你能够在这里 [2] 看到官网、非官方的 Exporter。如果还是没满足你的需要,你还能够本人编写 Exporter,简略不便、自在凋谢,这是长处。
然而过于凋谢就会带来选型、试错老本。之前只须要在 Zabbix Agent 外面几行配置就能实现的事,当初你会须要很多 Exporter 搭配能力实现。还要对所有 Exporter 保护、监控。尤其是降级 Exporter 版本时,很苦楚。非官方 Exporter 还会有不少 bug。这是应用上的有余,当然也是 Prometheus 的设计准则。
Kubernetes 生态的组件都会提供 /metric 接口以提供自监控,这里列下咱们正在应用的:
- cAdvisor:集成在 Kubelet 中。
- kubelet:10255 为非认证端口,10250 为认证端口。
- apiserver:6443 端口,关怀申请数、提早等。
- scheduler:10251 端口。
- controller-manager:10252 端口。
- etcd:如 etcd 写入读取提早、存储容量等。
- Docker:须要开启 experimental 试验个性,配置 metrics-addr,如容器创立耗时等指标。
- kube-proxy:默认 127 裸露,10249 端口。内部采集时能够批改为 0.0.0.0 监听,会裸露:写入 iptables 规定的耗时等指标。
- kube-state-metrics:Kubernetes 官网我的项目,采集 Pod、Deployment 等资源的元信息。
- node-exporter:Prometheus 官网我的项目,采集机器指标如 CPU、内存、磁盘。
- blackbox_exporter:Prometheus 官网我的项目,网络探测,DNS、ping、http 监控。
- process-exporter:采集过程指标。
- NVIDIA Exporter:咱们有 GPU 工作,须要 GPU 数据监控。
- node-problem-detector:即 NPD,精确的说不是 Exporter,但也会监测机器状态,上报节点异样打 taint。
- 应用层 Exporter:MySQL、Nginx、MQ 等,看业务需要。
还有各种场景下的自定义 Exporter,如日志提取,前面会再做介绍。
Kubernetes 外围组件监控与 Grafana 面板
Kubernetes 集群运行中须要关注外围组件的状态、性能。如 kubelet、apiserver 等,基于下面提到的 Exporter 的指标,能够在 Grafana 中绘制如下图表:
模板能够参考 Grafana Dashboards for Kubernetes Administrators[3],依据运行状况一直调整报警阈值。
这里提一下 Grafana 尽管反对了 templates 能力,能够很不便地做多级下拉框抉择,然而不反对 templates 模式下配置报警规定,相干 issue[4]。
官网对这个性能解释了一堆,可最新版本依然没有反对。借用 issue 的一句话吐槽下:
It would be grate to add templates support in alerts. Otherwise the feature looks useless a bit.
对于 Grafana 的根底用法,能够看这个文章[5]。
**
采集组件 All in One**
Prometheus 体系中 Exporter 都是独立的,每个组件各司其职,如机器资源用 Node-Exporter,GPU 有 Nvidia Exporter 等等。然而 Exporter 越多,运维压力越大,尤其是对 Agent 做资源管制、版本升级。咱们尝试对一些 Exporter 进行组合,计划有二:
- 通过主过程拉起 N 个 Exporter 过程,依然能够跟着社区版本做更新、bug fix。
- 用 Telegraf 来反对各种类型的 Input,N 合 1。
另外,Node-Exporter 不反对过程监控,能够加一个 Process-Exporter,也能够用上边提到的 Telegraf,应用 procstat 的 input 来采集过程指标。
** 正当抉择黄金指标
**
采集的指标有很多,咱们应该关注哪些?Google 在“SRE Handbook”中提出了“四个黄金信号”:提早、流量、谬误数、饱和度。实际操作中能够应用 Use 或 Red 办法作为领导,Use 用于资源,Red 用于服务。
- Use 办法:Utilization、Saturation、Errors。如 Cadvisor 数据
- Red 办法:Rate、Errors、Duration。如 Apiserver 性能指标
Prometheus 采集中常见的服务分三种:
- 在线服务:如 Web 服务、数据库等,个别关怀申请速率,提早和错误率即 RED 办法
- 离线服务:如日志解决、音讯队列等,个别关注队列数量、进行中的数量,处理速度以及产生的谬误即 Use 办法
- 批处理工作:和离线工作很像,然而离线工作是长期运行的,批处理工作是按计划运行的,如继续集成就是批处理工作,对应 Kubernetes 中的 Job 或 CronJob,个别关注所花的工夫、谬误数等,因为运行周期短,很可能还没采集到就运行完结了,所以个别应用 Pushgateway,改拉为推。
对 Use 和 Red 的理论示例能够参考《容器监控实际——Kubernetes 罕用指标剖析[6]》这篇文章。
Kubernetes 1.16 中 cAdvisor 的指标兼容问题
在 Kubernetes 1.16 版本,cAdvisor 的指标去掉了 pod_Name 和 container_name 的 label,替换为了 Pod 和 Container。如果你之前用这两个 label 做查问或者 Grafana 绘图,须要更改下 SQL 了。因为咱们始终反对多个 Kubernetes 版本,就通过 relabel 配置持续保留了原来的 **_name。
metric_relabel_configs:
- source_labels: [container]
regex: (.+)
target_label: container_name
replacement: $1
action: replace
- source_labels: [pod]
regex: (.+)
target_label: pod_name
replacement: $1
action: replace
留神要用 metric_relabel_configs,不是 relabel_configs,采集后做的 replace。
Prometheus 采集内部 Kubernetes 集群、多集群
Prometheus 如果部署在 Kubernetes 集群内采集是很不便的,用官网给的 Yaml 就能够,但咱们因为权限和网络须要部署在集群外,二进制运行,采集多个 Kubernetes 集群。
以 Pod 形式运行在集群内是不须要证书的(In-Cluster 模式),但集群外须要申明 token 之类的证书,并替换 __address__,即应用 Apiserver Proxy 采集,以 cAdvisor 采集为例,Job 配置为:
- job_name: cluster-cadvisor
honor_timestamps: true
scrape_interval: 30s
scrape_timeout: 10s
metrics_path: /metrics
scheme: https
kubernetes_sd_configs:
- api_server: https://xx:6443
role: node
bearer_token_file: token/cluster.token
tls_config:
insecure_skip_verify: true
bearer_token_file: token/cluster.token
tls_config:
insecure_skip_verify: true
relabel_configs:
- separator: ;
regex: __meta_kubernetes_node_label_(.+)
replacement: $1
action: labelmap
- separator: ;
regex: (.*)
target_label: __address__
replacement: xx:6443
action: replace
- source_labels: [__meta_kubernetes_node_name]
separator: ;
regex: (.+)
target_label: __metrics_path__
replacement: /api/v1/nodes/${1}/proxy/metrics/cadvisor
action: replace
metric_relabel_configs:
- source_labels: [container]
separator: ;
regex: (.+)
target_label: container_name
replacement: $1
action: replace
- source_labels: [pod]
separator: ;
regex: (.+)
target_label: pod_name
replacement: $1
action: replace
bearer_token_file 须要提前生成,这个参考官网文档即可。记得 base64 解码。
对于 cAdvisor 来说,__metrics_path__ 能够转换为 /api/v1/nodes/${1}/proxy/metrics/cadvisor,代表 Apiserver proxy 到 Kubelet。
如果网络能通,其实也能够间接把 Kubelet 的 10255 作为 target,能够间接写为:${1}:10255/metrics/cadvisor,代表间接申请 Kubelet,规模大的时候还加重了 Apiserver 的压力,即服务发现应用 Apiserver,采集不走 Apiserver。
因为 cAdvisor 是裸露主机端口,配置绝对简略,如果是 kube-state-metric 这种 Deployment,以 Endpoint 模式裸露,写法应该是:
- job_name: cluster-service-endpoints
honor_timestamps: true
scrape_interval: 30s
scrape_timeout: 10s
metrics_path: /metrics
scheme: https
kubernetes_sd_configs:
- api_server: https://xxx:6443
role: endpoints
bearer_token_file: token/cluster.token
tls_config:
insecure_skip_verify: true
bearer_token_file: token/cluster.token
tls_config:
insecure_skip_verify: true
relabel_configs:
- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]
separator: ;
regex: "true"
replacement: $1
action: keep
- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme]
separator: ;
regex: (https?)
target_label: __scheme__
replacement: $1
action: replace
- separator: ;
regex: (.*)
target_label: __address__
replacement: xxx:6443
action: replace
- source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_endpoints_name,
__meta_kubernetes_service_annotation_prometheus_io_port]
separator: ;
regex: (.+);(.+);(.*)
target_label: __metrics_path__
replacement: /api/v1/namespaces/${1}/services/${2}:${3}/proxy/metrics
action: replace
- separator: ;
regex: __meta_kubernetes_service_label_(.+)
replacement: $1
action: labelmap
- source_labels: [__meta_kubernetes_namespace]
separator: ;
regex: (.*)
target_label: kubernetes_namespace
replacement: $1
action: replace
- source_labels: [__meta_kubernetes_service_name]
separator: ;
regex: (.*)
target_label: kubernetes_name
replacement: $1
action: replace
对于 Endpoint 类型,须要转换 __metrics_path__ 为 /api/v1/namespaces/${1}/services/${2}:${3}/proxy/metrics,须要替换 namespace、svc 名称端口等。
这里的写法只适宜接口为 /metrics 的 exporter,如果你的 exporter 不是 /metrics 接口,须要替换这个门路。或者像咱们一样对立束缚都应用这个地址。
这里的 __meta_kubernetes_service_annotation_prometheus_io_port 起源就是 exporter 部署时写的那个 annotation,大多数文章中只提到 prometheus.io/scrape: ‘true’,但也能够定义端口、门路、协定。以不便在采集时做替换解决。
其余的一些 relabel 如 kubernetes_namespace 是为了保留原始信息,不便做 PromQL 查问时的筛选条件。
如果是多集群,同样的配置多写几遍就能够了,个别一个集群能够配置三类 Job:
- role:node 的,包含 cAdvisor、node-exporter、kubelet 的 summary、kube-proxy、Docker 等指标
- role:endpoint 的,包含 kube-state-metric 以及其余自定义 Exporter
- 一般采集:包含 etcd、Apiserver 性能指标、过程指标等。
GPU 指标的获取
nvidia-smi 能够查看机器上的 GPU 资源,而 cAdvisor 其实裸露了 Metric 来示意容器应用 GPU 状况。
container_accelerator_duty_cycle
container_accelerator_memory_total_bytes
container_accelerator_memory_used_bytes
如果要更具体的 GPU 数据,能够装置 dcgm exporter,不过 Kubernetes 1.13 能力反对。
更改 Prometheus 的显示时区
Prometheus 为防止时区凌乱,在所有组件中专门应用 Unix Time 和 Utc 进行显示。不反对在配置文件中设置时区,也不能读取本机 /etc/timezone 时区。
其实这个限度是不影响应用的:
- 如果做可视化,Grafana 是能够做时区转换的。
- 如果是调接口,拿到了数据中的工夫戳,你想怎么解决都能够。
- 如果因为 Prometheus 自带的 UI 不是本地工夫,看着不难受,2.16 版本的新版 Web UI 曾经引入了 Local Timezone 的选项,区别见下图。
- 如果你依然想改 Prometheus 代码来适应本人的时区。
如何采集 LB 前面的 RS 的 Metric
如果你有一个负载平衡 LB,但网络上 Prometheus 只能拜访到 LB 自身,拜访不到前面的 RS,应该如何采集 RS 裸露的 Metric?
- RS 的服务加 Sidecar Proxy,或者本机减少 Proxy 组件,保障 Prometheus 能拜访到。
- LB 减少 /backend1 和 /backend2 申请转发到两个独自的后端,再由 Prometheus 拜访 LB 采集。
版本的抉择
Prometheus 以后最新版本为 2.16,Prometheus 还在一直迭代,因而尽量用最新版,1.X 版本就不必思考了。
2.16 版本上有一套试验 UI,能够查看 TSDB 的状态,包含 Top 10 的 Label、Metric。
Prometheus 大内存问题
随着规模变大,Prometheus 须要的 CPU 和内存都会升高,内存个别先达到瓶颈,这个时候要么加内存,要么集群分片缩小单机指标。这里咱们先探讨单机版 Prometheus 的内存问题。
起因:
- Prometheus 的内存耗费次要是因为每隔 2 小时做一个 Block 数据落盘,落盘之前所有数据都在内存外面,因而和采集量无关。
- 加载历史数据时,是从磁盘到内存的,查问范畴越大,内存越大。这外面有肯定的优化空间。
- 一些不合理的查问条件也会加大内存,如 Group 或大范畴 Rate。
我的指标须要多少内存:
- 作者给了一个计算器,设置指标量、采集距离之类的,计算 Prometheus 须要的实践内存值。
以咱们的一个 Prometheus Server 为例,本地只保留 2 小时数据,95 万 Series,大略占用的内存如下:
有什么优化计划:
- Sample 数量超过了 200 万,就不要单实例了,做下分片,而后通过 Victoriametrics,Thanos,Trickster 等计划合并数据。
- 评估哪些 Metric 和 Label 占用较多,去掉没用的指标。2.14 以上能够看 TSDB 状态
- 查问时尽量避免大范畴查问,留神工夫范畴和 Step 的比例,慎用 Group。
- 如果须要关联查问,先想想能不能通过 Relabel 的形式给原始数据多加个 Label,一条 SQL 能查出来的何必用 Join,时序数据库不是关系数据库。
Prometheus 内存占用剖析:
- 通过 pprof 剖析:https://www.robustperception….
- 1.X 版本的内存:https://www.robustperception….
相干 issue:
- https://groups.google.com/for…:date/prometheus-users/q4oiVGU6Bxo/uifpXVw3CwAJ
- https://github.com/prometheus…
- https://github.com/prometheus…
Prometheus 容量布局
容量布局除了上边说的内存,还有磁盘存储布局,这和你的 Prometheus 的架构计划无关。
- 如果是单机 Prometheus,计算本地磁盘使用量。
- 如果是 Remote-Write,和已有的 TSDB 共用即可。
- 如果是 Thanos 计划,本地磁盘能够疏忽(2H),计算对象存储的大小就行。
Prometheus 每 2 小时将已缓冲在内存中的数据压缩到磁盘上的块中。包含 Chunks、Indexes、Tombstones、Metadata,这些占用了一部分存储空间。个别状况下,Prometheus 中存储的每一个样本大略占用 1-2 字节大小(1.7Byte)。能够通过 PromQL 来查看每个样本均匀占用多少空间:
rate(prometheus_tsdb_compaction_chunk_size_bytes_sum[1h])
/
rate(prometheus_tsdb_compaction_chunk_samples_sum[1h])
{instance="0.0.0.0:8890", job="prometheus"} 1.252747585939941
如果大抵估算本地磁盘大小,能够通过以下公式:
磁盘大小 = 保留工夫 \* 每秒获取样本数 \* 样本大小
保留工夫(retention_time_seconds)和样本大小(bytes_per_sample)不变的状况下,如果想缩小本地磁盘的容量需要,只能通过缩小每秒获取样本数(ingested_samples_per_second)的形式。
查看以后每秒获取的样本数:
rate(prometheus_tsdb_head_samples_appended_total[1h])
有两种伎俩,一是缩小工夫序列的数量,二是减少采集样本的工夫距离。思考到 Prometheus 会对工夫序列进行压缩,因而缩小工夫序列的数量成果更显著。
举例说明:
- 采集频率 30s,机器数量 1000,Metric 品种 6000,1000600026024 约 200 亿,30G 左右磁盘。
- 只采集须要的指标,如 match[], 或者统计下最常应用的指标,性能最差的指标。
以上磁盘容量并没有把 wal 文件算进去,wal 文件(Raw Data)在 Prometheus 官网文档中阐明至多会保留 3 个 Write-Ahead Log Files,每一个最大为 128M(理论运行发现数量会更多)。
因为咱们应用了 Thanos 的计划,所以本地磁盘只保留 2H 热数据。Wal 每 2 小时生成一份 Block 文件,Block 文件每 2 小时上传对象存储,本地磁盘根本没有压力。
对于 Prometheus 存储机制,能够看这篇[10]。
对 Apiserver 的性能影响
如果你的 Prometheus 应用了 kubernetes_sd_config 做服务发现,申请个别会通过集群的 Apiserver,随着规模的变大,须要评估下对 Apiserver 性能的影响,尤其是 Proxy 失败的时候,会导致 CPU 升高。当然了,如果单 Kubernetes 集群规模太大,个别都是拆分集群,不过随时监测下 Apiserver 的过程变动还是有必要的。
在监控 cAdvisor、Docker、Kube-Proxy 的 Metric 时,咱们一开始抉择从 Apiserver Proxy 到节点的对应端口,对立设置比拟不便,但起初还是改为了间接拉取节点,Apiserver 仅做服务发现。
Rate 的计算逻辑
Prometheus 中的 Counter 类型次要是为了 Rate 而存在的,即计算速率,单纯的 Counter 计数意义不大,因为 Counter 一旦重置,总计数就没有意义了。
Rate 会主动解决 Counter 重置的问题,Counter 个别都是始终变大的,例如一个 Exporter 启动,而后解体了。原本以每秒大概 10 的速率递增,但仅运行了半个小时,则速率(x_total [1h])将返回大概每秒 5 的后果。另外,Counter 的任何缩小也会被视为 Counter 重置。例如,如果工夫序列的值为[5,10,4,6],则将其视为[5,10,14,16]。
Rate 值很少是准确的。因为针对不同指标的抓取产生在不同的工夫,因而随着工夫的流逝会产生抖动,query_range 计算时很少会与抓取工夫完满匹配,并且抓取有可能失败。面对这样的挑战,Rate 的设计必须是强壮的。
Rate 并非想要捕捉每个增量,因为有时候增量会失落,例如实例在抓取距离中挂掉。如果 Counter 的变动速度很慢,例如每小时仅减少几次,则可能会导致【假象】。比方呈现一个 Counter 工夫序列,值为 100,Rate 就不晓得这些增量是当初的值,还是指标曾经运行了好几年并且才刚刚开始返回。
倡议将 Rate 计算的范畴向量的工夫至多设为抓取距离的四倍。这将确保即便抓取速度迟缓,且产生了一次抓取故障,您也始终能够应用两个样本。此类问题在实践中经常出现,因而放弃这种弹性十分重要。例如,对于 1 分钟的抓取距离,您能够应用 4 分钟的 Rate 计算,然而通常将其四舍五入为 5 分钟。
如果 Rate 的工夫区间内有数据缺失,他会基于趋势进行揣测,比方:
反直觉的 P95 统计
histogram_quantile 是 Prometheus 罕用的一个函数,比方常常把某个服务的 P95 响应工夫来掂量服务质量。不过它到底是什么意思很难解释得清,特地是面向非技术的同学,会遇到很多“灵魂拷问”。
咱们常说 P95(P99,P90 都能够)响应提早是 100ms,实际上是指对于收集到的所有响应提早,有 5% 的申请大于 100ms,95% 的申请小于 100ms。Prometheus 外面的 histogram_quantile 函数接管的是 0-1 之间的小数,将这个小数乘以 100 就能很容易失去对应的百分位数,比方 0.95 就对应着 P95,而且还能够高于百分位数的精度,比方 0.9999。
当你用 histogram_quantile 画出响应工夫的趋势图时,可能会被问:为什么 P95 大于或小于我的平均值?
正如中位数可能比平均数大也可能比平均数小,P99 比平均值小也是齐全有可能的。通常状况下 P99 简直总是比平均值要大的,然而如果数据分布比拟极其,最大的 1% 可能大得离谱从而拉高了平均值。一种可能的例子:
1, 1, … 1, 901 // 共 100 条数据,平均值 =10,P99=1
服务 X 由程序的 A,B 两个步骤实现,其中 X 的 P99 耗时 100ms,A 过程 P99 耗时 50ms,那么揣测 B 过程的 P99 耗时状况是?
直觉上来看,因为有 X=A+B,所以答案可能是 50ms,或者至多应该要小于 50ms。实际上 B 是能够大于 50ms 的,只有 A 和 B 最大的 1% 不恰好遇到,B 齐全能够有很大的 P99:
A = 1, 1, … 1, 1, 1, 50, 50 // 共 100 条数据,P99=50
B = 1, 1, … 1, 1, 1, 99, 99 // 共 100 条数据,P99=99
X = 2, 2, … 1, 51, 51, 100, 100 // 共 100 条数据,P99=100
如果让 A 过程最大的 1% 靠近 100ms,咱们也能结构出 P99 很小的 B:
A = 50, 50, … 50, 50, 99 // 共 100 条数据,P99=50
B = 1, 1, … 1, 1, 50 // 共 100 条数据,P99=1
X = 51, 51, … 51, 100, 100 // 共 100 条数据,P99=100
所以咱们从题目惟一能确定的只有 B 的 P99 应该不能超过 100ms,A 的 P99 耗时 50ms 这个条件其实没啥用。
相似的疑难很多,因而对于 histogram_quantile 函数,可能会产生反直觉的一些后果,最好的解决方法是一直试验调整你的 Bucket 的值,保障更多的申请工夫落在更粗疏的区间内,这样的申请工夫才有统计意义。
慢查问问题
Prometheus 提供了自定义的 PromQL 作为查问语句,在 Graph 上调试的时候,会通知你这条 SQL 的返回工夫,如果太慢你就要留神了,可能是你的用法呈现了问题。
评估 Prometheus 的整体响应工夫,能够用这个默认指标:
prometheus_engine_query_duration_seconds{}
个别状况下响应过慢都是 PromQL 使用不当导致,或者指标布局有问题,如:
- 大量应用 join 来组合指标或者减少 label,如将 kube-state-metric 中的一些 meta label 和 node-exporter 中的节点属性 label 退出到 cAdvisor 容器数据里,像统计 Pod 内存使用率并依照所属节点的机器类型分类,或依照所属 RSS 归类。
- 范畴查问时,大的工夫范畴 step 值却很小,导致查问到的数量过大。
- rate 会主动解决 counter 重置的问题,最好由 PromQL 实现,不要本人拿进去全副元数据在程序中本人做 rate 计算。
- 在应用 rate 时,range duration 要大于等于 step,否则会失落局部数据。
- Prometheus 是有根本预测性能的,如 deriv 和 predict_linear(更精确)能够依据已有数据预测将来趋势。
- 如果比较复杂且耗时的 SQL,能够应用 record rule 缩小指标数量,并使查问效率更高,但不要什么指标都加 record,一半以上的 metric 其实不太会查问到。同时 label 中的值不要加到 record rule 的 name 中。
高基数问题 Cardinality
高基数是数据库避不开的一个话题,对于 MySQL 这种 DB 来讲,基数是指特定列或字段中蕴含的惟一值的数量。基数越低,列中反复的元素越多。对于时序数据库而言,就是 tags、label 这种标签值的数量多少。
比方 Prometheus 中如果有一个指标 http_request_count{method=”get”,path=”/abc”,originIP=”1.1.1.1″} 示意访问量,method 示意申请办法,originIP 是客户端 IP,method 的枚举值是无限的,但 originIP 却是有限的,加上其余 label 的排列组合就无穷大了,也没有任何关联特色,因而这种高基数不适宜作为 Metric 的 label,真要的提取 originIP,应该用日志的形式,而不是 Metric 监控。
时序数据库会为这些 Label 建设索引,以进步查问性能,以便你能够疾速找到与所有指定标签匹配的值。如果值的数量过多,索引是没有意义的,尤其是做 P95 等计算的时候,要扫描大量 Series 数据。
官网文档中对于 Label 的倡议:
CAUTION: Remember that every unique combination of key-value label pairs represents a new time series, which can dramatically increase the amount of data stored. Do not use labels to store dimensions with high cardinality (many different label values), such as user IDs, email addresses, or other unbounded sets of values.
如何查看以后的 Label 散布状况呢,能够应用 Prometheus 提供的 TSDB 工具。能够应用命令行查看,也能够在 2.16 版本以上的 Prometheus Graph 查看。
[work@xxx bin]$ ./tsdb analyze ../data/prometheus/
Block ID: 01E41588AJNGM31SPGHYA3XSXG
Duration: 2h0m0s
Series: 955372
Label names: 301
Postings (unique label pairs): 30757
Postings entries (total label pairs): 10842822
....
top10 高基数的 metric:
Highest cardinality metric names:
87176 apiserver_request_latencies_bucket
59968 apiserver_response_sizes_bucket
39862 apiserver_request_duration_seconds_bucket
37555 container_tasks_state
….
高基数的 label:
Highest cardinality labels:
4271 resource_version
3670 id
3414 name
1857 container_id
1824 __name__
1297 uid
1276 pod
...
找到最大的 Metric 或 Job
top10 的 Metric 数量:按 Metric 名字分。
topk(10, count by (__name__)({__name__=~".+"}))
apiserver_request_latencies_bucket{} 62544
apiserver_response_sizes_bucket{} 44600
top10 的 Job 数量:按 Job 名字分。
topk(10, count by (__name__, job)({__name__=~".+"}))
{job="master-scrape"} 525667
{job="xxx-kubernetes-cadvisor"} 50817
{job="yyy-kubernetes-cadvisor"} 44261
Prometheus 重启慢与热加载
Prometheus 重启的时候须要把 Wal 中的内容 Load 到内存里,保留工夫越久、Wal 文件越大,重启的理论越长,这个是 Prometheus 的机制,没得方法,因而能 Reload 的就不要重启,重启肯定会导致短时间的不可用,而这个时候 Prometheus 高可用就很重要了。
Prometheus 也已经对启动工夫做过优化,在 2.6 版本中对于 Wal 的 Load 速度就做过速度的优化,心愿重启的工夫不超过 1 分钟。
Prometheus 提供了热加载能力,不过须要开启 web.enable-lifecycle 配置,更改完配置后,curl 下 reload 接口即可。prometheus-operator 中更改了配置会默认触发 reload,如果你没有应用 Operator,又心愿能够监听 ConfigMap 配置变动来 reload 服务,能够试下这个简略的脚本。
#!/bin/sh
FILE=$1
URL=$2
HASH=$(md5sum $(readlink -f $FILE))
while true; do
NEW_HASH=$(md5sum $(readlink -f $FILE))
if ["$HASH" != "$NEW_HASH"]; then
HASH="$NEW\_HASH"
echo "[$(date +%s)] Trigger refresh"
curl -sSL -X POST "$2" > /dev/null
fi
sleep 5
done
应用时和 Prometheus 挂载同一个 ConfigMap,传入如下参数即可:
args:
- /etc/prometheus/prometheus.yml
- http://prometheus.kube-system.svc.cluster.local:9090/-/reload
args:
- /etc/alertmanager/alertmanager.yml
- http://prometheus.kube-system.svc.cluster.local:9093/-/reload
你的利用须要裸露多少指标
当你开发本人的服务的时候,你可能会把一些数据裸露 Metric 进来,比方特定申请数、Goroutine 数等,指标数量多少适合呢?
尽管指标数量和你的利用规模相干,但也有一些倡议(Brian Brazil),比方简略的服务,如缓存等,相似 Pushgateway,大概 120 个指标,Prometheus 自身裸露了 700 左右的指标,如果你的利用很大,也尽量不要超过 10000 个指标,须要正当管制你的 Label。
node-exporter 的问题
- node-exporter 不反对过程监控,这个后面曾经提到了。
- node-exporter 只反对 Unix 零碎,Windows 机器请应用 wmi_exporter。因而以 yaml 模式不是 node-exporter 的时候,node-selector 要表明 OS 类型。
- 因为 node_exporter 是比拟老的组件,有一些最佳实际并没有 merge 进去,比方合乎 Prometheus 命名标准,因而倡议应用较新的 0.16 和 0.17 版本。
一些指标名字的变动:
* node_cpu -> node_cpu_seconds_total
* node_memory_MemTotal -> node_memory_MemTotal_bytes
* node_memory_MemFree -> node_memory_MemFree_bytes
* node_filesystem_avail -> node_filesystem_avail_bytes
* node_filesystem_size -> node_filesystem_size_bytes
* node_disk_io_time_ms -> node_disk_io_time_seconds_total
* node_disk_reads_completed -> node_disk_reads_completed_total
* node_disk_sectors_written -> node_disk_written_bytes_total
* node_time -> node_time_seconds
* node_boot_time -> node_boot_time_seconds
* node_intr -> node_intr_total
如果你之前用的旧版本 Exporter,在绘制 Grafana 的时候指标名称就会有差异,解决办法有两种:
- 一是在机器上启动两个版本的 node-exporter,都让 Prometheus 去采集。
- 二是应用指标转换器,他会将旧指标名称转换为新指标。
kube-state-metric 的问题
kube-state-metric 的应用和原理能够先看下这篇[13]。
除了文章中提到的作用,kube-state-metric 还有一个很重要的应用场景,就是和 cAdvisor 指标组合,原始的 cAdvisor 中只有 Pod 信息,不晓得属于哪个 Deployment 或者 sts,然而和 kube-state-metric 中的 kube_pod_info 做 join 查问之后就能够显示进去,kube-state-metric 的元数据指标,在扩大 cAdvisor 的 label 中起到了很多作用,prometheus-operator 的很多 record rule 就应用了 kube-state-metric 做组合查问。
kube-state-metric 中也能够展现 Pod 的 label 信息,能够在拿到 cAdvisor 数据后更不便地做 group by,如依照 Pod 的运行环境分类。然而 kube-state-metric 不裸露 Pod 的 annotation,起因是上面会提到的高基数问题,即 annotation 的内容太多,不适宜作为指标裸露。
relabel_configs 与 metric_relabel_configs
relabel_config 产生在采集之前,metric_relabel_configs 产生在采集之后,正当搭配能够满足很多场景的配置。
如:
metric_relabel_configs:
- separator: ;
regex: instance
replacement: $1
action: labeldrop
- source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_endpoint_name,
__meta_kubernetes_service_annotation_prometheus_io_port]
separator: ;
regex: (.+);(.+);(.*)
target_label: __metrics_path__
replacement: /api/v1/namespaces/${1}/services/${2}:${3}/proxy/metrics
action: replace
Prometheus 的预测能力
场景 1:你的磁盘残余空间始终在缩小,并且升高的速度比拟平均,你心愿晓得大略多久之后达到阈值,并心愿在某一个时刻报警进去。
场景 2:你的 Pod 内存使用率始终升高,你心愿晓得大略多久之后会达到 Limit 值,并在肯定时刻报警进去,在被杀掉之前下来排查。
Prometheus 的 Deriv 和 Predict_Linear 办法能够满足这类需要,Promtheus 提供了根底的预测能力,基于以后的变动速度,揣测一段时间后的值。
以 mem_free 为例,最近一小时的 free 值始终在降落。
mem_free 仅为举例,理论内存可用以 mem_available 为准
Deriv 函数能够显示指标在一段时间的变动速度:
predict_linear 办法是预测基于这种速度,最初能够达到的值:
predict_linear(mem_free{instanceIP="100.75.155.55"}[1h], 2*3600)/1024/1024
你能够基于设置正当的报警规定,如小于 10 时报警:
bash
rule: predict_linear(mem_free{instanceIP="100.75.155.55"}[1h], 2*3600)/1024/1024 <10
predict_linear 与 Deriv 的关系:含意上约等于如下表达式,不过 predict_linear 略微精确一些。
deriv(mem_free{instanceIP="100.75.155.55"}[1h]) * 2 * 3600
+
mem_free{instanceIP="100.75.155.55"}[1h]
如果你要基于 Metric 做模型预测,能够参考下 forecast-prometheus[14]。
alertmanager 的下层封装
Prometheus 部署之后很少会改变,尤其是做了服务发现,就不须要频繁新增 target。但报警的配置是很频繁的,如批改阈值、批改报警人等。alertmanager 领有丰盛的报警能力如分组、克制等,但如果你要想把它给业务部门应用,就要做一层封装了,也就是报警配置台。用户喜爱表单操作,而非艰涩的 yaml,同时他们也并不违心去了解 PromQL。而且大多数公司内曾经有现成的监控平台,也只有一份短信或邮件网关,所以最好能应用 webhook 间接集成。
例如:机器磁盘使用量超过 90% 就报警,rule 应该写为:disk_used/disk_total > 0.9。
如果不加 label 筛选,这条报警会对所有机器失效,但如果你想去掉其中几台机器,就得在 disk_used 和 disk_total 前面加上 {instance != “”}。这些操作在 PromQL 中是很简略的,然而如果放在表单里操作,就得和外部的 CMDB 做联动筛选了。
对于一些简略的需要,咱们应用了 Grafana 的报警能力,所见即所得,间接在图表上面配置告警即可,报警阈值和状态很清晰。不过 Grafana 的报警能力很弱,只是试验性能,能够作为调试应用。
对于常见的 Pod 或利用监控,咱们做了一些表单化,如下图所示:提取了 CPU、内存、磁盘 IO 等常见的指标作为选择项,不便配置。
应用 webhook 扩大报警能力,革新 alertmanager,在 send message 时做加密和认证,对接外部已有报警能力,并联动用户体系,做限流和权限管制。
调用 alertmanager api 查问报警事件,进行展现和统计。
对于用户来说,封装 alertmanager yaml 会变的易用,但也会限度其能力,在减少报警配置时,研发和运维须要有肯定的配合。如新写了一份自定义的 exporter,要将须要的指标供用户抉择,并调整好展现和报警用的 PromQL。还有报警模板、原生 PromQL 裸露、用户分组等,须要视用户需要做衡量。
谬误的高可用设计
有些人提出过这种类型的计划,想进步其扩展性和可用性。
应用程序将 Metric 推到到音讯队列如 Kafaka,而后通过 Exposer 生产直达,再被 Prometheus 拉取。产生这种计划的起因个别是有历史包袱、复用现有组件、想通过 MQ 来进步扩展性。
这种计划有几个问题:
- 减少了 Queue 组件,多了一层依赖,如果 App 与 Queue 之间连贯失败,难道要在 App 本地缓存监控数据?
- 抓取工夫可能会不同步,提早的数据将会被标记为古老数据,当然你能够通过增加工夫戳来标识,但就失去了对古老数据的解决逻辑。
- 扩展性问题:Prometheus 适宜大量小指标,而不是一个大指标,如果你把所有数据都放在了 Exposer 中,那么 Prometheus 的单个 Job 拉取就会成为 CPU 瓶颈。这个和 Pushgateway 有些相似,没有特地必要的场景,都不是官网倡议的形式。
- 短少了服务发现和拉取管制,Prom 只晓得一个 Exposer,不晓得具体是哪些 Target,不晓得他们的 UP 工夫,无奈应用 Scrape_* 等指标做查问,也无奈用 scrape_limit 做限度。
如果你的架构和 Prometheus 的设计理念相悖,可能要从新设计一下计划了,否则扩展性和可靠性反而会升高。
prometheus-operator 的场景
如果你是在 Kubernetes 集群内部署 Prometheus,那大概率会用到 prometheus-operator,他对 Prometheus 的配置做了 CRD 封装,让用户更不便的扩大 Prometheus 实例,同时 prometheus-operator 还提供了丰盛的 Grafana 模板,包含下面提到的 Master 组件监控的 Grafana 视图,Operator 启动之后就能够间接应用,免去了配置面板的懊恼。
Operator 的长处很多,就不一一列举了,只提一下 Operator 的局限:
- 因为是 Operator,所以依赖 Kubernetes 集群,如果你须要二进制部署你的 Prometheus,如集群外部署,就很难用上 prometheus-operator 了,如多集群场景。当然你也能够在 Kubernetes 集群中部署 Operator 去监控其余的 Kubernetes 集群,但这外面坑不少,须要批改一些配置。
- Operator 屏蔽了太多细节,这个对用户是坏事,但对于了解 Prometheus 架构就有些 gap 了,比方碰到一些用户一键装置了 Operator,但 Grafana 图表异样后齐全不晓得如何排查,record rule 和服务发现还不理解的状况下就间接配置,倡议在应用 Operator 之前,最好相熟 Prometheus 的根底用法。
- Operator 不便了 Prometheus 的扩大和配置,对于 alertmanager 和 exporter 能够很不便的做到多实例高可用,然而没有解决 Prometheus 的高可用问题,因为无奈解决数据不统一,Operator 目前的定位也还不是这个方向,和 Thanos、Cortex 等计划的定位是不同的,上面会具体解释。
高可用计划
Prometheus 高可用有几种计划:
- 根本 HA:即两套 Prometheus 采集齐全一样的数据,外边挂负载平衡。
- HA + 近程存储:除了根底的多正本 Prometheus,还通过 Remote Write 写入到近程存储,解决存储长久化问题。
- 联邦集群:即 Federation,依照性能进行分区,不同的 Shard 采集不同的数据,由 Global 节点来对立寄存,解决监控数据规模的问题。
- 应用 Thanos 或者 Victoriametrics,来解决全局查问、多正本数据 Join 问题。
就算应用官网倡议的多正本 + 联邦,依然会遇到一些问题:
- 官网倡议数据做 Shard,而后通过 Federation 来实现高可用,
- 然而边缘节点和 Global 节点仍然是单点,须要自行决定是否每一层都要应用双节点反复采集进行保活。也就是依然会有单机瓶颈。
- 另外局部敏感报警尽量不要通过 Global 节点触发,毕竟从 Shard 节点到 Global 节点传输链路的稳定性会影响数据达到的效率,进而导致报警实效升高。
- 例如服务 Updown 状态,API 申请异样这类报警咱们都放在 Shard 节点进行报警。
实质起因是,Prometheus 的本地存储没有数据同步能力,要在保障可用性的前提下,再保持数据一致性是比拟艰难的,根底的 HA Proxy 满足不了要求,比方:
- 集群的后端有 A 和 B 两个实例,A 和 B 之间没有数据同步。A 宕机一段时间,失落了一部分数据,如果负载平衡失常轮询,申请打到 A 上时,数据就会异样。
- 如果 A 和 B 的启动工夫不同,时钟不同,那么采集同样的数据工夫戳也不同,就不是多正本同样数据的概念了。
- 就算用了近程存储,A 和 B 不能推送到同一个 TSDB,如果每人推送本人的 TSDB,数据查问走哪边就是问题了。
因而解决方案是在存储、查问两个角度上保证数据的统一:
- 存储角度:如果应用 Remote Write 近程存储,A 和 B 前面能够都加一个 Adapter,Adapter 做选主逻辑,只有一份数据能推送到 TSDB,这样能够保障一个异样,另一个也能推送胜利,数据不丢,同时近程存储只有一份,是共享数据。计划能够参考这篇文章[15]。
- 查问角度:上边的计划实现很简单且有肯定危险,因而当初的大多数计划在查问层面做文章,比方 Thanos 或者 Victoriametrics,依然是两份数据,然而查问时做数据去重和 Join。只是 Thanos 是通过 Sidecar 把数据放在对象存储,Victoriametrics 是把数据 Remote Write 到本人的 Server 实例,但查问层 Thanos-Query 和 Victor 的 Promxy 的逻辑基本一致。
咱们采纳了 Thanos 来反对多地区监控数据,具体计划能够看这篇文章[16]。
容器日志与事件
本文次要是 Prometheus 监控内容,这里只简略介绍下 Kubernetes 中的日志、事件处理计划,以及和 Prometheus 的搭配。
日志解决:
- 日志采集与推送:个别是 Fluentd/Fluent-Bit/Filebeat 等采集推送到 ES、对象存储、Kafka,日志就该交给业余的 EFK 来做,分为容器规范输入、容器内日志。
- 日志解析转 metric:能够提取一些日志转为 Prometheus 格局的指标,如解析特定字符串呈现次数,解析 Nginx 日志失去 QPS、申请提早等。罕用计划是 mtail 或者 grok。
日志采集计划:
- Sidecar 形式:和业务容器共享日志目录,由 sidecar 实现日志推送,个别用于多租户场景。
- DaemonSet 形式:机器上运行采集过程,对立推送进来。
须要留神的点:对于容器规范输入,默认日志门路是:
/var/lib/docker/containers/xxx,kubelet 会将改日志软链到 /var/log/pods,同时还有一份 /var/log/containers 是对 /var/log/pods 的软链。不过不同的 Kubernetes 版本,日志的目录格局有所变动,采集时依据版本做辨别:
- 1.15 及以下:/var/log/pods/{pod_uid}/
- 1.15 以上:var/log/pods/{pod_name+namespace+rs+uuid}/
事件:在这里特指 Kubernetes Events,Events 在排查集群问题时也很要害,不过默认状况下只保留 1h,因而须要对 Events 做长久化。个别 Events 解决形式有两种:
- 应用 kube-eventer 之类的组件采集 Events 并推送到 ES
- 应用 event_exporter 之类的组件将 Events 转化为 Prometheus Metric,同类型的还有谷歌云的 stackdriver 下的 event-exporter。