前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Prometheus监控实战

Prometheus监控实战

作者头像
yeedomliu
发布2021-07-19 11:55:19
9.2K0
发布2021-07-19 11:55:19
举报
文章被收录于专栏:yeedomliuyeedomliu

第1章 监控简介

  • 一个开源的监控系统,它从应用程序中实时获取时间序列数据,然后通过功能强大的规则引擎,帮助你识别监控环境所需的信息

1.1 什么是监控

  • 监控将系统和应用程序生成的指标转换为对应的业务价值。你的监控系统会将这些指标转换为衡量用户体验的依据,该依据为业务提供反馈,以确保为客户提供了所需的产品。同时该依据还提供了对技术的反馈,指出哪些组件不起作用或者导致服务质量下降
  • 监控系统有以下两个“客户”
  1. 技术
  2. 业务
1.1.1 技术作为客户
  • 通过监控来了解技术环境状况,还可以帮助检测、诊断和解决技术环境中的故障和问题(尤其是在影响用户之前)。监控提供了大量的数据,帮助洞察关键的产品和技术决策,并衡量这些项目是否成功。监控也是产品管理生命周期以及与内部客户关系的基础,有助于验证项目资金是否得到充分利用。如果没有监控,那么最好的情况是没有问题发生,最糟糕的情况则是问题发生了但没有被发现
1.1.2 业务作为客户
  • 监控可以提供报告,使企业能够进行良好的产品和技术投资,还有助于企业衡量技术带来的价值

1.2 监控基础知识

  • 监控是管理基础设施和业务的核心工具。监控也是必需的,应该和应用程序一起构建和部署
1.2.1 事后监控
  • 对于任何应用程序开发方法,在构建之前确定要构建的内容都是个好主意。遗憾的是,有一种常见的反模式,即将监控和其他运维工作(比如安全性)视为应用程序的增值组件而非核心功能
  • 服务层级(图)
1.2.2 机械式监控
  • 团队始终复用他们过去使用的检查机制,而不会为新系统或应用程序进行更新。一个常见的例子是监控每台主机上的CPU、内存和磁盘,但不监控可以指示主机上应用程序是否正常运行的关键服务
  • 根据服务价值设计自上而下的监控系统是一个很好的方式,这会帮助明确应用程序中更有价值的部分,并优先监控这些内容,再从技术堆栈中依次向下推进
  • 监控设计(图)
  • 如果无法从业务指标开始,则可试着从靠近用户侧的地方开始监控。因为他们才是最终的客户,他们的体验是推动业务发展的动力,了解他们的体验并发现他们何时遇到问题本身就很有价值
1.2.3 不够准确的监控
  • 这个反模式的一个常见形式是虽然监控了主机上的服务状态,但不够准确。例如,通过检查HTTP200状态码可以监控Web应用程序是否正常运行,它会告诉你应用程序正在响应请求,但并不会反映出是否返回了正确的数据
1.2.4 静态监控
  • 另一种反模式是使用静态阈值——例如,如果主机的CPU使用率超过80%就发出警报。这种检查通常是不灵活的布尔逻辑或者一段时间内的静态阈值,它们通常会匹配特定的结果或范围,这种模式没有考虑到大多数复杂系统的动态性。阈值的匹配或许很重要,但它可能由异常事件触发,甚至可能是自然增长的结果
1.2.5 不频繁的监控
  • 你应该频繁地监控应用程序,以获得以下好处
  1. 识别故障或异常
  2. 满足响应时间预期——你绝对希望在用户报告故障之前找到问题
  3. 提供更细颗粒度的数据,以识别性能的问题和趋势
1.2.6 缺少自动化或自服务
  • 监控系统很差或者没能正确实施的常见原因是它很难实现。如果你让应用程序开发人员觉得监测应用程序、收集数据或可视化很难完成,那么他们可能就不会去做这些事
  • 应尽可能使监控系统的实施和部署自动化
  1. 应该由配置管理进行部署
  2. 主机和服务的配置应该通过自动发现或自助提交来进行,这样可以自动监控新的应用程序,而不需要人为添加
  3. 添加检测应该很简单,并且是基于插件模式,开发人员应该能够把它放置到库中,而不必自己配置它
  4. 数据和可视化应该是自服务的。每个需要查看监控输出的人都应该能够查询和可视化这些内容。(这并不是说你不应该为人们构建仪表板,而是如果他们想要更多,他们就不应该问你。)
1.2.7 监控模式总结
  • 一个良好的监控系统应该能提供以下内容
  1. 全局视角,从最高层(业务)依次展开
  2. 协助故障诊断
  3. 作为基础设施、应用程序开发和业务人员的信息源
  4. 内置于应用程序设计、开发和部署的生命周期中
  5. 尽可能自动化,并提供自服务
1.3 监控机制
  • 从单元测试到检查清单(checklist)的所有事情都是监控的某种形式
1.3.1 探针和内省
  • 监控应用程序主要有两种方法
探针(probing)
  • 探针监控是在应用程序的外部,它查询应用程序的外部特征:监听端口是否有响应并返回正确的数据或状态码
内省(introspection)
  • 内省监控主要查看应用程序内部的内容。应用程序经过检测,并返回其状态、内部组件,或者事务和事件性能的度量
  • 内省监控可以直接将事件、日志和指标发送到监控工具,也可以将信息发送给状态或健康检查接口,然后由监控工具收集
1.3.2 拉取和推送
  • 关于这些优点和缺点,监控领域内部存在相当大的争议,但就许多用户而言,这些辩论没有实际意义。Prometheus主要是一个基于拉取的系统,但它也支持接收推送到网关的事件
1.3.3 监控数据的类型
  • 监控工具可以收集各种不同类型的数据,这些数据主要有两种形式
  1. 指标:大多数现代监控工具都非常依赖指标来帮助我们了解系统的情况。指标存储为时间序列数据,用于记录应用程序度量的状态
  2. 日志:日志是从应用程序发出的(通常是文本的)事件。虽然有助于让你知道发生了什么,但它们通常对故障诊断和调查最有帮助

1.4 指标

  • 指标似乎始终是任何监控体系结构中最直接的部分。然而,有时候我们并没有投入足够的时间来理解我们正在收集的内容,为什么要收集它们,以及我们对这些指标做了些什么
  • Prometheus改变了“指标作为补充”的观念,指标变成了监控工作流程中最重要的部分。Prometheus颠覆了以故障检测为中心的模型,指标用来反映环境的状态、可用性以及性能
1.4.1 什么是指标
  • 由于指标和度量对监控框架至关重要
  • 指标是软件或硬件组件属性的度量。为了使指标有价值,我们会跟踪其状态,通常记录一段时间内的数据点。这些数据点称为观察点(observation),观察点通常包括值、时间戳,有时也涵盖描述观察点的一系列属性(如源或标签)。观察的集合称为时间序列
1.4.2 指标类型
测量型
  • 测量型(gauge),这种类型是上下增减的数字,本质上是特定度量的快照
计数型
  • 计数型(counter),这种类型是随着时间增加而不会减少的数字
  • 直方图(histogram)是对观察点进行采样的指标类型,可以展现数据集的频率分布。将数据分组在一起并以这样的方式显示,这个被称为“分箱”(binning)的过程可以直观地查看数值的相对大小
  • 直方图指标示例
1.4.3 指标摘要
  • 通常来说,单个指标对我们价值很小,往往需要联合并可视化多个指标,这其中需要应用一些数学变换。例如,我们可能会将统计函数应用于指标或指标组
  1. 计数:计算特定时间间隔内的观察点数
  2. 求和:将特定时间间隔内所有观察点的值累计相加
  3. 平均值:提供特定时间间隔内所有值的平均值
  4. 中间数:数值的几何中点,正好50%的数值位于它前面,而另外50%则位于它后面
  5. 百分位数:度量占总数特定百分比的观察点的值
  6. 标准差:显示指标分布中与平均值的标准差,这可以测量出数据集的差异程度。标准差为0表示数据都等于平均值,较高的标准差意味着数据分布的范围很广
  7. 变化率:显示时间序列中数据之间的变化程度
1.4.4 指标聚合
  • 你可能经常希望能看到来自多个源的指标的聚合视图,例如所有应用程序服务器的磁盘空间使用情况。指标聚合最典型的样式就是在一张图上显示多个指标,这有助于你识别环境的发展趋势
平均值
  • 平均值是标准的指标分析方法。实际上,几乎所有曾经监控或分析过网站及应用程序的人都会使用平均值
  • 平均值假设事件都是正常的或者说你的数据是正态(或高斯)分布的——例如,在我们的平均响应时间中,假设所有事件以相同的速度运行或响应时间分布大致为钟形曲线,但应用程序很少出现这种情况。事实上,有个古老的统计学笑话,一位统计学家跳进平均深度只有10英寸(约25厘米)的湖中,然后差点被淹死……
  • 平均值的缺陷(图)
中间数
  • 中间数处在所有数值的正中心:正好50%的数值位于它前面,而另外50%则位于它后面。如果有奇数项个值,则处于中间位置的值即为中间数
  • 你可能又发现这里的问题了,就像平均值一样,当数据分布呈钟形曲线时,中间数效果最好,但在真实环境中这是不现实的
标准差
  • 标准差衡量数据集的变化或分布。标准差为0表示大部分数据接近平均值,标准差越大意味着数据越分散。标准差由正或负加上sigma符号表示,例如,1 sigma表示与平均值有一个标准差
  • 在正态分布中,有一种简单的方式来阐明分布:经验法则,也称为68-95-99.7法则或3 sigma法则(如图1-13所示)。法则指出,一个标准差或1到–1代表平均值两边68.27%的数据,两个标准差或2到–2代表95.45%,而三个标准差则代表99.73%
百分位数
  • 百分位数度量的是占总数特定百分比的观察点的值。从本质上讲,它们会展示数据集的分布。例如,一个事务的99百分位数为10毫秒,这很容易理解:99%的事务在10毫秒或更短时间内完成,1%的事务处理时间超过10毫秒
  • 百分位数是识别异常值的理想选择。如果响应时间小于10毫秒表示你网站上的一个良好体验,那么99%的用户都是这样的——但其中1%的用户没有。一旦意识到这一点,你就可以专注于解决造成那1%的性能问题
  • 然而,百分位数并不是完美的。我们建议绘制几种指标组合,以获得更清晰的数据图。例如,在测量延迟时,最好可以展示以下几项内容
  1. 50百分位数(或中间数)
  2. 99百分位数
  3. 最大值
  • 当开始构建检查和收集指标时,我们会应用百分位数和其他聚合指标

1.5 监控方法论

  • Brendan GreggUSEUtilizationSaturationError)方法[1],侧重于主机级监控
  1. resource: all physical server functional components (CPUs, disks, busses, ...)
  2. utilization: the average time that the resource was busy servicing work
  3. saturation: the degree to which the resource has extra work which it can't service, often queued
  4. errors: the count of error events 1.5.1 USE方法
    • 我们将这些定义结合起来创建一份资源清单,并采用一种方法来监控每个要素:使用率、饱和度和错误
    • 在这个示例中,我们将从CPU开始
    • USE是使用率(Utilization)、饱和度(Saturation)和错误(Error)的缩写,该方法是由Netflix的内核和性能工程师Brendan Gregg开发的。USE方法建议创建服务器分析清单,以便快速识别问题
    • USE方法可以概括为:针对每个资源,检查使用率、饱和度和错误。该方法对于监控那些受高使用率或饱和度的性能问题影响的资源来说是最有效的
    • Google的四个黄金指标,专注于应用程序级监控
    • 结合使用可以获得一个相当全面的环境视图,帮助你解决任何问题
    1. 资源:系统的一个组件。在Gregg对模型的定义中,它是一个传统意义上的物理服务器组件,如CPU、磁盘等,但许多人也将软件资源包含在定义中
    2. 使用率:资源忙于工作的平均时间。它通常用随时间变化的百分比表示
    3. 饱和度:资源排队工作的指标,无法再处理额外的工作。通常用队列长度表示
    4. 错误:资源错误事件的计数
CPU
  • CPU使用率随时间的百分比
  • CPU饱和度,等待CPU的进程数
  • 错误,通常对CPU资源不太有影响
内存
  • 内存使用率随时间的百分比
  • 内存饱和度,通过监控swap测量
  • 错误,通常不太关键,但也可以捕获
  • 检查清单:http://www.brendangregg.com/USEmethod/use-linux.html 1.5.2 Google的四个黄金指标
    • Weaveworks团队开发了一个名为REDRateErrorDuration)的相关框架,你可能也会对此感兴趣(https://rancher.com/red-method-for-prometheus-3-key-metrics-for-monitoring/)
    • Google的四个黄金指标来自Google SRE手册,它们采用与USE类似的方法,指定要监控的一系列通用指标类型
    • 主要关注的不是系统级的时间序列数据,更多是针对应用程序或面向用户的部分
    1. 延迟:服务请求所花费的时间,需要区分成功请求和失败请求。例如,失败请求可能会以非常低的延迟返回错误结果
    2. 流量:针对系统,例如,每秒HTTP请求数,或者数据库系统的事务
    3. 错误:请求失败的速率,要么是HTTP500错误等显式失败,要么是返回错误内容或无效内容等隐式失败,或者基于策略原因导致的失败——例如,强制要求响应时间超过30ms的请求视为错误
    4. 饱和度:应用程序有多“满”,或者受限的资源,如内存或IO。这还包括即将饱和的部分,例如正在快速填充的磁盘

1.6 警报和通知

  • 要建立一个出色的通知系统,需要考虑以下基础信息
  1. 哪些问题需要通知
  2. 谁需要被告知
  3. 如何告知他们
  4. 多久告知他们一次
  5. 何时停止告知以及何时升级到其他人
  • 如果配置不当,导致生成了过多通知,那么人们将无法对它们采取任何行动,甚至可能将它们忽略掉
  • 最重要的是,你需要考虑通知内容。通常当出现问题或者有事件需要你注意时,通知是唯一的途径。它们需要简洁、清晰、准确,易于理解并且可操作。设计有价值、有意义的通知至关重要
  • 在我们的框架中,将重点关注以下内容
  1. 使通知清晰、准确、可操作。使用由人而不是计算机编写的通知在清晰度和实用性方面有显著差异
  2. 为通知添加上下文。通知应包含组件的其他相关信息
  3. 仅发送有意义的通知
  • 在这里给出的最简单的建议是记住“通知是供人而不是计算机阅读的”,请用心地设计它们

1.7 可视化

  • 数据可视化既是一门非常强大的分析和解释技术,也是一种令人惊叹的学习工具。指标及其可视化通常很难解释
  • 理想的可视化应该能够清晰地显示数据,突出重点而不仅是提高视觉效果。我们会按照以下规则进行构建
  1. 清晰地显示数据
  2. 引发思考(而不是视觉效果)
  3. 避免扭曲数据
  4. 使数据集保持一致
  5. 允许更改颗粒度而不影响理解

1.9 本书内容

  • 本书会介绍关于监控的方法,然后使用Prometheus来实例化这些监控方法。在本书的最后,你应该可以搭建出一个高度可扩展的监控平台

1.10 小结

  • 我们介绍了现代监控方法,列举了几种监控实现的细节。我们讨论了监控的最佳实践和反模式,以及如何避免反模式的设计

资料

  • The Packer Book
  • The Terraform Book
  • The Art of Monitoring
  • The Docker Book
  • The Logstash Book
  • The Art of Monitoring
  • Pro Puppet
  • Pulling Strings with Puppet
  • Pro Linux System Administration
  • Pro Nagios2.0》
  • Hardening Linux
  • The Visual Display of Quantitative Information
  • 文章 https://www.datadoghq.com/blog/timeseries-metric-graphs-101/
  • 代码:https://github.com/turnbullpress/prometheusbook-code

第2章 Prometheus简介


2.1 Prometheus起源

  • Prometheus的灵感来自谷歌的Borgmon。它最初由前谷歌SRE Matt T.Proud开发,并转为一个研究项目。在Proud加入SoundCloud之后,他与另一位工程师Julius Volz合作开发了Prometheus。后来其他开发人员陆续加入了这个项目,并在SoundCloud内部继续开发,最终于2015年1月将其发布
  • Facebook发现85%的查询是针对26小时内的数据

2.2 Prometheus架构

  • Prometheus通过抓取或拉取应用程序中暴露的时间序列数据来工作。时间序列数据通常由应用程序本身通过客户端库或称为exporter(导出器)的代理来作为HTTP端点暴露
  • Prometheus还有一个推送网关(push gateway)[3],可用于接收少量数据——例如,来自无法拉取的目标数据(如临时作业或者防火墙后面的目标)
  • Prometheus架构(图)
资料
  • [1] https://github.com/Lusitaniae/apache_exporter
  • [2] https://github.com/prometheus/mysqld_exporter
  • [3] https://github.com/prometheus/pushgateway 2.2.1 指标收集 2.2.2 服务发现 2.2.3 聚合和警报 2.2.4 查询数据 2.2.5 自治 2.2.6 冗余和高可用性 2.2.7 可视化

2.3 Prometheus数据模型 2.3.1 指标名称 2.3.2 标签 2.3.3 采样数据 2.3.4 符号表示 2.3.5 保留时间


2.4 安全模型


2.5 Prometheus生态系统 链接


2.6 参考链接

  • 官网:https://prometheus.io/
  • 文档:https://prometheus.io/docs/
  • GitHub主页:https://github.com/prometheus
  • GitHub源码:https://github.com/prometheus/prometheus
  • 参考视频:大规模Prometheus和时间序列设计(https://www.youtube.com/watch?v=gNmWzkGViAY
  • Grafana官网:https://grafana.com

  • [1] https://prometheus.io/docs/instrumenting/exporters/
  • [2] https://prometheus.io/docs/instrumenting/clientlibs/。(Prometheus客户端库)
  • 生态系统的核心是Prometheus服务器,此外还有Alertmanager,它为Prometheus提供警报引擎并进行管理
  • Prometheus项目还包括一系列exporter<sup>[1]</sup>,用于监控应用程序和服务,并在端点上公开相关指标以进行抓取。核心exporter支持常见工具,如Web服务器、数据库等。许多其他exporter都是开源的,你可以从Prometheus社区查看
  • Prometheus2.0开始,默认情况下某些HTTP API的管理功能被禁用
  • Prometheus可以通过多种方式进行配置和部署,关于安全有以下两个假设
  • Prometheus专为短期监控和警报需求而设计。默认情况下,它在其数据库中保留15天的时间序列数据。如果要保留更长时间的数据,则建议将所需数据发送到远程的第三方平台
  • 时间序列表示为符号(notation
  • 时间序列示例
  • 首先是时间序列名称,后面跟着一组键/值对标签。通常所有时间序列都有一个instance标签(标识源主机或应用程序)以及一个job标签(包含抓取特定时间序列的作业名称)
  • 时间序列的真实值是采样(sample)的结果
  • 时间序列由名称和标签标识(尽管从技术上讲,名称本身也是名为__name__的标签)
  • 带有__前缀的标签名称保留给Prometheus内部使用
  • 标签为Prometheus数据模型提供了维度
  • 标签共有两大类
  • 时间序列名称[1]通常描述收集的时间序列数据的一般性质——例如,website_visits_total为网站访问的总数
  • Prometheus收集时间序列数据。为了处理这些数据,它使用一个多维时间序列数据模型。这个时间序列数据模型结合了时间序列名称和称为标签(label)的键/值对,这些标签提供了维度。每个时间序列由时间序列名称和标签的组合唯一标识
  • 可视化通过内置表达式浏览器提供,并与开源仪表板Grafana集成。此外,Prometheus也支持其他仪表板
  • 冗余和高可用性侧重弹性而非数据持久性。Prometheus团队建议将Prometheus服务器部署到特定环境和团队,而不是仅部署一个单体Prometheus服务器
  • Prometheus冗余架构(图)
  • 每个Prometheus服务器都设计为尽可能自治,旨在支持扩展到数千台主机的数百万个时间序列的规模。数据存储格式被设计为尽可能降低磁盘的使用率,并在查询和聚合期间快速检索时间序列
  • Prometheus服务器还提供了一套内置查询语言PromQL、一个表达式浏览器(如图2-2所示)以及一个用于浏览服务器上数据的图形界面
  • Prometheus表达式浏览器(图)
  • Prometheus还可以定义警报规则。这些是为系统配置的在满足条件时触发警报的标准,例如,资源时间序列开始显示异常的CPU使用率。Prometheus服务器没有内置警报工具,而是将警报从Prometheus服务器推送到名为Alertmanager(警报管理器)[1]的单独服务器。Alertmanager可以管理、整合和分发各种警报到不同目的地
  • https://prometheus.io/docs/alerting/alertmanager/
  • Prometheus称其可以抓取的指标来源为端点(endpoint)。端点通常对应单个进程、主机、服务或应用程序。为了抓取端点数据,Prometheus定义了名为目标(target)的配置
  1. 不受信任的用户将能够访问Prometheus服务器的HTTP API,从而访问数据库中的所有数据
  2. 只有受信任的用户才能访问Prometheus命令行、配置文件、规则文件和运行时配置
  3. 一个float64类型的数值
  4. 一个毫秒精度的时间戳
  5. 插桩标签(instrumentation label):插桩标签来自被监控的资源——例如,对于与HTTP相关的时间序列,标签可能会显示所使用的特定HTTP动词。这些标签在由诸如客户端或exporter抓取之前会被添加到时间序列中
  6. 目标标签(target label):目标标签更多地与架构相关——它们可能会识别时间序列所在的数据中心。目标标签由Prometheus在抓取期间和之后添加
  7. 用户提供的静态资源列表
  8. 基于文件的发现。例如,使用配置管理工具生成在Prometheus中可以自动更新的资源列表
  9. 自动发现。例如,查询Consul等数据存储,在AmazonGoogle中运行实例,或使用DNS SRV记录来生成资源列表

第3章 安装和启动Prometheus


3.1 安装Prometheus

  • 如果要将Prometheus部署到生产环境或进行扩展,则应该始终选择配置管理工具作为安装方法
  • 下载地址:https://prometheus.io/download/
3.1.4 在Mac OS X上安装Prometheus
代码语言:javascript
复制
$ brew install prometheus
3.1.5 通过监控套件安装Prometheus
  • 使用Docker Compose安装PrometheusNode ExporterGrafana<sup>[1]</sup>
  • 使用Docker Compose单节点安装PrometheusAlertmanagerNode ExporterGrafana<sup>[2]</sup>
  • [1]https://github.com/vegasbrianc/prometheus
  • [2]https://github.com/danguita/prometheus-monitoring-stack 3.1.6 通过配置管理工具安装Prometheus
    • 使用配置文件来构建自己的部署并将Prometheus打包成服务:https://github.com/kayrus/prometheus-kubernetes
    • 使用CoreOSPrometheus Operatorhttps://github.com/coreos/prometheus-operator

    3.2 配置Prometheus

    • YAML配置非常复杂,可能让人很痛苦,建议你通过YAML Linthttp://www.yamllint.com/)在线方式或者在命令行使用yamllinthttps://github.com/adrienverge/yamllint)来验证YAML配置文件
    • 我们的默认配置文件中定义了4个YAML
    • PuppetPrometheus模块:https://forge.puppet.com/puppet/prometheus
    • ChefPrometheus cookbookhttps://supermarket.chef.io/cookbooks/prometheus-platform
    • AnsiblePrometheus rolehttps://github.com/cloudalchemy/ansible-prometheus
    • SaltStackPrometheus formulahttps://github.com/arnisoph/saltstack-prometheus-formula 3.1.7 通过Kubernetes安装Prometheus
    1. global
    2. alerting
    3. rule_files
    4. scrape_configs 3.2.1 global

    3.2.2 alerting

    3.2.3 rule_files

    3.2.4 scrape_configs

3.3 启动Prometheus

  • 配置文件prometheus.yml global: scrape_interval: 15s evaluation_interval: 15s alerting: alertmanagers: - static_configs: - targets: # - alertmanager:9093 rule_files: # - "first_rules.yml" # - "second_rules.yml" scrape_configs: - job_name: 'prometheus' static_configs: - targets: ['localhost:9090']
    • 如果发生异常,则可以使用promtool来验证配置文件,这是Prometheus附带的一个代码校验工具
    • 通过Docker运行Prometheus docker run -p 9090:9090 -v /tmp/prometheus.yml:/etc/prometheus/prometheus.yml prom/prometheuslevel=info ts=2019-11-07T14:51:10.156Z caller=main.go:296 msg="no time or size retention was set so using the default time retention" duration=15dlevel=info ts=2019-11-07T14:51:10.156Z caller=main.go:332 msg="Starting Prometheus" version="(version=2.13.1, branch=HEAD, revision=6f92ce56053866194ae5937012c1bec40f1dd1d9)"level=info ts=2019-11-07T14:51:10.156Z caller=main.go:333 build_context="(go=go1.13.1, user=root@88e419aa1676, date=20191017-13:15:01)"level=info ts=2019-11-07T14:51:10.156Z caller=main.go:334 host_details="(Linux 4.9.184-linuxkit #1 SMP Tue Jul 2 22:58:16 UTC 2019 x86_64 765454899e9c (none))"level=info ts=2019-11-07T14:51:10.156Z caller=main.go:335 fd_limits="(soft=1048576, hard=1048576)"level=info ts=2019-11-07T14:51:10.156Z caller=main.go:336 vm_limits="(soft=unlimited, hard=unlimited)"level=info ts=2019-11-07T14:51:10.158Z caller=main.go:657 msg="Starting TSDB ..."level=info ts=2019-11-07T14:51:10.159Z caller=web.go:450 component=web msg="Start listening for connections" address=0.0.0.0:9090level=info ts=2019-11-07T14:51:10.161Z caller=head.go:514 component=tsdb msg="replaying WAL, this may take awhile"level=info ts=2019-11-07T14:51:10.161Z caller=head.go:562 component=tsdb msg="WAL segment loaded" segment=0 maxSegment=0level=info ts=2019-11-07T14:51:10.162Z caller=main.go:672 fs_type=EXT4_SUPER_MAGIClevel=info ts=2019-11-07T14:51:10.162Z caller=main.go:673 msg="TSDB started"level=info ts=2019-11-07T14:51:10.162Z caller=main.go:743 msg="Loading configuration file" filename=/etc/prometheus/prometheus.ymllevel=info ts=2019-11-07T14:51:10.165Z caller=main.go:771 msg="Completed loading of configuration file" filename=/etc/prometheus/prometheus.ymllevel=info ts=2019-11-07T14:51:10.165Z caller=main.go:626 msg="Server is ready to receive web requests."3.4 第一个指标
    • 磁盘使用量受存储的时间序列数量和这些时间序列的保留时间限制。默认情况下,指标会在本地时间序列数据库中存储15天。数据库的位置和保留时间由命令行选项控制
    • --storage.tsdb.path选项:它的默认数据目录位于运行Prometheus的目录中,用于控制时间序列数据库位置
    • --storage.tsdb.retention选项:控制时间序列的保留期。默认值为15d,代表15天
    • 建议采用SSD作为时间序列数据库的磁盘。对于每秒10万个样本的示例,我们知道按时间序列收集的每个样本在磁盘上占用大约1到2个字节。假设每个样本有2个字节,那么保留15天的时间序列意味着需要大约259 GB的磁盘
    • 有关Prometheus磁盘使用情况的更多信息,请参见存储文档(https://prometheus.io/docs/prometheus/latest/storage/)
    • 你还需要考虑在查询和记录规则方面的内存使用情况。这个不太好计算,并且依赖于许多其他变量,建议根据内存使用情况灵活调整。你可以通过检查process_resident_memory_bytes指标来查看Prometheus进程的内存使用情况
    • 这里使用sum聚合来计算所有匹配的指标的计数和,使用=~运算符和.+的正则表达式来匹配所有指标
    • 每个样本的大小通常为1到2个字节,让我们谨慎一点,按照2个字节计算。假设在12小时内每秒收集100 000个样本,那我们可以像下面这样计算内存使用情况:结果大概是8.64GB的内存
    • 这将显示你在最后一分钟添加到数据库的每秒样本率。如果想知道收集的指标数量
    • Prometheus在内存中做了很多工作。每个收集的时间序列、查询和记录规则都会消耗进程内存。但一个有用的、粗略的经验法则是将每秒采集的样本数乘以样本的大小。我们可以使用以下查询语句来查看样本收集率
    • Prometheus的性能很难估计,因为它在很大程度上取决于你的配置、所收集的时间序列的数量以及服务器上规则的复杂性。一般容量规划关注两个问题:内存和磁盘
    • 如果在构建PromQL查询方面需要帮助,则可以使用查询编辑器Promeditor,它可以在Prometheus本地运行(https://github.com//davkal/promeditor
    • 还可以进行指标的复杂聚合。我们选择promhttp_metric_handler_requests_total指标来看
    • 使用sum()运算符累加,但没有按作业分类,子句by它允许按特定维度聚合
    • rate()函数用来计算一定范围内时间序列的每秒平均增长率,只能与计数器一起使用,最适合用于增长较慢的计数器或用于警报的场景
    • 计算5分钟范围向量的速率。范围向量(range vector)是第二个PromQL数据类型,它包含一组时间序列,其中每个时间序列都包含一系列数据点。范围向量允许我们显示该时间段的时间序列,持续时间被包含在中括号[ ]中,内容是一个整数值后跟一个单位缩写,其中单位缩写
    • 看看另一个指标prometheus_build_info,它包含有关Prometheus服务器的构建信息
    • 可以看到这个指标使用了大量的标签,并且指标的值为1。这是通过指标将信息传递到Prometheus服务器的常见模式,它使用恒定值为1的指标,并且添加了你可能希望通过标签附加的相关信息
    • 浏览http://localhost:9090/graph来获得
    • 这个指标的名称是go_gc_duration_seconds,里面有一个标签quantile="0.5",表示这衡量的是第50百分位数,后面的数字是这个指标的值
    • 查看http://localhost:9090/metrics内容
    • 启动Prometheus prometheus --config.file ./prometheus.ymllevel=info ts=2019-11-07T14:45:47.188Z caller=main.go:293 msg="no time or size retention was set so using the default time retention" duration=15dlevel=info ts=2019-11-07T14:45:47.188Z caller=main.go:329 msg="Starting Prometheus" version="(version=2.11.1, branch=non-git, revision=non-git)"level=info ts=2019-11-07T14:45:47.188Z caller=main.go:330 build_context="(go=go1.12.7, user=brew@Mojave-2.local, date=20190711-02:47:26)"level=info ts=2019-11-07T14:45:47.188Z caller=main.go:331 host_details=(darwin)level=info ts=2019-11-07T14:45:47.188Z caller=main.go:332 fd_limits="(soft=4864, hard=unlimited)"level=info ts=2019-11-07T14:45:47.188Z caller=main.go:333 vm_limits="(soft=unlimited, hard=unlimited)"level=info ts=2019-11-07T14:45:47.189Z caller=main.go:652 msg="Starting TSDB ..."level=info ts=2019-11-07T14:45:47.189Z caller=web.go:448 component=web msg="Start listening for connections" address=0.0.0.0:9090level=info ts=2019-11-07T14:45:47.195Z caller=main.go:667 fs_type=19level=info ts=2019-11-07T14:45:47.195Z caller=main.go:668 msg="TSDB started"level=info ts=2019-11-07T14:45:47.195Z caller=main.go:738 msg="Loading configuration file" filename=./prometheus.ymllevel=info ts=2019-11-07T14:45:47.707Z caller=main.go:766 msg="Completed loading of configuration file" filename=./prometheus.ymllevel=info ts=2019-11-07T14:45:47.707Z caller=main.go:621 msg="Server is ready to receive web requests."
    1. s:秒
    2. m:分钟
    3. h:小时
    4. d:天
    5. w:周

第4章 监控主机和容器

  • 首先,我们将在每台主机上安装exporter,然后配置节点和Docker指标让Prometheus来抓取。基本主机资源监控
  1. CPU
  2. 内存
  3. 磁盘
  4. 可用性
  • 然后,我们利用收集的指标来构建一些聚合指标并保存为记录规则
  • 最后,我们会简要介绍Grafana,并对收集的数据进行基本的可视化
  • 回顾USE
  1. 使用率(Utilization
  2. 饱和度(Saturation
  3. 错误(Error
  • USE方法可以概括为:针对每个资源,检查使用率、饱和度和错误。该方法对于监控那些受高使用率或饱和度的性能问题影响的资源来说是最有效的
  1. 资源:系统的一个组件。它是一个传统意义上的物理服务器组件,如CPU、磁盘等,但许多人也将软件资源包含在定义中
  2. 使用率:资源忙于工作的平均时间。它通常用随时间变化的百分比表示
  3. 饱和度:资源排队工作的指标,无法再处理额外的工作。通常用队列长度表示
  4. 错误:资源错误事件的计数

4.1 监控节点

  • Prometheus使用exporter工具来暴露主机和应用程序上的指标,目前有很多可用于各种目的的exporterhttps://prometheus.io/docs/instrumenting/exporters/)
Exporter
  • Third-party exporters
    • 现在我们将专注于一个特定的exporterNode Exporterhttps://github.com/prometheus/node_exporter),它还有一个textfile收集器,允许你导出静态指标
    • 首先选择其中一台Linux主机,然后下载并安装Node Exporter。我们将选择一个Docker守护进程主机
    • 如果你不想使用exporter,那么还有很多支持Prometheus的主机监控客户端。例如,collectd也可以收集Prometheus指标(https://collectd.org/wiki/index.php/Plugin:Write_Prometheus
    • 使用配置管理工具是运行和安装exporter的最佳方式。这种方式可以轻松地管理配置,并提供自动化和服务管理
    • 默认情况下,node_exporter在端口9100上运行,并在路径/metrics上暴露指标。你可以通过--web.listen-address--web.telemetry-path参数来设置端口和路径
    • 查看数据
    • 许多收集器默认都是启用的。它们的状态要么是启用要么是禁用,可通过使用no-前缀来修改状态。(https://github.com/prometheus/node_exporterEnabled by defaultDisabled by default),如
    • 我们还想配置一个特定的收集器,即textfile收集器,textfile收集器非常有用,因为它允许我们暴露自定义指标。这些自定义指标可能是批处理或cron作业 等无法抓取的,可能是没有exporter的源,甚至可能是为主机提供上下文的表态指标
    • 收集器通过扫描指定目录中的文件,提取所有格式为Prometheus指标的字符串,然后暴露它们以便抓取
    • 现在在这个目录中创建一个新的指标。刚创建的目录中,指标在以.prom结尾的文件内定义,并且使用Prometheus特定文本格式
    • 我们使用此格式创建一个包含有关此主机的元数据指标
    • 可以看到它包含一个指标名称(metadata)和两个标签。一个标签role定义节点的角色。在示例中,标签的植 为docker_server。另一个标签datacenter定义主机的地理位置。最后,指标的值 为1,因为它不是计数型、测量型或计时型的指标,而是提供上下文
    • 要启用textfile收集器,我们不需要配置参数,它默认就会被加载。但我们需要指定textfile_exporter目录,以便Node Exporter知道在哪里可以找到自定义指标。为此,我们需要指定--collector.textfile.directory参数
    • 只收集以下服务指标
    • 使用--collector.systemd.unit-whitelist参数配置,它会匹配systemd正则表达式
    • 第一台服务器
    • 结果
    • 第二台服务器
    • 结果
    • 让我们配置一个新作业来抓取Node Exporter导出的数据。查看当前prometheus.yml文件和抓取配置文件的scrape_configs部分,Node Exporter默认抓取路径是/metrics,重启Prometheus服务器
    • Prometheus假设Node Exporter具有默认路径/metrics,并且抓取的目标形式如下:
    • 如果现在使用SIGHUP或重新启动Prometheus服务器,那么我们的配置将被重新加载,并且服务器也会开始抓取。我们很快就会看到时间序列数据开始流入Prometheus服务器
    • Prometheus还提供了一种方式来限制收集器从服务器端实际抓取的数据,尤其是在你无法控制正抓取的主机的配置很有用
    • 过滤惧器示例
    • 使用params块中collect[]列表指定,然后将它们作为URL参数传递给抓取请求。你可以使用Node Exporter实例上的curl命令来对此进行测试(只收集cpu指标,其它指标忽略)
    • 推荐的方法是使用GooglecAdvisor工具。在Docker守护进程上,cAdvisor作为Docker容器运行,单个cAdvisor容器返回针对Docker守护进程和所有正在运行的容器的指标。Prometheus支持通过它导出指标,并将数据传输到其他各种存储系统,如InfluxDBElasticsearchKafka 4.2.1 运行cAdvisor docker run --volume=/:/rootfs:ro \ --volume=/var/run:/var/run:rw \ --volume=/sys:/sys:ro \ --volume=/var/lib/docker:/var/lib/docker:ro \ --volume=/dev/disk:/dev/disk:ro \ --publish=8080:8080 \ --detach=true \ --name=cadvisor \ google/cadvisor:latest

    4.2.2 抓取cAdvisor scrape_configs: - job_name: 'prometheus' static_configs: - targets: ['localhost:9090'] - job_name: 'node' static_configs: - targets: ['138.197.26.39:9100', '138.197.30.147:9100', '138.197.30.163:9100'] - job_name: 'docker' static_configs: - targets: ['138.197.26.39:8080']


    4.3 抓取的生命周期 scrape_configs: - job_name: 'node' scheme: https metrics_path: /moremetrics static_configs: - targets: ['138.197.26.39:9100', '138.197.30.147:9100', '138.197.30.163:9100']


    4.4 标签 更改或添加标签会创建新的时间序列 4.4.1 标签分类 4.4.2 重新标记 - job_name: 'docker' static_configs: - targets: ['138.197.26.39:8080', '138.197.30.147:8080', '138.197.30.163:8080'] metric_relabel_configs: - source_labels: [__name__] regex: '(container_tasks_state|container_memory_failures_total)' action: drop 删除指标 - job_name: 'docker' static_configs: - targets: ['138.197.26.39:8080', '138.197.30.147:8080', '138.197.30.163:8080'] metric_relabel_configs: - source_labels: [__name__] separator: ',' regex: '(container_tasks_state|container_memory_failures_total)' action: drop regex1;regex2;regex3 替换标签值 id="/docker/6f823kdj39892jd8j3jid983ujj8edjie99djc883mnbnc8833nccllddklsoow838ckkc8" metric_relabel_configs: - source_labels: [id] regex: '/docker/([a-z0-9]+)' replacement: '$1' target_label: container_id 删除标签 metric_relabel_configs: - regex: '/docker/([a-z0-9]+)' action: labeldrop 文档

    • 现在从4个主机中收集总共7组独立的指标
    • 可通过http://localhost:9090/targets查看抓取目标列表以及每个目标的状态
    • 名为node_cpu_seconds_total的指标是主机上CPU使用率
    • 应该看到一个指标列表,像这样
    • 这里真正想要的是获得每个实例的CPU使用百分比,但要实现这一点,需要稍微处理下指标,可以通过一系列PromQL计算来实现这一结果
    • 首先计算每种CPU模式的每秒使用率。PromQL有一个名为irate的函数,用于计算范围向量中时间序列增加的每秒即时速率。让我们对node_cpu_seconds_total指标使用irate函数,在查询框中输入(表示为5分钟范围内的每秒速率)
    • 可使用avgaverage去处符介绍的by子句
    • 上面指标不太准确,它包括idle的值 ,并且宽没有表示成百分比形式。我们将查询每个实例的idle使用率,它已经是一个比率,将它乘以100转换为百分比
    • 可用100减去这个值 ,结果就是CPU使用率的百分比
    • 将主机上的CPU考虑在内的一段时间内的平均运行队列长度。平均负载少于CPU的数量通常是正常的,长时间内超过该数字的平均值 则表示CPU已饱和
    • 我们需要计算主机上的CPU数量,可使用count聚合实现
    • idlemode计算node_cpu_seconds_total时间序列出现次数,然后使用by子句从结果向量中删除instance之外的所有标签,可以看到3个节点各有2个CPU
    • 然后将此计数与node_load1指标结合起来,如下所示
    • 查询的是1分钟的平均负载超过主机CPU数量的两倍的结果
    • 聚合操作符:https://prometheus.io/docs/prometheus/latest/querying/operators/#aggregation-operators
    • Node Exporter内存指标按内存的类型和使用率进行细分。可以在以node_memory为前缀的指标列表中找到它们
    • 重点关注node_memory指标的一个子集以获取使用率
    • 所有这些指标都是以字节为单位表示的,公式为
    • 我们还可以通过检查内存和磁盘的读写来监控内存饱和度。可以使用从/proc/vmstat收集的两个Node Exporter指标
    • 两者都是自上次启动以来的字节数,以KB为单位
    • 为了获得饱和度指标,我们对每个指标计算一分钟的速率,将两个速率相加,然后乘以1024以获得字节数。我们会创建一个查询来执行此操作
    • 代码清单:内存饱和度查询
    • node_vmstat_pswpin:系统每秒从磁盘读到内存的字节数
    • node_vmstat_pswpout:系统每秒从内存写到磁盘的字节数
    • 对于磁盘,我们只测量磁盘使用情况而不是使用率、饱和度或错误。这是因为在大多数情况下,它是对可视化和警报最有用的数据
    • node_filesystem_size_bytes显示被监控的每个文件系统挂载大小。我们可以使用与内存指标类似的查询来生成在主机上使用的磁盘空间的百分比
    • 使用mountpoint标签文件系统"/"挂载,返回该文件系统的磁盘使用指标
    • 我们更关心的是什么时候耗尽磁盘空间?
    • 实际上Prometheus提供了一种机制,通过一个名为predict_linear的函数,可以构造一个查询来回答这个问题
    • 我们选择一小时的时间窗口[1h],并将此时间序列快照放在predict_linear函数中。该函数使用简单的线性回归,根据以前的增长情况来确定文件系统何时会耗尽空间。该函数参数包括一个范围向量,即一小时窗口,以及未来需要预测的时间点
    • 如果基于最后一小时的增长历史记录,文件系统将在接下来的四小时内用完空间,那么查询将返回一个负数,然后可以使用它来触发警报
    • 服务的状态在node_systemd_unit_state指标中暴露出来。对于收集的每个服务和服务状态都有一个指标
    • 代码清单:指标node_systemd_unit_state
    • 可以通过添加state标签来进一步缩小搜索范围,仅返回active状态的数据
    • 看到了一个新的查询,使用比较运算符"=="。查询将返回值为1、name标签为docker.service的所有指标
    • 比较运算符:https://prometheus.io/docs/prometheus/latest/querying/operators/#comparison-binary-operators
    • 代码清单:up指标
    • 如果实例是健康的,则指标设置为1,即数据抓取成功返回。如果抓取失败,则设置为0
    • 提示:Prometheus还会填充其他一些监控指标,包括scrape_duration_seconds(抓取的持续时间)和scrape_duration_scraped(目标暴露的样本数)
    • 可以查询主机的所有up指标
    • 许多exporter都有特定的指标,旨在确定最后一次成功的数据抓取。例如,cAdvisor指标container_last_seen,它提供容器列表以及它们最近一次活动的时间。MySQL Exporter返回一个指标mysql_up
    • 注意:你不能重新票房自动填充的指标,如up指标,因为它们是在重新标记阶段之后生成的
    • 该指标提供资源的上下文信息,如角色 docker_server,主机的位置datacenter。尽管这些数据本身很有用,但为什么又要创建一个单独的指标而不是仅将作为标签添加到主机的指标中呢?我们已经知道标签提供了时间序列的维度,并且与指标名称相结合,它们构成了时间序列的标识。同时,我们也已经被警告过:
    • 这意味着我们应该谨慎地使用标签,并且应尽可能保持不变
    • 让我们看看如何利用该指标上的标签。假设我们只想从某个特定的数据中心或一组数据中心选择指标。我们可以快速找到所有主机,例如,查询不在新泽西(NJ)的数据中心
    • 我们可以使用metadata指标来进行向量匹配(vector match)。向量匹配可以使用任何PromQL二元运算符。向量匹配尝试针对左侧向量中的每个元素在右侧向量中查找对应的匹配元素
    • 有两种向量匹配:一对一(one-to-one)和多对一(many-to-one,或一对多(one-to-many))
    • 一对一匹配从每一侧找到唯一匹配的条目对。如果两个条目具有完全相同的标签的值 ,则它们是一对一匹配的。你可以考虑使用ignoring修饰符忽略掉特定标签,或者使用on修饰符来减少显示的标签列表
    • 代码清单:一对一向量匹配
    • 将查询所有node_system_unit_state指标中name标签为docker.service并且值为1的指标。然后,我们使用on修饰符将返回的标签列表减少到metadata指标的instancejob标签,并且datacenter标签的值 为SF
    • 这将返回一个指标
    • 多对一和一对多匹配是其中一侧向量中的元素与另一侧微量中的多个元素相匹配。这些匹配使用group_leftgroup_right修饰符显式指定,其中leftright是为了确定哪个向量具有更高的基数。Prometheus文档包含了一些这种匹配的例子,但它们通常不会被使用。大多数情况下,一对一匹配就足够了
    • 向量匹配文档:https://prometheus.io/docs/prometheus/latest/querying/operators/#vector-matching 元数据风格的指标 4.6 查询持久性 4.6.1 记录规则 4.6.2 配置记录规则 global: scrape_interval: 15s evaluation_interval: 15s mkdir -p rules cd rules touch node_rules.yml rule_files: - "rules/node_rules.yml"
    • 让我们把CPU、内存和磁盘计算转换为记录规则。我们有很多要监控的主机,所以我们要对所有节点预先计算这三个指标的查询,这样就可以将这些计算作为指标,然后可以设置警报或者通过Grafana等仪表板进行可视化
    • 代码清单:一个记录规则
    • 记录规则在规则组中定义,这里的规则组叫作node_rules。规则组名称在服务器中必须是唯一的。规则组内的规则以固定间隔顺序执行。默认情况下,这是通过全局evaluate_interval来控制的,但你可以使用interval子句在规则组中覆盖
    • 规则组内规则执行的顺序性质意味着你可以在后续规则中使用之前创建的规则。这允许你根据规则创建指标,然后在之后的规则中重用这些指标。这仅在规则组内适用,规则组是并行运行的,因此不建议跨组使用规则
    • 我们有一个名为rulesYAML块,它包含该组的记录规则。每条规则都包含一条记录,告诉Prometheus将新的时间序列命名为什么。你应该仔细命名规则,以便快速识别它们代表的内容。一般推荐的格式是:
    • 其中level表示聚合级别,以及规则输出的标签。metric是指标名称,除了使用rate()irate()函数剥离_total计数器之外,应该保持不变。这样的命名可以帮助你更轻松地找到新指标。最后,operations是应用于指标的操作列表,一般最新的操作放在前面。
    • 所以我们的CPU查询将命名为
    • expr字段来保存生成新时间序列的查询
    • 添加labels块以向新时间序列添加新标签
    • 代码清单:一个记录规则
    • 代码清单:一个记录规则
    • 通过将SIGHUP信号发送到Prometheus进程,可以在运行时重新加载规则文件。重新加载仅在规则文件格式良好时才有效。Prometheuus服务器附带一个名为promtool的实用程序,可以帮助检测规则文件
    • 如果我们现在搜索一个新的时间序列instance:node_cpu:avg_rate5m,则应该可以看到如下内容
    • 可以在web界面的/rules路径中查看当前服务器上定义的规则
    • Prometheus通常不用于长期数据保留,默认保存15天的时间序列数据。这意味着Prometheus更专注于监控问题,而不是其他可视化和仪表板系统
    • 我们也将快速安装Grafana并将Prometheus连接到上面
    • 代码清单:在Red Hat上获取PackageCloud公钥
    • 然后将以下内容添加到grafana.repo文件内的/etc/yum.repos.d/目录中
    • 现在我们通过yum或者dnf命令来安装Grafana sudo yum install grafana 在Mac OS X上安装Grafana brew install grafana Docker镜像
    • 代码清单:在Linux上启动Grafana sudo service grafana-server start
    • Grafana是一个基于GoWeb应用服务,默认在端口3000上运行。一旦运行,你就可以通过浏览器访问——例如,如果它在本地主机上运行,则可直接访问http://localhost:3000
    • 开始你会看到一个登录界面,默认用户名和密码为adminadmin,你可以通过更新Grafana配置文件的[security]部分来控制
    • 首先要将Grafana与我们的Prometheus数据关联起来,单击入门教程中的"Add data source"
    • 要添加新数据源,我们需要指定一些细节信息。首先,我们需要命名数据源,这里采用Prometheus。接下来,选中Default复选框,告诉Grafana默认搜索该数据源的数据。我们还需要确保将数据源类型设置为Prometheus
    • 需要为数据源指定HTTP设置,这是我们要查询的Prometheus服务器的URL。我们假设GrafanaPrometheus在同一台主机上运行,因此这里使用本地服务器的http://localhost:9090
    • Access选项设置为proxy(代理),但是这并没有为我们的连接配置HTTP代理,不过它告诉Grafana使用自己的Web服务来代理与Prometheus的连接。另一个选项direct,是从Web浏览器直接连接。proxy设置更加实用一些,因为连接部分交由Grafana服务负责
    • 单击"Add"按钮就可以完成新数据源的添加。在界面上,现在可以看到我们的数据源显示。如果保存后有横幅弹出提示"Datasource is working",则表明它正在工作!
    • 单击Grafana图标,然后单击"Dashboards->Home"返回主控制台视图
    • Grafana入门:https://grafana.com/docs/guides/getting_started/
    • Grafana教程和录像:https://grafana.com/docs/tutorials/screencasts/
    • Prometheus文档中的Grafana部分:https://prometheus.io/docs/visualization/grafana/
    • Grafana预建仪表板:https://grafana.com/grafana/dashboards
    • 许多项目还包括预先构建的Grafana仪表板,以满足特定的需求,如Mysqlhttps://github.com/percona/grafana-dashboards)或Redishttps://grafana.com/grafana/dashboards/763)
    • 我们在本书源码中提供了可导入的完整仪表板JSON文件(https://github.com/turnbullpress/prometheusbook-code/blob/master/4/dashboard.json
    • The Docker Bookhttps://dockerbook.com/
    • 代码清单:在配置OS X启动时启动Grafana brew services start grafana 4.7.3 配置Grafana Web界面 4.7.4 第一个仪表板 有大量资源和示例可供参考


    • 链接:https://hub.docker.com/search?q=grafana&type=image 4.7.2 启动和配置Grafana
    • 新的Prometheus使用2.0格式:https://www.robustperception.io/converting-rules-to-the-prometheus-2-0-format 4.6.3 添加记录规则 group: - name: node_rules rules: - record: instance:node_cpu:avg_rate5m expr: 100 - avg(irate(node_cpu_seconds_total{job="node",mode="idle"}[5m])) by (instance) * 100 level:metric:operations instance:node_cpu:avg_rate5m Prometheus文档中有一些全名的最佳实践:https://prometheus.io/docs/practices/naming/ group: - name: node_rules rules: - record: instance:node_cpu:avg_rate5m expr: 100 - avg(irate(node_cpu_seconds_total{job="node",mode="idle"}[5m])) by (instance) * 100 labels: metric_type: aggregation group: - name: node_rules rules: - record: instance:node_cpu:avg_rate5m expr: 100 - avg(irate(node_cpu_seconds_total{job="node",mode="idle"}[5m])) by (instance) * 100 - record: instance:node_memory_usage:percentage expr: (node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Cached_bytes + node_memory_Buffers_bytes)) / node_memory_MemTotal_bytes * 100 - record: instance:root:node_filesystem_usage:percentage expr: (node_filesystem_size_bytes{mountpoint="/"} - node_filesystem_free_bytes{mountpoint="/"}) / node_filesystem_size_bytes{mountpoint="/"} * 100

    4.7 可视化 4.7.1 安装Grafana sudo rpm --import https://packagecloud.io/gpg.key [grafana] name=grafana baseurl=https://packagecloud.io/grafana/stable/el/7/$basearch repo_gpgcheck=1 enabled=1 gpgcheck=1 gpgkey=https://packagecloud.io/gpg.key https://grafanarel.s3.amazonaws.com/RPM-GPG-KEY-grafana sslverify=1 sslcacert=/etc/pki/tls/certs/ca-bundle.crt

    • Prometheus配置文件prometheus.ymlrule_files块中添加这个文件
    • 代码清单:添加规则文件
    • 代码清单:创建一个记录规则文件
    • 规则是自动计算的,频率则由第3章介绍的prometheus.yml配置文件的global块中的evaluation_interval参数控制
    • 代码清单:evaluation_interval参数
    • 记录规则是一种根据已有时间序列计算新时间序列(特别是聚合时间序列)的方法
    • 跨多个时间序列生成聚合
    • 预先计算消耗大的查询
    • 产生可用于生成警报的时间序列
    • 文档链接
    • 可以通过以下三种方式使查询持久化
    • 许多现有的exporter使用这种"元数据"模式来提供额外状态的信息,例如cAdvisorcadvisor_version指标
    • relabel_configshttps://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config
    • RE2正则表达式语法:https://github.com/google/re2/wiki/Syntax
    • 正则表达式测试工具:http://www.regexplanet.com/advanced/golang/index.html

    4.5 Node Exporter和cAdvisor指标 4.5.1 USE方法 CPU使用率

    node_cpu_seconds_total{cpu="cpu0",instance="138.197.26.39.9100",job="node",mode="user"} irate(node_cpu_seconds_total{job="node"}[5m]) avg(irate(node_cpu_seconds_total{job="node"}[5m])) by (instance) avg(irate(node_cpu_seconds_total{job="node",mode="idle"}[5m])) by (instance) * 100 100 - avg(irate(node_cpu_seconds_total{job="node",mode="idle"}[5m])) by (instance) * 100

    CPU饱和度 count by (instance)(node_cpu_seconds_total{mode="idle"}) node_load1 > on (instance) 2 * count by (instance)(node_cpu_seconds_total{mode="idle"}) 内存使用率 (总内存-(可用内存+缓冲缓存中的内存+页面缓存中的内存))÷总内存×100 (node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Cached_bytes + node_memory_Buffers_bytes)) / node_memory_MemTotal_bytes * 100

    内存饱和度 1024 * sum by (instance) ( (rate(node_vmstat_pgpgin[1m]) + rate(node_vmstat_pgpgout[1m])) ) 磁盘使用率 (node_filesystem_size_bytes{mountpoint="/"}) - node_filesystem_free_bytes{mountpoint="/") / node_filesystem_size_bytes{mountpoint="/"} * 100 predict_linear(node_filesystem_free_bytes{mountpoint="/"}[1h], 4*3600) < 0 4.5.2 服务状态 node_systemd_unit_state{name="docker.service",state="active"} 4.5.3 可用性和up指标 up{job="<job-name>", instance="<instance-id>"} 4.5.4 metadata指标 metadata{role="docker_server",datacenter="NJ"} 1 更改标签或添加新标签都会创建新的时间序列 metadata{datacenter!="NJ"} 向量匹配 一对一匹配 node_system_unit_state{name="docker.service"} == 1 and on (instance, job) metadata{datacenter="SF"} node_system_unit_state{instance="138.197.30.147:9100", job="node", name="docker.service", state="active"} 多对一和一对多匹配

    • 请记住,标签是时间序列的唯一性约束。如果你删除标签并导致时间序列重复,那么系统可能会出现问题!
    • 代码示例:删除标签
    • 然后将其放入一个新标签container_id中,通过重新标记我们可以这样做
    • 代码示例:替换标签
    • 许多cAdvisor指标都有一个id标签,其中包含正在运行的进程的名称。如果进程是一个容器,会看到如下信息
    • 如果指定了多个源标签,那么我们将使用分隔符隔开每个正则表达式,例如
    • 首先来看看第一种操作。我们使用source_labels参数选择要操作的指标,并且还需要一组标签名称。在示例中我们使用____name标签,____name标签是表示指标名称的预留标签
    • 代码示例:定义新的分隔符
    • 请记住,我们有两个阶段可以重新标记。第一个阶段是对来自服务发现的目标进行重新标记(relabel_configs),这对于将来自服务发现的元数据标签中的信息应用于指标上的标签来说非常有用。这是在作业内的relabel_configs块中完成的
    • 第二个阶段是在抓取之后(metric_relabel_configs)且指标被保存于存储系统之前。这样,我们就可以确定哪些指标需要保存、哪些需要丢弃以及这些指标的样式。这是在我们作业 内的metric_relabel_configs块中完成的
    • 代码示例:通过重新标记来删除指标
    • 在一个集中的复杂监控环境中,有时你无法控制监控所有资源以及所有暴露的监控数据。通过重新票房,你可以控制、管理并标准化环境中的指标。一些最常见的用例是:
    • 每个指标都天然带有两个拓扑标签:jobinstance
    • 模式标签(schematic label)是urlerror_codeuser之类的东西,它们允许你将拓扑中同一级别的时间序列匹配在一起,例如创建数据间的比率
    • 如果你需要添加额外的标签,则可以考虑如图所示的层次结构(标签分类)
    • 意味着应该明知地使用标签并尽可能保持不变。如果不遵守这一规定,则可能产生新的时间序列,从而创建出一个动态的数据环境,使监控的数据源难以跟踪。想象一下,你有一个时间序列,用于跟踪服务的状态。你为该时间系列配置了一个警报,该警报依赖于指标的标签来评判。如果更改或添加标签,那么警报将变为无效
    • 标签提供了时间序列的维度。它们可以定义目标,并为时间序列提供上下文。但最重要的是,结合指标名称,它们构成了时间序列的标识,如果它们改变了,那么时间序列的标识也会跟着改变。
    • Prometheus提供了可以重新标记目标的机会,并可能使用你的服务发现所添加的一些元数据。你还可以过滤目标,以删除或保留特定条目
    • 然后就是真正的数据抓取,以及指标返回。当指标被抓取时,你将拥有最后一次机会在将它们保存在服务器之前重新标记并过滤
    • 抓取的生命周期(图)
    • 示例都是已手动指定的静态配置的主机,以及一些其他服务发现机制,例如从文件加载目标或查询API
    • 服务发现返回一个目标列表,其中饮食一组称为元数据的标签,这些标签以__meta_为前缀。每个服务发现机制都有不同的元数据
    • 服务发现还会根据目标的配置来设置其他标签,这些标签带有的前缀和后缀,包括____scheme_______address____metrics_path_。这些标签包含目标的模式(httphttps)、目标的地址以及指标的具体路径
    • 每个标签通常都有一个默认值。例如,______metrics_path__默认为/metrics____scheme默认为值http。此外,如果路径中存在任何URL参数,则它们的前缀会设置为________param___*。
    • 配置标签会在抓取的生命周期中重复利用以生成其他标签。例如,指标上instance标签的默认内容是____address标签的内容
    • 注意:为什么到现在我们都没有在界面上看到任何带前缀和后缀的标签呢?这是因为有些标签在生命周期的后期被删除了,并且所有这些标签都被专门排除掉,不在Web UI上显示
    • 然后这些目标列表和标签会返回给Prometheus,其中一些标签可以在配置中被覆盖 ,例如,通过metrics_path参数设置的指标路径以及通过scheme参数指定的模式
    • 代码示例:覆盖标签配置
    • 我们需要告诉Prometheus cAdvisor是在Docker守护进程上运行的。为此,我们将在配置中添加第三个作业
    • 查看web uihttp://11.11.11.112:8080/containers
    • 目录分为两种类型
    • 让我们在Docker1主机上运行一个cAdvisor容器
    • 运行cAdvisor容器
    • Databases
    • Hardware related
    • Issue trackers and continuous integration
    • Messaging systems
    • Storage
    • HTTP
    • APIs
    • Logging
    • Other monitoring systems
    • Miscellaneous
    • Software exposing Prometheus metrics
    • [Other third-party utilities](https://prometheus.io/docs/instrumenting/exporters/#other-third-party-utilitie4.1.1 安装Node ExporterMac brew install node_exporterUpdating Homebrew...==> Auto-updated Homebrew!Updated 2 taps (homebrew/cask-versions and homebrew/cask).==> Downloading https://homebrew.bintray.com/bottles/node_exporter-0.18.1.mojave.bottle.tar.gz==> Downloading from https://akamai.bintray.com/ff/ff7b019285ebd031d6985bb06e4300518c9e07f01536ebb5281e4970818cb8a3?__gda__=exp=1573171435~hmac=467cbe2######################################################################## 100.0%==> Pouring node_exporter-0.18.1.mojave.bottle.tar.gz==> CaveatsWhen used with `brew services`, node_exporter's configuration is stored as command line flags in /usr/local/etc/node_exporter.argsExample configuration: echo --web.listen-address :9101 > /usr/local/etc/node_exporter.argsFor the full list of options, execute node_exporter -hTo have launchd start node_exporter now and restart at login: brew services start node_exporterOr, if you don't want/need a background service you can just run: node_exporter==> Summary? /usr/local/Cellar/node_exporter/0.18.1: 8 files, 16MB node_exporter --web.listen-address=":9600" --web.telemetry-path="/node_metrics"INFO[0000] Starting node_exporter (version=, branch=, revision=) source="node_exporter.go:156"INFO[0000] Build context (go=go1.12.5, user=, date=) source="node_exporter.go:157"INFO[0000] Enabled collectors: source="node_exporter.go:97"INFO[0000] - boottime source="node_exporter.go:104"INFO[0000] - cpu source="node_exporter.go:104"INFO[0000] - diskstats source="node_exporter.go:104"INFO[0000] - filesystem source="node_exporter.go:104"INFO[0000] - loadavg source="node_exporter.go:104"INFO[0000] - meminfo source="node_exporter.go:104"INFO[0000] - netdev source="node_exporter.go:104"INFO[0000] - textfile source="node_exporter.go:104"INFO[0000] - time source="node_exporter.go:104"INFO[0000] Listening on :9600 source="node_exporter.go:170" mkdir -p /var/lib/node_exporter/textfile_collectormetadata{role="docker_server",datacenter="NJ"} 1 node_exporterINFO[0000] Starting node_exporter (version=0.18.1, branch=HEAD, revision=3db77732e925c08f675d7404a8c46466b2ece83e) source="node_exporter.go:156"INFO[0000] Build context (go=go1.12.5, user=root@b50852a1acba, date=20190604-16:41:18) source="node_exporter.go:157"INFO[0000] Enabled collectors: source="node_exporter.go:97"INFO[0000] - arp source="node_exporter.go:104"INFO[0000] - bcache source="node_exporter.go:104"INFO[0000] - bonding source="node_exporter.go:104"INFO[0000] - conntrack source="node_exporter.go:104"INFO[0000] - cpu source="node_exporter.go:104"INFO[0000] - cpufreq source="node_exporter.go:104"INFO[0000] - diskstats source="node_exporter.go:104"INFO[0000] - edac source="node_exporter.go:104"INFO[0000] - entropy source="node_exporter.go:104"INFO[0000] - filefd source="node_exporter.go:104"INFO[0000] - filesystem source="node_exporter.go:104"INFO[0000] - hwmon source="node_exporter.go:104"INFO[0000] - infiniband source="node_exporter.go:104"INFO[0000] - ipvs source="node_exporter.go:104"INFO[0000] - loadavg source="node_exporter.go:104"INFO[0000] - mdadm source="node_exporter.go:104"INFO[0000] - meminfo source="node_exporter.go:104"INFO[0000] - netclass source="node_exporter.go:104"INFO[0000] - netdev source="node_exporter.go:104"INFO[0000] - netstat source="node_exporter.go:104"INFO[0000] - nfs source="node_exporter.go:104"INFO[0000] - nfsd source="node_exporter.go:104"INFO[0000] - pressure source="node_exporter.go:104"INFO[0000] - sockstat source="node_exporter.go:104"INFO[0000] - stat source="node_exporter.go:104"INFO[0000] - textfile source="node_exporter.go:104"INFO[0000] - time source="node_exporter.go:104"INFO[0000] - timex source="node_exporter.go:104"INFO[0000] - uname source="node_exporter.go:104"INFO[0000] - vmstat source="node_exporter.go:104"INFO[0000] - xfs source="node_exporter.go:104"INFO[0000] - zfs source="node_exporter.go:104"INFO[0000] Listening on :9100 source="node_exporter.go:170"node_exporter --collector.systemd.unit-whitelist="(docker|ssh|rsyslog).service"[vagrant@lab2 ~]
    1. docker.serviceDocker守护进程
    2. ssh.serviceSSH守护进程
    3. rsyslog.serviceRSyslog守护进程
    4. Prometheus服务器自己的指标
    5. 来自3台主机的Node Exporter指标
    6. 来自3台主机的cAdvisor指标
    7. node_memory_MemTotal_bytes:主机上的总内存
    8. node_memory_MemFree_bytes:主机上的可用内存
    9. node_memory_Buffers_bytes:缓冲缓存中的内存
    10. node_memory_Cached_bytes:页面缓存中的内存
    11. 记录规则:https://prometheus.io/docs/prometheus/latest/configuration/recording_rules/
    12. 警报规则:https://prometheus.io/docs/prometheus/latest/configuration/alerting_rules/
    13. 记录规则:根据查询创建新指标
    14. 警报规则:从查询生成警报
    15. 可视化:使用Grafana等仪表板可视化查询
    16. 删除不必要的指标
    17. 从指标中删除敏感或不需要的标签
    18. 添加、编辑或修改指标的标签值 或标签格式
    19. 第一种是只读的,cAdvisor将从中收集数据,例如/sys目录--volume=/sys:/sys:ro
    20. 第二种类型是可读写的,是Docker套接字的挂载,通常位于/var/run目录中

第5章 服务发现

  • 服务发现可以通过以下几种机制实现
  1. 从配置管理工具生成的文件中接收目标列表
  2. 查询API(例如Amazon AWS API)以获取目标列表
  3. 使用DNS记录以返回目标列表

5.1 静态配置的局限性

  • 数据抓取生命周期
  • 在现有的配置中,服务发现机制是在static_configs块中定义的
  • 代码清单:静态服务发现
代码语言:javascript
复制
scrape_configs:
- job_name: 'prometheus'
  static_configs:
    - targets: ['localhost:9090']

- job_name: 'node'
  static_configs:
    - targets: ['11.11.11.111:9100', '11.11.11.112:9100', '11.11.11.113:9100']
  • 需要更成熟的服务发现方式
  1. 基于文件的方式
  2. 基于云的方式
  3. 基于DNS的方式

5.2 基于文件的服务发现

  • 基于文件的发现只比静态配置更先进一小步,但它非常适合配置管理工具
  • 代码清单:基于文件的服务发现
代码语言:javascript
复制
- job_name: node
  file_sd_configs:
    - files:
      - targets/nodes/*.json
      refresh_interval: 5m

- job_name: docker
  file_sd_configs:
    - files:
      - targets/docker/*.json
      refresh_interval: 5m
  • 我们用file_sd_configs块替换prometheus.yml文件中的static_configs块。在这些块中,已经指定了文件列表,并包含在files列表中。我们在父目录targets下为每个作业指定了对应的文件,并为每个作业创建了一个子目录。你可以创建适合你的任何文件结构
  • 每次作业运行或这些文件发生变化时,Prometheus都会重新加载文件的内容。以防万一,我们还指定了refresh_interval选项,该选项将在每个间隔结束时加载文件列表中的目标——对这个示例来说是5分钟
  • 提示:还有一个名为prometheus_sd_file_mtime_seconds的指标将告诉你文件的上次更新时间。你可以监控这个指标以识别数据过期问题
  • 我们快速创建上述的目录结构
  • 代码清单:创建目标文件目录
代码语言:javascript
复制
cd /etc/prometheus
mkdir -p targets/{nodes,docker}
  • 将节点和Docker守护进程列表移动到新的JSON文件,创建两个文件来保存这些目标
  • 代码清单:创建保存目标的JSON文件
代码语言:javascript
复制
touch targets/nodes/nodes.json
touch targets/docker/daemons.json
  • 首先填充node.json文件
  • 代码清单:node.json文件
代码语言:javascript
复制
[{
"targets": ["138.197.26.39:9100", "138.197.30.147:9100", "138.197.30.163:9100"]
}]
  • 然后是daemons.json文件
  • 代码清单:daemons.json文件
代码语言:javascript
复制
[{
"targets": ["138.197.26.39:8080", "138.197.30.147:8080", "138.197.30.163:8080"]
}]
  • 也可以通过yaml格式实现和使用JSON的目标列表是一样的
代码语言:javascript
复制
- targets:
  - "138.197.26.39:8080"
  - "138.197.30.147:8080"
  - "138.197.30.163:8080"
  • 也可以为这些目标添加标签
  • 代码清单:添加标签
代码语言:javascript
复制
[{
  "targets": ["138.197.26.39:8080", "138.197.30.147:8080", "138.197.30.163:8080"],
  "labels": {"datacenter": "nj"}
}]
  • 基于文件的服务发现会在重新标记阶段自动给每个目标添加一个元数据标签__meta_filepath,它包括配置目标的文件路径和文件名
  • 注意:可以访问https://localhost:9090/service-discovery,通过web界面查看服务发现目标的完整列表及其元数据标签
编写文件
  • 如果你要自定义编写脚本,则可以遵循以下几条简单的规则:
  1. 使你的服务发现可配置——不要采用硬编码方式。最好确保你的文件发现能够使用默认配置自动 运行
  2. 不要在配置中暴露API密钥或密码等信息,应该依靠密钥保管库或环境变量(https://www.12factor.net/)
  3. 针对输出目标的文件的操作应该是原子的
  • 注意:在Prometheus文档中还有一个基于文件的服务发现集成列表(https://prometheus.io/docs/operating/integrations/#file-service-discovery

5.3 基于API的服务发现

  • 当前可用的本机服务发现插件包括以下平台
  1. Amazon EC2
  2. Azure
  3. Consul
  4. Google Compute Cloud
  5. Kuberneteshttps://prometheus.io/docs/prometheus/latest/configuration/configuration/#kubernetes_sd_config

5.4 基于DNS的服务发现

  • DNS服务发现允许你指定DNS条目列表,然后查询这些条目中的记录以发现目标列表。它依赖于AAAAASRV DNS记录查询
  • 提示:DNS记录将由Prometheus服务器上本地定义的DNS服务器解析。例如,Linux上的/etc/resolv.conf
  • 代码清单:DNS服务发现作业
代码语言:javascript
复制
- job_name: webapp
  dns_sd_configs:
    - names: ['_prometheus._tcp.example.com']
  • 我们定义了一个名为webapp的新作业 ,并指定了一个dns_sd_configs块。在该块中,我们指定了names参数,其中包含要查询的DNS条目列表
  • 默认情况下,PrometheusDNS服务发现假定你会查询SRV或服务发现。服务记录是一种在DNS配置中定义服务的方法,服务通常由运行服务的一个或多个目标主机和端口组合组成。DNS SRV条目的格式如下所示
  • 代码清单:SRV记录
代码语言:javascript
复制
_service._proto.name. TTL IN SRV priority weight port target.
  • 其中_service是要查询的服务名称,__proto是服务的协议,通常是TCPUDP。我们指定条目的名称,后面以"."结尾,然后是记录的TTLTime To Live)时间。IN是标准的DNS类(通常都会用IN)。我们指定目标主机的优先级(priority):较低的值 具有较高的优先级
  • 代码清单:示例的SRV记录
代码语言:javascript
复制
_prometheus._tcp.example.com 300 IN SRV 10 1 9100 webapp1.
_prometheus._tcp.example.com 300 IN SRV 10 1 9100 webapp2.
_prometheus._tcp.example.com 300 IN SRV 10 1 9100 webapp3.
  • Prometheus查询目标时,它会通过DNS服务器查找example.com域。然后,它将在该域中搜索名为_prometheus._tcp.example.comSRV记录,并返回该条目中的服务记录。在该条目中有三条记录,因此我们会看到返回了三个目标
  • 代码清单:SRVDNS目标
代码语言:javascript
复制
webapp1.example.com
webapp2.example.com
webapp3.example.com
  • 还可以使用DNS服务发现来查询单个AAAAA记录。为此,我们需要为抓取明确指定查询类型的端口。之所以需要指定端口,是因为AAAAA记录只返回主机,而不是像SRV记录那样返回主机和端口组合
  • 代码清单:DNS A记录服务发现作业
代码语言:javascript
复制
- job_name: webapp
  dns_sd_configs:
    - names: ['example.com']
      type: A
      port: 9100
  • 这只返回example.com域根目录下的所有A记录
  • 代码清单:DNS子域A记录服务发现作业
代码语言:javascript
复制
- job_name: webapp
  dns_sd_configs:
    - names: ['web.example.com']
      type: A
      port: 9100
  • 我们提取了一个子域web.example.comA记录解析,并在后面加上9100端口后缀
  • 提示:DNS服务发现中只有一个元数据标签__meta_dns_name,它被设置为生成目标的特定DNS记录

第6章 警报管理

  • Prometheus是一个按功能划分的平台,指标的收集和存储与警报是分开的。警报管理功能由名为Alertmanager的工具提供,该工具是监控体系中的独立组件。我们需要在Prometheus服务器上定义警报规则,这些规则可以触发事件,然后传播到Altermanager。接下来,Alertmanager会决定如何处理相应的警报,进而解决去重等问题,还会确定在发送警报时使用的机制:实时消息、电子邮件或通过PagerDutyVictorOps等工具

6.1 警报

  • 警报可以为我们提供一些指示,表明我们环境中的某些状态已发生变化,且通常会是比想象更糟的情况。一个好警报的关键是能够在正确的时间、以正确的理由和正确的速度发送,并在其中放入有用的信息
  • 警报方法中最常见的反模式是发送过多的警报。对于监控来说,过多的警报相当于“狼来了”这样的故事
  • 通常发送过多警报的原因可能包括
  1. 警报缺少可操作性,它只是提供信息。你应关闭所有这些警报,或将其转换为计算速率的计数器,而不是发出警报
  2. 故障的主机或服务上游会触发其下游的所有内容的警报。你应确保警报系统识别并抑制这些重复的相邻警报
  3. 对原因而不是症状(symptom)进行警报。症状是应用程序停止工作的迹象,它们可能是由许多原因导致的各种问题的表现。API或网站的高延迟是一种症状,这种症状可能由许多问题导致:高数据库使用率、内存问题、磁盘性能等。对症状发送警报可以识别真正的问题。仅对原因(例如高数据库使用率)发出警报也可能识别出问题(但通常很可能不会)。对于这个应用程序,高数据库使用率可能是完全正常的,并且可能不会对最终用户或应用程序造成性能问题。作为一个内部状态,发送警报是没有意义的。这种警报可能会导致工程师错过更重要的问题,因为他们已经对大量不可操作且基于原因的警报变得麻木。你应该关注基于症状的警报,并依赖你的指标或其他诊断数据来确定原因
  • 第二种最常见的反模式是警报的错误分类。有时,这也意味着重要的警报会隐藏在其他警报中。但有时候,警报会被发送到错误的地方或者带有不正确的紧急情况说明
  • 第三种反模式是发送无用的警报
  • 代码清单:Nagios警报
  • 这个通知似乎提供了一些有用的信息,但事实并非如此。这是由存储空间突增导致的?还是逐渐增长的结果?增长速度是多少?1GB分区上9%的可用磁盘空间与1TB磁盘上9%的可用磁盘空间完全不同。我们可以忽略或静音这类通知吗?还是需要立即采取行动?
  • 良好的警报应该具备以下几个关键特征:
  1. 适当数量的警报,关注症状而不是原因。噪声警报会导致警报疲劳,最终警报会被忽略。修复警报不足比修复过度警报更容易
  2. 应设置正确的警报优先级。如果警报是紧急的,那么它应该快速路由到负责响应的一方。如果警报不紧急,那么我们应该以适当的速度发送警报,以便在需要时做出响应
  3. 警报应包括适当的上下文,以便它们立即可以使用
  • 提示:Google SRE手册中有一个很棒的关于警报的章节

6.2 Alertmanager如何工作

  • Alertmanager处理从客户端发来的警报(https://prometheus.io/docs/alerting/alertmanager/),客户端通常是Prometheus服务器。Alertmanager对警报进行去重、分组,然后路由到不同的接收器,如电子邮件、短信或SaaS服务(PagerDuty等)
  • Alertmanager架构
  • 警报规则(https://prometheus.io/docs/prometheus/latest/configuration/alerting_rules/)将使用收集的指标并在指定的阈值或标准上触发警报。我们还将看到如何为警报添加一些上下文。当指标达到阈值或标准时,会生成一个警报并将其推送到Alertmanager。警报在Alertmanager上的HTTP端点上接收。一个或多个Prometheus服务器可以将警报定向到单个Alertmanager,或者你可以创建一个高可用的Alertmanager集群

6.3 安装Alertmanager

  • Alertmanager二进制:https://github.com/prometheus/alertmanager
  • Prometheus.io下载页面:https://prometheus.io/download/#alertmanager
  • GitHub Releases页面:https://github.com/prometheus/alertmanager/releases
  • amtool二进制文件用于帮助管理Alertmanager
  • 代码清单:移动amtool二进制文件
代码语言:javascript
复制
sudo cp alertmanager /usr/local/bin
sudo cp amtool /usr/local/bin
sudo chmod -R 777 /usr/local/bin/{alertmanager,amtool}
  • 监控套件安装方式
  1. 使用Docker Compose安装PrometheusNode ExporterGrafanahttps://github.com/vegasbrianc/prometheus
  2. 使用Docker Compose单节点安装PrometheusAlertmanagerNode ExporterGrafanahttps://github.com/danguita/prometheus-monitoring-stack
  3. 使用Docker Swarm安装Prometheushttps://github.com/stefanprodan/swarmprom

6.4 配置Alertmanager

  • Alertmanager配置也是基于YAML的配置文件(https://prometheus.io/docs/alerting/configuration/)
  • 代码清单:一个简单的alertmanager.yml配置文件
代码语言:javascript
复制
global:
  smtp_smarthost: 'localhost:25'
  smtp_from: 'alertmanager@example.com'
  smtp_require_tls: false

template:
  - '/etc/alertmanager/template/*.tmpl'

route:
  receiver: email

receivers:
  - name: 'email'
    email_configs:
      - to: 'alerts@example.com'
  • template块包含保存警报模板的目录列表(https://prometheus.io/docs/alerting/notifications/)。由于Alertmanager可以发送到各种目的地,因此你通常需要能够自定义警报的外观及其包含的数据。现在让我们创建这个目录
  • 代码清单:创建模板目录
代码语言:javascript
复制
sudo mkdir -p /etc/alertmanager/template
  • route块会告诉Alertmanager如何处理特定的传入警报。警报根据规则进行匹配然后采取相应的操作。你可以把路由想象成有树枝的树,每个警报都从树的根(基本路由或基本节点)进入。除了基本节点之外,每个路由都有匹配的标准,这些标准应该匹配所有警报。然后,你可以定义子路由或子节点,它们是树的分支,对某些特定的警报感兴趣,或者会采取某些特定的操作
  • Alertmanager路由
  • 在当前的配置中,我们只定义了基本路由,即树的根节点。在后面,我们将利用路由来确保警报具有正确的容量、频率和目的地
  • 对于电子邮件警报,我们使用email_configs块来指定电子邮件选项,例如接收警报的地址。我们还可以指定SMTP设置(将覆盖 全局设置),并添加其他条目(例如邮件标头)
  • 提示:有一个称为Webhook接收器的内置接收器,你可以使用它将警报发送到Alertmanager中没有特定接收器的其他目的地(https://prometheus.io/docs/alerting/configuration/#webhook_config

6.5 运行Alertmanager

Alertmanager作为一个Web服务运行,默认端口为9093。启动Alertmanager

代码清单:启动Alertmanager


代码语言:javascript
复制
alerting:
  alertmanagers:
    - static_configs:
      - targets:
        - alertmanager: 9093
  • 代码清单:发现Alertmanager alerting: alertmanagers: - dns_sd_configs: - names: ['_alertmanager._tcp.example.com'] 6.6.2 监控Alertmanager - job_name: 'alertmanager' static_configs: - targets: ['localhost:9093']

6.7 添加警报规则 cd rules touch node_alerts.yml rule_files: - "rules/*_rules.yml" - "rules/*_alerts.yml" 6.7.1 添加第一条警报规则 groups: - name: node_alerts rules: - alert: HighNodeCPU expr: instance:node_cpu:avg_rate5m > 80 for: 60m labels: severity: warning annotations: summary: High Node CPU of {{ humanize $value}}% for 1 hour console: You might want to check the Node Dashboard at http://grafana.example.com/dashboard/db/node-dashboard instance:node_cpu:avg_rate5m > 80 6.7.2 警报触发 警报未激活

  • 注意:Alertmanager API/api/v1/alerts路径接收警报
  • 代码清单:ALERT时间序列
  • 假设一个HighNodeCPU警报被触发了,我们将能在Alertmanager Web控制台http://alertmanager:9093/#/alerts上看到该警报
  • HighNodeCPU警报邮件
  • 提示:变量\labels和\value分别是底层Go变量.Labels和.Value的名称
  • 我们应始终牢记:Prometheus服务器也可能出问题。让我们添加一些规则来识别问题并对它们发出警告
  • 第一个是PrometheusConfigReloadFailed,它让我们知道Prometheus配置重新加载是否失败。如果上次重新加载失败,则使用指标prometheus_config_last_reload_successful,且指标的值 为0
  • 最后的警报可以帮助我们确定主机和服务的能力。第一个警报利用了我们使用Node Exporter收集的systemd指标。如果我们在节点上监控的服务不再活动,则会生成一个警报
  • 如果带有active标签的node_systemd_unit_state指标值为0,则会触发此警报,表示服务故障至少60秒
  • 我们在severity标签中添加了一个新值 critical,并添加了一个模板注解,以帮助指示哪个实例和作业失败
  • 这个测试表达式计算出up指标的平均值然后按job聚合,并在该值低于50%时触发。如果作业中50%的实例无法完成抓取,则会触发警报
  • 我们根据jobup指标求和,然后将其除以计数,如果结果大于或等于0.8,或者特定作业中20%的实例未启动,则触发警报
  • 代码清单:up指标缺失警报
  • 代码清单:添加路由配置
  • 代码清单:分组
  • 分组还会更改Alertmanager的行为。如果引发了新警报,那么Alertmanager将等待下一个选项group_wait中指定的时间段,以便在触发警报之前查看是否收到该组中的其他警报。你可以将其视为警报缓冲
  • 我们还指定了repeat_interval。这个暂停并不适用于我们的警报组,而是适用于单个警报,并且是等待重新发送相同警报的时间段,我们指定为3个小时
  • 代码清单:标签匹配
  • 代码清单:路由分支
  • 我们可以使用continue选项来覆盖此行为,该选项控制警报是否先遍历路由,然后再返回以遍历路由树
  • continue选项默认为false,但如果设置为true,则警报将在此路由中触发(如果匹配),并继续执行下一个相邻路由。有时这对于向两个地方发送警报很有用,但更好的解决方法是在接收器中指定多个端点
  • 通过在接收器配置中将send_resolved选项设置为true,可以使用Alertmanager发送它们。通常不建议发送这些已解决的警报,因为其可能导致“错误警报”的循环,进而导致警报疲劳,所以在启用之前要仔细考虑
  • 代码清单:正则表达式匹配
  • 添加一个非电子邮件的接收器,我们添加Slack接收器,它会消息发送到Slack实例(https://prometheus.io/docs/alerting/configuration/#slack_config
  • Alertmanager发送给Slack的一般警报消息非常简单。你可以在其源代码中看到Alertmanager使用的默认模板,该模板包含电子邮件和其他接收器的默认值 ,但是我们可以为许多接收器覆盖这些值 。例如,可以在Slack警报中添加文本行
  • Alertmanager自定义通知使用Go模板语法。警报中包含的数据也通过变量暴露。我们使用CommonAnnotations变量,该变量包含一组警报通用的注解集
  • 我们还可以使用Go template函数来引用外部模板,从而避免在配置文件中嵌入较长且复杂的字符串,目录位于/etc/alertmanager/templates
  • 第一种方法使用Web界面
  • 新建silence
  • 注意:你也可以参考使用一个名为UnseeAlertmanager控制台(https://github.com/cloudflare/unsee
  • 第二种方法是使用amtool命令行。amtool二进制文件随Alertmanager安装tar包附带
  • 我们可以使用query子命令来查询当前silence列表
  • 这里我们使用=~来表示标签匹配是一个正则表达式,并在所有警报上匹配一个以Instance开头的alertname。我们还使用了--comment参数来添加有关警报的信息
  • 我们用--author参数覆盖了silence的创建者,并将持续时间指定为两个小时,而不是默认的一小时
  • 回到silence的创建,在创建silence时,你还可以使用正则表达式作为标签值
  • 你可以为某些选项创建一个YAML配置文件,而不必每次都指定--alertmanager.url参数。amtool查找的默认配置文件路径是$HOME/.config/amtool/config.yml/etc/amtool/config.yml
  • 代码清单:使silence过期
  • 提示:使用amtool创建的silence被设置为一小时后自动过期,可以使用--expires--expire-on参数来指定更长的时间或窗口
  • 通常我们需要让警报系统道我们已经停止服务以进行维护,并且不希望触发警报。或者,当上游出现问题时,我们需要将下游服务和应用程序“静音”。Prometheus称这种警报静音为silencesilence可以设定为特定时期,例如一小时,或者是一个时间窗口(如直到今天午夜)。这是silence的到期时间或到期日期。如果需要,我们也可以提前手动让silence过期(如果我们的维护比计划提前完成)
  • 提示:Alertmanager文档中还有一些其他的通知模板示例(https://prometheus.io/docs/alerting/notification_examples/)
  • 代码清单:添加Slack接收器
  • Prometheus以一个固定时间间隔来评估所有规则,这个时间由evaluate_interval定义,我们将其设置为15秒。在每个评估周期,Prometheus运行每个警报规则中定义的天工并更新警报状态
  • 下一个子句for,控制在触发警报之前测试天工必须为true的时间长度。在示例中,指标instance:node_cpu:avg_rate5m需要在触发警报之前的60分钟内大于80%。这限制了警报误报或是暂时状态的可能性
  • 警报上的标签与警报的名称相结合,构成警报的标识。这与时间序列相同,其中指标名称和标签构成时间序列的标识
  • 重新启动Prometheus后,你将能够在Prometheus Web界面http://localhost:9090/alerts中看到新的警报
  • 与记录规则一样,警报规则也可以组合在一起。我们已经指定了一个组名node_alerts,该组中的规则包含在rules块中。在每个警报组中,警报名称都必须是唯一的
  • 让我们添加第一条规则:一个CPU警报规则(https://prometheus.io/docs/prometheus/latest/configuration/alerting_rules/)。我们将创建一个警报,如果我们创建的CPU查询(5分钟内的节点平均CPU使用率)在至少60分钟内超过80%,则会触发警报
  • 不需要单独将此文件添加到prometheus.yml配置文件中的rule_files块,可以使用globbing通配符加载该目录中以_rules.yml__alerts.yml结尾的所有文件
  • 现在Alertmanager已经配置完成,让我们添加第一条警报规则。根据使用的节点查询来创建警报,并使用up指标来创建一些基本的可用性警报
  • 代码清单:创建警报规则文件
  • Prometheus一样,Alertmanager暴露了自身的相关指标
  • 在这里,Prometheus将查询_alertmanager.__tcp.example.com SRV记录以获得Alertmanager的主机名
  • alerting块包含允许Prometheus识别一个或多个Alertmanager的配置。为此,Prometheus使用与查找抓取目标时相同的发现机制,在默认配置中是static_configs。与监控作业一样,它指定目标列表,此处是主机名alertmanager加端口9093(Alertmanager默认端口)的形式。该列表假定你的Prometheus服务器可以解析alertmanager主机名为IP地址,并且Alertmanager在该主机的端口9093上运行
  • 我们在prometheus.yml配置文件中使用了默认的Alertmanager配置,它包含在alerting块中。让我们先来看看默认配置
  • Alertmanager提供了一个Web界面:http://localhost:9093
  • 提示:随Alertmanager一起附带的还有一个命令行工具amtool,允许你查询警报、管理silence和使用Alertmanager服务器等
  • https://prometheus.io/docs/prometheus/latest/configuration/template_reference/
  • 通过Alertmanager Web控制台
  • 节点的CPU不断变化,每隔一段由scrape_interval定义的时间被Prometheus抓取一次,对我们来说是15秒
  • 当警报表达式为true时(对于我们来说是CPU超过80%),会创建一个警报并转换到Pending状态,执行for子句
  • 如果警报测试表达式不再为true,则Prometheus会将警报规则的状态从Pending更改为Inactive6.7.3 Alertmanager的警报ALERTP{alertname="HighNodeCPU",alertstate="firing",severity=warning,instance="138.197.26.39:9100"}http://alertmanager:9093/api/v1/alerts6.7.4 添加新警报和模板#模板Prometheus警报groups:- name: prometheus_alerts rules: - alert: PrometheusConfigReloadFailed expr: prometheus_config_last_reload_successful == 0 for: 10m labels: severity: warning annotations: description: Reloading Prometheus' configuration has failed on {{ labels.name }} on {{ labels.instance }} is no longer active! description: Werner Heisenberg says - "OMG Where's my service?"up{job="node"} == 0avg(up) by (job) <= 0.50sum by job(up) / count(up) <= 0.8- alert: InstancesGone expr: absent(up{job="node"}) for: 10s labels: severity: critical annotations: summary: Host {{ labels.instance }} is no longer reporting! description: Werner Heisenberg says - "OMG Where are my instances?"6.8 路由route: group_by: ['instance'] group_wait: 30s group_interval: 5m repeat_interval: 3h receiver: email routes: - match: severity: critical receiver: pager routes: - match: service: application1 receiver: support_team - match_re: severity: ^(informational|warning)

第7章 可靠性和可扩展性

  • 分为两个问题进行考虑
  1. 可靠性和容错性
  2. 可扩展性

7.1 可靠性和容错性

  • 通常的实现方式是构建集群。但是,集群解决方案需要相对复杂的网络,并且需要解决集群中节点之间的状态管理问题
  • Prometheus架构认为,实现集群所需的投入以及维护集群节点之间数据一致性的成本要高于数据本身的价值
  • Prometheus推荐的容错解决方案是并行运行两个配置相同的Prometheus服务器,并且这两个服务器同时处于活动状态。该配置生成的重复警报可以交由上游Alertmanager使用其分组(及抑制)功能进行处理。一个推荐的方法是尽可能使上游Alertmanager高度容错,而不是关注Prometheus服务器的容错能力
  • 这种方法可以通过创建一个Alertmanager集群来实现的。所有Prometheus服务器会向所有的Alertmanager发送警报。Alertmanager负责去除重复数据并通过集群共享警报状态
  • 这种方法有明显的缺点。首先,两个Prometheus服务器都会收集指标,以加倍该集合可能产生的工作负载。其次,如果某个Prometheus服务器出现故障或中断,那么另一台服务器就会存在数据缺失,在查询该服务器上的数据时会发现这一差距
  • 提示:有多种方法可以在PromQL中对上述问题进行修补。例如,当请求来自两个源的同一指标值 时,你可以通过max by获取两个指标的最大值。或者,当单个工作分片可能存在差距的警报发生时,你可以增加for子句以确保有多个值
7.1.1 重复的Prometheus服务器
  • 两个重复的Prometheus服务器的细节,使用配置管理工具可以相对容易实现这一点
7.1.2 设置Alertmanager集群
  • Alertmanager包含由HashiCorp Memberlist库(https://github.com/hashicorp/memberlist)提供的集群功能。Memberlist是一个Go语言库,使用基于gossip的协议来管理集群成员和成员故障检测,其也是SWIM协议的扩展(http://arvix.org/abs/1707.00788)
  • 我们在每个主机上安装Alertmanager,将使用am1主机来启动集群
  • 代码清单:启动Alertmanager集群
  • 我们运行alertmanager二进制文件来指定一个配置文件,以及一个集群监听地址和端口。你需要在集群中的每个节点上使用相同的配置,这样可以确保对警报的处理是相同的,并且确保集群的一致性
  • 警告:所有Alertmanager应使用相同的配置!如果不相同,那么集群实际上并不是高可用的
  • 我们指定了am1主机的IP地址172.19.0.10和8001端口。Alertmanager集群中的其他节点将使用这个地址和羊肉串连接到集群,因此该端口需要在Alertmanager集群节点之间的网络上保持可访问状态
  • 提示:如果未指定集群监听地址,则默认为0.0.0.0的9094端口
  • 在其他两台主机上运行Alertmanager,监听它们的本地IP地址,并引用刚刚创建的集群节点的IP地址和端口
  • 代码清单:启动Alertmanager集群的其他节点
  • 可以看到我们在另外两个Alertmanager主机(am2和am3)上同样运行alertmanager二进制文件,并使用各自的IP地址和8001端口来为每台主机指定一个集群监听地址。还使用集群cluster.peer参数来指定am1节点的IP地址和端口作为peer,以便它们可以加入集群
  • 可以在Alertmanager的控制台状态页面/status上进行确认。我们来看看主机am1上显示的集群状态https://172.19.0.10:9093/status

7.1.3 为Prometheus配置Alertmanager集群 alerting: alertmanagers: - static_configs: - targets: - am1:9003 - am2:9003 - am3:9003

alerting: alertmanager: - dns_sd_configs: - names: ['_alertmanager._tcp.example.com']


7.2 可扩展性 7.2.1 功能扩展

7.2.2 水平分片 scrape_configs: - job_name: 'federate' scrape_interval: 15s honor_labels: true metrics_path: '/federate' params: 'match[]': - '{job="prometheus"}' - '{__name__=~"job:.*"}' static_configs: - targets: - 'source-prometheus-1:9090' - 'source-prometheus-2:9090' - 'source-prometheus-3:9090'


  • 主节点不仅可以提取聚合指标,还可以为Grafana等工具暴露指标或者作为可视化的默认数据源
  • 这种扩展方式存在风险和限制,最显而易见的是,你需要从工作节点中抓取一部分指标,而不是大量或正在收集的所有指标。这是一个类似金字塔的层级结构,而不是分布式的层级结构。此外,你还需要考虑主节点对工作节点的抓取请求负载
  • 还需要担心主节点与工作节点之间的连接,而不仅仅是工作节点与目标之间的连接。这可能会降低解决方案的可靠性
  • 最后,数据的一致性和正确性也可能会降低。工作节点正在根据设定的间隔抓取目标,而你的主节点也要抓取工作节点。这会导致到达主节点的结果出现延迟,并可能导致数据化作或警报延迟
  • 两个问题的后果是,在主节点上集中警报可能不是一个好主意。相反,应该将警报推送到工作节点上,在那里更有可能识别出问题,或者减少识别警报条件和触发警报之间的滞后
  • 注意:水平分片通常是最后的选择。我们希望在你需要以这种方式扩展之前,每个目标都有数万个目标或大量时间序列
  • 水平分片
  • 当单个作业包含数千个实例时,可以考虑另一种方案:水平分片。水平分片使用一系列工作节点(worker),每个节点都抓取一部分目标。然后,我们在工作节点上汇总感兴趣的特定时间序列。例如,若我们正在监控主机指标,则可能会汇总这些指标的子集。然后,主节点(primary)使用Prometheus federation API来抓取每个工作节点的聚合指标(https://prometheus.io/docs/prometheus/latest/federation/)
  • 如果需要对某些区域或功能进行整体视图查看,那么你可以使用联合功能(federation)将时间序列撮到集中的Prometheus服务器。Grafana支持从多个Prometheus服务器撮数据来构建图形,允许在可视化级别联合来自多个服务器的数据,前提是收集的时间序列具有一定的一致性(https://grafana.com/docs/grafana/latest/features/datasources/mixed/#mixed-data-source
  • 功能扩展使用分片将监控内部分布到不同的Prometheus服务器上。例如,可以通过地理位置或者逻辑域来拆分服务器
  • 或者可以通过特定功能,将所有基础设施监控发送到一台服务器,而将所有应用程序监控发送到另一台服务器
  • 按功能分片
  • Prometheus环境扩展通常有两种形式:功能扩展或水平扩展
  • 在这里,Prometheus将查询alertmanager.example.com SRV记录以返回Alertmanager列表。我们可以使用其他服务发现机制,让Prometheus识别集群中的所有Alertmanager
  • 如果现在重新启动Prometheus,那么我们可以在Prometheus服务器的状态页面中看到所有连接的Alertmanager
  • 现在,当有警报时,它将被发送到所有已发现的AlertmanagerAlertmanager接收警报、处理数据去重并共享 状态
  • Prometheus连接的Alertmanager集群
  • 我们以SRV记录的形式指定了名为_alertmanagerTCP服务。我们的记录返回三个主机名am1、am2和am3,以及端口号9093(Prometheus可以在这里找到一个正在运行的Alertmanager)。让我们配置Prometheus服务器来发现它们
  • 代码清单:Alertmanager服务发现
  • 我们可以为每个Alertmanager添加DNS SRV记录
  • 代码清单:Alertmanager SRV记录
  • Alertmanager集群本身负责与集群的其他活动成员共享所有收到的警报,并处理数据去重(如果需要)。因此,你不应该为Alertmanager设置负载平衡,因为Prometheus会帮你处理
  • 代码清单:Alertmanager静态定义
  • 可以在一个Alertmanager上设置silence并查看配置是否复制到其他Alertmanager节点,以此来测试集群是否正常工作。为此,请单击am1上的New Silence按钮并设置silence,然后检查am2和am3上的/silences路径,应该可以看到所有主机上都复制了相同的silence配置

第8章 监控应用程序

  • 首先,考虑的一些高级设计模式和原则

8.1 应用程序监控入门

  • 应用程序开发中存在一种常见的反模式,即把监控和其他运维功能(如安全性)视为应用程序的增值组件而非核心功能。但监控(和安全性)应该是应用程序的核心功能。如果你要为应用程序构建规范或用户故事,则请把对应用程序每个组件的监控包含进去。不构建指标或监控将存在严重的业务和运营风险,这将导致
  1. 无法识别或诊断故障
  2. 无法衡量应用程序的运行性能
  3. 无法衡量应用程序或组件的业务指标以及成功与否,例如跟踪销售数据或交易价值
  • 另一种常见的反模式是监控力度不足,我们始终建议你尽全力监控应用程序。人们经常会抱怨数据太少,但很少会担心数据太多
  • 注意:在存储容量的限制范围内,因超出容量而导致监控停止工作显然是不可取的。一种有效的方法是首先关注并修改保留时间,以便在减少存储的同时又不丢失有用的信息
  • 第三点需要注意的是,如果你使用多个环境(例如开发、测试、预生产和生产),那么请确保为监控配置提供标签,以便明确数据来自哪个特定环境,这样就可以对监控和指标进行分区
8.1.1 从哪里开始
  • 开始为应用添加监控,一个不错的选择是程序的入口和出口。例如
  1. 测量请求和响应的数量和时间,例如特定网页或API端点。如果你正在监控现有应用程序,那么可以创建一个特定网页或端点的优先级列表,并按重要性顺序对其进行监控
  2. 测量对外部服务和API的调用次数和时间,例如,如果你的应用程序使用数据库、缓存或搜索服务,或者使用第三方服务(如支付网关)
  3. 测量作业调度、执行和其他周期性事件(如cron作业)的数量和时间
  4. 测量重要业务和功能性事件的数量和时间,例如正在创建的用户或者支付和销售等交易
8.1.2 监控分类
  • 你应该通过应用程序、方法、函数或类似票房对指标进行分类并清晰地标识它们,以便了解指标生成的内容和位置

8.2 指标

  • 我们使用以下两种类型的指标,尽管它们之间有很多重叠
  1. 应用程序指标:通常用于衡量应用程序代码的状态和性能
  2. 业务指标:通常用于衡量应用程序的价值,例如电子商务网站上的销售量
  • 这两种指标的示例,Prometheus会倾向于关注可即刻获取的指标。对于长期业务指标,在许多情况下,你可能会使用基于事件的系统
8.2.1 应用程序指标
  • 应用程序指标可以衡量应用程序的性能和状态,包括应用程序最终用户的体验,如延迟和响应时间
  • 提示:一些好的衡量应用程序性能的例子是之前提到的USERED方法,以及Google黄金指标
  • 我们还会查看应用程序的功能和状态,一个很好的例子可能是成功的登录,或者错误、崩溃和失败。我们还可以测量诸如作业 、电子邮件或其他异步活动等的数量和性能
8.2.2 业务指标
  • 业务指标是应用程序指标的更进一层,它们通常与应用程序指标同义。如果你考虑将对特定服务的请求数量作为应用程序指标进行测量,那么业务指标通常会对请求的内容执行某些操作
8.2.3 放置指标
  • 一旦知道了我们想要监控和测量的内容,就需要确定将指标放在何处。在绝大多数情况下,放置这些指标的最佳位置是在我们的代码中,尽可能接近试图监控或测量的操作
  • 我们想要创建一个实用程序库:一个允许我们从集中设置创建各种指标的函数。这有时被称为实用程序模式:一个metrics-utility类,它不需要实例化,只包含静态方法
8.2.4 实用程序模式
  • 常见的模式是使用客户端创建实用程序库或模块(https://prometheus.io/docs/instrumenting/clientlibs/)。实用程序库将暴露一个允许 我们创建和增加指标的API
  • 我们创建了一些Ruby风格的代码来演示,假设已经创建了一个名为Metric的实用程序库
  • 代码清单:一个付款方法的示例
代码语言:javascript
复制
include Metric
def pay_user(user, amount)
  pay(user.account, amount)
  Metric.increment 'payment'
  Metric.increment "payment-amount, #{amount.to_i}"
  send_payment_notification(user.email)
end

def send_payment_notification(email)
  send_email(payment, email)
  Metric.increment 'email-payment'
end
  • 第一个方法中增加两个指标的值
  1. payment指标:在每次付款时都会增加指标的值
  2. payment-amount指标:该指标按金额记录每笔付款
  • 第二种方法send_payment_notification来发送一封电子邮件,其中增加了第三个指标email-payment的值。email-payment指标用于计算发送的付款电子邮件的数量
8.2.5 外部模式
  • 如果你不能控制代码库,无法在代码中插入监控或测量内容,或者可能无法更改或更新旧应用程序,那么该怎么办?你需要找到距离应用程序最近的位置。最显著的地方是应用程序周围的输出和外部子系统,例如数据库或缓存
  • 如果应用程序发出日志,那么可以识别日志包含的内容,并查看是否可以使用这些内容来衡量应用程序的行为
8.2.6 监控应用程序示例
  • mwp-rails应用程序例子:https://github.com/turnbullpress/mwp-rails
  • 首先需要使用基于Ruby的客户端添加对Prometheus的支持(https://github.com/prometheus/client_ruby
添加客户端
  • 代码清单:mwp-rails Gemfile source 'https://rubygems.org' ruby '2.4.2' gem 'rails', '5.1.5' ... gem 'prometheus-client' ... prometheus = Prometheus::Client.registry test_counter = prometheus.count(:test_count, 'A test counter') test_counter.increment test_counter.get 1.0 test_counter = prometheus.counter(:test_counter, 'A test counter') test_gauge = prometheus.gauge(:test_gauge, 'A test gauge') test_histogram = prometheus.histogram(:test_histogram, 'A test histogram') test_summary = prometheus.summary(:test_summary, 'A test summary') 将监控添加到Rails touch lib/metrics.rb module Metrics def self.counter(name, docstring, base_labels = {}) provide_metric(name) || registry.counter(name, docstring, base_labels) end def self.summary(name, docstring, base_labels = {}) provide_metric(name) || registry.summary(name, docstring, base_labels) end def self.gauge(name, docstring, base_labels = {}) provide_metric(name) || registry.summary(name, docstring, base_labels) end def self.histogram(name, docstring, base_labels = {}, buckets = ::Prometheus::Clien::Histogram::DEFAULT_BUCKETS) provide_metric(name) || registry.histogram(name, docstring, base_labels, buckets) end private def self.provide_metrics(name) registry.get(name) end def. self.registry @registry || ::Prometheus::Client.registry end end touch config/initializers/lib.rb
    • 我们可以为一些方法添加指标,让我们从删除用户的计数器开始
    • 代码清单:删除用户的计数器
    • 还可以使用increment方法添加标签或者指定增量,如下所示
    • 还可以为所建用户构建另一个计数器并添加到User模型
    • 代码清单:创建用户的计数器
    • 我们需要暴露要抓取的指标,还将启用Rack中间件来自动创建一些有关HTTP请求的有用指标。在示例中,我们通过以config.ru文件内添加exporter(和中间件收集器)来启用指标端点
    • 代码清单:将Prometheus添加到config.ru文件中
    • exporter会创建一个路径/metrics,其中包含由应用程序定义的Prometheus注册表中指定的所有指标。惧器将一些HTTP服务器指标添加到通过Rack中间件收集的端点
    • 代码清单:Rails/metrics端点
    • 现在应用程序已生成指标,我们可以在Prometheus中使用它们。让我们创建一个作业来抓取/metrics端点,然后把Rails服务器添加到基于文件的服务发现中,按主机名添加3个Rails服务器
    • 代码清单:Rails服务器的服务发现
    • 接下来通过prometheus.yml配置文件创建新的作业
    • Rails服务器目标
    • 可在仪表板中查看新指标
    • 然后在程序中添加对metrics库的依赖
    • 代码清单:文件/config/initializers/lib.rb require 'metrics' def destroy user = User.find(params[:id]) user.destroy Metrics.counter(:users_deletes_counter, "Deletes users counter").increment redirect_to users_path, :notice => "User deleted." end .increment({service: 'foo'}, 2) class User < ActiveRecord::Base enum role: [:user, :vip, :admin] after_initialize :set_default, :if => :new_record? after_create do Metrics.counter(:user_created_counter, "Users created counter").increment end end require 'prometheus/middleware/collector' require 'prometheus/middleware/exporter' use Prometheus::Middleware::Collector use Prometheus::Middleware::Exporter 使用指标 - job_name: rails file_sd_configs: - files: - targets/rails/*.json refresh_interval: 5m

    • 需要扩展Rails以加载metrics库,有几种方法可以做到这一点,但我最喜欢使用添加初始化程序的方式
    • 代码清单:为metrics库创建初始化程序
    • 代码清单:Metrics模块
    • 我们不希望每次记录指标时,都需要手动创建注册表和指标,所以用实用程序代码来执行此操作
    • 代码清单:创建Metrics模块
    • 可以注册多种类型的指标,包括摘要和直方图
    • 代码清单:基本的Prometheus client_ruby指标
    • 代码清单:查询指标的值
    • 使用increment方法来增加指标的值
    • 代码清单:增加指标的值
    • 注册表是Prometheus应用程序监控的核心,你创建的每个指标都需要先注册。我们已创建了一个名为prometheus的注册表,现在可以在此注册表中创建指标
    • 代码清单:注册Prometheus指标
    • 然后使用bundle命令安装新的gem
    • 代码清单:使用bundle命令安装prometheus-client
    • 然后可以使用Rails控制台来测试客户端,现在通过rails c命令启动一个客户端
    • 代码清单:使用Rails控制台测试Prometheus客户端
    • 已经启动一个控制台,并使用以下代码创建一个Prometheus注册表(registry
    • 代码清单:创建Prometheus注册表

第9章 日志监控

  • 虽然我们的主机、服务和应用程序可以生成关键指标和事件,但它们也会生成日志,这些日志可以告诉我们其状态的有用信息
  • 特别是对于没有设置监控或者不容易进行监控的遗留应用程序,有时重写、修补或重构该应用程序以暴露内部状态的成本绝对不是一项有利的工程投资,或者还可能存在监控上的技术限制。但是你仍然需要了解应用程序内部发生的情况,最简单的方法之一是调整日志输出
  • 提示:另一种方法是使用Process exporter查看/proc子系统的内容(https://github.com/ncabatoff/process-exporter
代码语言:javascript
复制
docker run -d --rm -p 9256:9256 --privileged -v /proc:/host/proc -v `pwd`:/config ncabatoff/process-exporter --procfs /host/proc -config.path /config/filename.yml

9.1 日志处理

  • 为了从日志条目中提取数据,我们将使用日志处理工具。处理工具有很多种选择,包括Grok Exporterhttps://github.com/fstab/grok_exporter)和名为mtailGoogle实用程序(https://github.com/google/mtail)。我们选择mtail,因为它更轻巧,也更受欢迎
  • 提示:你是否安装了Logstash或者ELK?目前它们无法直接输出到Prometheus,但你可以使用Logstash的指标过滤器来创建指标并将其直接输出到Alertmanagerhttps://github.com/wtliuNA/logstash-output-prometheus

9.2 mtail简介

  • mtail日志处理器是由GoogleSRE人员编写的,其采用Apache2.0许可证,并且使用Go语言。mtail日志处理器专门用于从应用程序日志中提取要导出到时间序列数据库中的指标
  • mtail日志处理器通过运行“程序”(program)来工作,它定义了日志匹配模式,并且指定了匹配后要创建和操作的指标。它与Prometheus配合得很好,可以暴露任何要抓取的指标,也可以配置为将指标发送到collectdStatsDGraphite等工具
9.2.1 安装mtail
  • 代码清单:下载并安装mtail二进制文件
代码语言:javascript
复制
wget https://github.com/google/mtail/releases/download/v3.0.0-rc33/mtail_v3.0.0-rc33_linux_amd64 -0 mtail
chmod 0755 mtail
sudo cp mtail /usr/local/bin
  • 代码清单:运行mtail二进制文件
代码语言:javascript
复制
mtail --version
9.2.2 使用mtail
  • 首先创建一个目录来保存mtail程序
  • 代码清单:创建一个mtail程序目录
代码语言:javascript
复制
sudo mkdir /etc/mtail
  • 代码清单:创建line_count.mtail程序
代码语言:javascript
复制
sudo touch /etc/mtail/line_count.mtail
  • 代码清单:编辑line_count.mtail程序
代码语言:javascript
复制
counter line_count

/$/ {
  line_count++
}
  • 我们定义了一个名为line_count的计数器。计数器名称以counter为前缀(自然地,测量型以gauge为前缀)。这让计数和测量通过mtail导出到你定义的任何目的地
  • 我们定义mtail程序的内容:匹配的条件和采取的操作;首先指定条件,然后执行以下操作,包含在{}中
  • 你可以在程序中指定多组条件和操作,也可以使用条件逻辑以else子句的形式扩展(https://github.com/google/mtail/blob/master/docs/Language.md
代码语言:javascript
复制
/foo/ {
  ACTION1
} else {
  ACTION2
}
9.2.3 运行mtail
  • 代码清单:运行mtailsudo mtail --progs /etc/mtail --logs '/var/log/*.log'func Matchfunc Match(pattern, name string) (matched bool, err error)Match reports whether name matches the shell file name pattern. The pattern syntax is:pattern:{ term }term: '*' matches any sequence of non-Separator characters '?' matches any single non-Separator character '[' [ '^' ] { character-range } ']' character class (must be non-empty) c matches character c (c != '*', '?', '\\', '[') '\\' c matches character ccharacter-range: c matches character c (c != '\\', '-', ']') '\\' c matches character c lo '-' hi matches character c for lo <= c <= hiMatch requires pattern to match all of name, not just a substring. The only possible returned error is ErrBadPattern, when pattern is malformed.On Windows, escaping is disabled. Instead, '\\' is treated as path separator.9.3 处理Web服务器访问日志## Parser for the common apache "NCSA extended/combined" log format## LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"counter apache_http_requests_total by request_method, http_version, request_statuscounter apache_http_bytes_total by request_method, http_version, request_status/^/ +/(?P<hostname>[0-9A-Za-z\.:-]+) / + # %h/(?P<remote_logname>[0-9A-Za-z-]+) / + # %l/(?P<remote_username>[0-9A-Za-z-]+) / + # %u/\[(?P<timestamp>\d{2}\/\w{3}\/\d{4}:\d{2}:\d{2}:\d{2} (\+|-)\d{4})\] / + # %u/"(?P<request_method>[A-Z]+) (?P<URI>\S+) (?P<http_version>HTTP\/[0-9\.]+)" / + # \"%r\"/(?P<request_status>\d{3}) / + # %>s/((?P<response_size>\d+)|-) / + # %b/"(?P<referer>\S+)" / + # \"%{Referer}i\"/"(?P<user_agent>[[:print:]]+)"/ + # \"%{User-agent}i\"/request_method][http_version][request_status]++ response_size > 0 { apache_http_bytes_total[request_method][http_version][request_status] += response_size }}counter apache_http_requests_total by request_method, http_version, request_statuscounter apache_http_bytes_total by request_method, http_version, request_statusConstant pattern fragmentsTo re-use parts of regular expressions, you can assign them to a const identifier:const PREFIX /^\w+\W+\d+ /PREFIX { ACTION1}PREFIX + /foo/ { ACTION2}In this example, ACTION1 is done for every line that starts with the prefix regex, and ACTION2 is done for the subset of those lines that also contain 'foo'.Pattern fragments like this don't need to be prefixes, they can be anywhere in the expression.counter maybe_ipv4const IPv4 /(?P<ip>\d+\.\d+\.\d+\.\d+)//something with an / + IPv4 + / address/ { maybe_ipv4++}(?P<request_status>\d{3})Capture GroupsRegular expressions in patterns can contain capture groups -- subexpressions wrapped in parentheses. These can be referred to in the action block to extract data from the line being matched.For example, part of a program that can extract from rsyncd logs may want to break down transfers by operation and module.counter transfers_total by operation, module/(?P<operation>\S+) (\S+) \[\S+\] (\S+) \(\S*\) \S+ (?P<bytes>\d+)/ { transfers_total[operation][3]++}Or, the value of the counter can be increased by the value of a capture group:counter bytes_total by operation, module/(?P<operation>\S+) (\S+) \[\S+\] (\S+) \(\S*\) \S+ (?P<bytes>\d+)/ { bytes_total[operation][3] += bytes}Numeric capture groups address subexpressions in the match result as you might expect from regular expression groups in other languages, like awk and perl -- e.g. the expression 3 refers to the third capture group in the regular expression.Named capture groups can be referred to by their name as indicated in the regular expression using the ?P<name> notation, as popularised by the Python regular expression library -- e.g. bytes refers to (?P<bytes>\d+) in the examples above.Capture groups can be used in the same expression that defines them, for example in this expression that matches and produces x > 1 { nonzero_positives++}{ apache_http_requests_total[http_version][request_status]++ apache_http_bytes_total[request_method][http_version][request_status] += response_size}
    • 然后浏览/metrics路径
    • 代码清单:Apache combined指标
    • 可以看到一组新的计数器,每个方法都有一个计数器和HTTP响应代码维度。我们还可以执行更复杂的操作,例如构建直方图
    • 代码清单:rails程序(https://github.com/google/mtail/blob/master/examples/rails.mtail
    • 首先定义已启动、已完成的请求计数器;然后看到一个条件和操作;接下来计算完成的请求;我们捕获状态码和请求时间,并使用这些数据来计算按状态创建请求时间和请求计数的总和
    • 代码清单:Rails mtail指标输出
    • 可以看到,针对不同请求方法和总数的计数器,以及对已完成请求的总烽和按状态码请求的总数的统计
    • 我们现在有了两个mtail程序,可以通过多种方式部署它们。我们建议为每个应用程序运行一个mtail实例,并作为依赖项通过配置管理部署在应用程序周围。这种模式通常被称为边车(sidecar)模式,非常适合容器化应用
    • 也可以在一个mtail实例中运行多个程序,但有一点需要注意,mtail会在传递给它的每个日志文件上运行每个程序,这可能会对主机产生性能影响
    • 我们已经暴露了一些指标,接下来创建一个Prometheus作业抓取它们
    • 代码清单:mtail作业
    • 作业使用基于文件的服务发现方式来定义几个目标,一个Web服务器和一个Rails服务器,两个目标都在端口3903上被抓取
    • 代码清单:工作文件发现
    • 操作会递增第一个计数器apache_http_requests_total,将一些前缀为$的捕获添加到计数器中作为维度。每个维度都包含在[]方括号中
    • 第二个计数器有一个加法运算,使用+=运算符将每个新的响应大小(以字节为单位)添加到计数器
    • 如果我们再次运行mtail,这次加载一些Apache(或其他使用combined日志格式的Web服务器),那么会看到这些新生成的指标
    • 代码清单:运行mtailsudo mtail --progs /etc/mtail --logs '/var/log/apache/*.access'9.4 解析Rails日志到直方图counter rails_requests_started_totalcounter rails_requests_started by verbcounter rails_requests_completed_totalcounter rails_requests_completed by statushistogram rails_requests_completed_seconds by status buckets 0.005, 0.01, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 15.0/^Started (?P<verb>[A-Z]+) .*/ { ### # Started HTTP requests by verb (GET, POST, etc.) # rails_requests_started_total++ rails_requests_started[status]++ ### # Completed requests by status with histogram buckets # # These statements "fall through", so the histogram is cumulative. The # collecting system can compute the percentile bands by taking the ratio of # each bucket value over the final bucket. rails_requests_completed_seconds[
    • 这些是命名捕获组(named capture group)。在示例中,我们捕获request_status的命名值,然后可以在操作中使用这些捕获
    • 代码清单:combined访问日志操作
    • 这些是命名捕获组(named capture group)。在示例中,我们捕获request_status的命名值 ,然后可以在操作中使用这些捕获
    • 在这些正则表达式中,你可以看到一系列捕获,如下所示
    • by运算符指定要添加到指标的其他维度。在第一个计数器apache_http_requests_total中,我们添加了request_methodhttp_versionrequest_status的附加维度,这些维度将作为标签添加到结果计数器中
    • 提示:在解析复杂的日志行时,这些正则表达式也会变得非常复杂,因此mtail还允许你通过将正则天工定义为常量来重用它们
    • 大量其他示例程序:https://github.com/google/mtail/tree/master/examples
    • 定义了两个计数器
    • 使用mtailApache访问日志中撮一些指标,特别是使用combined日志格式的指标
    • 代码清单:apache_combined程序
    • 可以将--emit_prog_label参数设置为false来省略此标签
    • 注意:运行mtail的用户需要针对正在解析的日志文件的权限,否则mtail将无法读取文件。当无法读取文件时,你将在使用--logtostderr参数获得的mtail日志输出中看到读取错误
    • 它将在端口3903上启动Web服务器(可以使用--address--port参数来设置IP地址和端口)。浏览一下这个Web服务器,根路径下会显示一些诊断信息
    • 提示:你还可以将指标发送到StatsDGraphite等工具
    • 代码清单:mtail/metrics路径
    • 第一个参数--progs告诉mtail在哪里找到我们的程序,第二个参数--logs告诉mtail在哪里找到要解析的日志文件。我们使用glob模式(https://godoc.org/path/filepath#Match)来匹配/var/log目录中的所有日志文件。你可以指定以逗号分隔的文件列表,也可以多次指定--logs参数
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-06-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 yeedomliu 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 第1章 监控简介
    • 1.1 什么是监控
      • 1.1.1 技术作为客户
      • 1.1.2 业务作为客户
    • 1.2 监控基础知识
      • 1.2.1 事后监控
      • 1.2.2 机械式监控
      • 1.2.3 不够准确的监控
      • 1.2.4 静态监控
      • 1.2.5 不频繁的监控
      • 1.2.6 缺少自动化或自服务
      • 1.2.7 监控模式总结
      • 1.3 监控机制
      • 1.3.1 探针和内省
      • 探针(probing)
      • 内省(introspection)
      • 1.3.2 拉取和推送
      • 1.3.3 监控数据的类型
    • 1.4 指标
      • 1.4.1 什么是指标
      • 1.4.2 指标类型
      • 测量型
      • 计数型
      • 1.4.3 指标摘要
      • 1.4.4 指标聚合
      • 平均值
      • 中间数
      • 标准差
      • 百分位数
    • 1.5 监控方法论
      • CPU
      • 内存
    • 1.6 警报和通知
      • 1.7 可视化
        • 1.9 本书内容
          • 1.10 小结
            • 资料
            • 第2章 Prometheus简介
              • 2.1 Prometheus起源
                • 2.2 Prometheus架构
                  • 资料
              • 第3章 安装和启动Prometheus
                • 3.1 安装Prometheus
                  • 3.1.4 在Mac OS X上安装Prometheus
                  • 3.1.5 通过监控套件安装Prometheus
                • 3.3 启动Prometheus
                • 第4章 监控主机和容器
                  • 4.1 监控节点
                    • Exporter
                • 第5章 服务发现
                  • 5.1 静态配置的局限性
                    • 5.2 基于文件的服务发现
                      • 编写文件
                    • 5.3 基于API的服务发现
                      • 5.4 基于DNS的服务发现
                      • 第6章 警报管理
                        • 6.1 警报
                          • 6.2 Alertmanager如何工作
                            • 6.3 安装Alertmanager
                              • 6.4 配置Alertmanager
                                • 6.5 运行Alertmanager
                                • 第7章 可靠性和可扩展性
                                  • 7.1 可靠性和容错性
                                    • 7.1.1 重复的Prometheus服务器
                                    • 7.1.2 设置Alertmanager集群
                                • 第8章 监控应用程序
                                  • 8.1 应用程序监控入门
                                    • 8.1.1 从哪里开始
                                    • 8.1.2 监控分类
                                  • 8.2 指标
                                    • 8.2.1 应用程序指标
                                    • 8.2.2 业务指标
                                    • 8.2.3 放置指标
                                    • 8.2.4 实用程序模式
                                    • 8.2.5 外部模式
                                    • 8.2.6 监控应用程序示例
                                    • 添加客户端
                                • 第9章 日志监控
                                  • 9.1 日志处理
                                    • 9.2 mtail简介
                                      • 9.2.1 安装mtail
                                      • 9.2.2 使用mtail
                                      • 9.2.3 运行mtail
                                  相关产品与服务
                                  Prometheus 监控服务
                                  Prometheus 监控服务(TencentCloud Managed Service for Prometheus,TMP)是基于开源 Prometheus 构建的高可用、全托管的服务,与腾讯云容器服务(TKE)高度集成,兼容开源生态丰富多样的应用组件,结合腾讯云可观测平台-告警管理和 Prometheus Alertmanager 能力,为您提供免搭建的高效运维能力,减少开发及运维成本。
                                  领券
                                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档