1. 为什么一定要用 Grafana 重做 Ambari 的监控看板
先说个场景。你在维护一套带有 Ambari 的 Hadoop 集群,Ambari Web UI 里确实能看到 CPU、内存、HDFS 容量、YARN 应用数,但真正用起来会很难受:时间粒度只能跟着 UI 的固定选项走,想同时对比 HDFS 已用容量和剩余容量要来回切页面,更别说把多个服务指标叠到同一张图里观察因果。Ambari 自带的监控面板更像是"能看",离"好用"差得很远。
Ambari-Metrics 本身就是一套独立运行的指标采集服务,它会从集群各节点收集 CPU、内存、磁盘、HDFS、YARN、HBase 等组件的指标,并对外暴露 HTTP 接口,默认端口是 6188。也就是说数据一直都在,只是 Ambari Web UI 替我们画了一部分图而已。如果我们能直接从这个 HTTP 接口取数,再交给 Grafana 做面板和告警,等于绕过了 Ambari UI 的限制,自由度会大很多。
Grafana 这边的关键角色是 Infinity 数据源插件。它允许我们在不写后端服务的情况下,把任意返回 JSON 的 REST 接口直接变成 Grafana 可视化数据源。放在这个场景里就是:Grafana -> Infinity -> Ambari-Metrics Collector 的 6188 接口,一步到位。相比自己写一个 Grafana 数据源插件或额外接一套 Prometheus,这个组合可以把搭建时间压缩到半小时以内,特别适合快速做 DEMO、临时验证、向团队展示监控效果。
这篇博文适合两类人:一类是天天面对 Ambari 集群但不想再引入新组件的运维工程师,另一类是想学习 Grafana + Infinity 这套"免后端取数"玩法的开发者。我会按"先看接口、再配数据源、最后搭面板"的顺序完整走一遍,并把我在实际操作中遇到的时间戳、JSON 解析、宏替换等问题一并写出来。
2. 动手前先摸清 Ambari-Metrics Collector 的 API 长相
很多人第一次接触 Ambari-Metrics,都是直接去 Grafana 里配插件,配完发现拉到一堆解析错误,原因就是没先单独验证接口。Infinity 本质上是在替我们发 HTTP 请求,所以接口本身必须能通、参数必须对,后面这一切才成立。
2.1 核心接口与关键参数
Ambari-Metrics Collector 对外提供的是 Hadoop Timeline 风格的 REST API,核心路径是:
http://<AMS_HOST>:6188/ws/v1/timeline/metrics其中<AMS_HOST>通常是部署 Ambari Metrics Collector 的那台服务器,不是随便哪台 NameNode 或 DataNode。生产环境里,如果集群开启了 Kerberos 认证,请求还需要带认证信息;内网快速验证时,Ambari 默认配置下通常可以直接访问。
这个接口常用的查询参数如下:
| 参数 | 说明 | 典型值 |
|---|---|---|
| metricName | 指标名,可逗号分隔传多个 | cpu_user, cpu_idle |
| appId | 指标所属应用的标识 | HOST、HDFS、YARN、HBASE |
| hostname | 主机名,不传通常代表聚合整个集群 | hadoop001 或留空 |
| startTime | 开始时间,Unix 毫秒时间戳 | 1690000000000 |
| endTime | 结束时间,Unix 毫秒时间戳 | 1690003600000 |
| precision | 数据点聚合粒度,单位秒 | 60、300、3600 |
一个容易被忽略的点是precision。AMS 收集原始指标之后,会根据时间维度做聚合,precision=60表示返回 1 分钟一个点,precision=3600表示返回 1 小时一个点。如果不传或者传得不合适,要么数据点爆炸,要么曲线锯齿严重,所以后面配置 Infinity 查询时,我习惯把precision和 Grafana 面板的$__interval绑定。
2.2 先 curl 一下再往下走
我强烈建议在浏览器或命令行先验证一次。比如要看整个集群的 HDFS 已用容量,可以这么试:
curl -s "http://<AMS_HOST>:6188/ws/v1/timeline/metrics?metricName=dfs_capacity_used&appId=HDFS&startTime=1690000000000&endTime=1690003600000&precision=300" | jq .正常情况下,返回的 JSON 大致长这样:
[ { "metricname": "dfs_capacity_used", "appid": "HDFS", "instanceid": "hadoop001.hadoop.com", "starttime": "1690000000000", "metrics": [ { "timestamp": "1690000000000", "value": "1024000000" }, { "timestamp": "1690000300000", "value": "1024005000" } ] } ]也就是一个数组,每个元素里metricname是指标名,appid是应用标识,instanceid是数据来源主机,metrics这个数组里装的才是真正的时序数据,其中value在 JSON 里往往是字符串,后面在 Infinity 里要转成数字才能绘图。
这一步很关键:你只有亲眼看到返回结构,才能知道 Infinity 里的rows路径怎么写。不要凭记忆猜。
2.3 指标名去哪查
Ambari 里指标名非常多,不同版本还会有差异。最稳妥的办法是去 Ambari Server 的 Metrics Collector 页面上看,或者直接在 AMS 接口里模糊查。我常用的方式是把metricName换成可能的关键字,比如:
curl -s "http://<AMS_HOST>:6188/ws/v1/timeline/metrics?metricName=dfs&appId=HDFS&startTime=<毫秒>&endTime=<毫秒>&precision=3600" | jq '.[].metricname' | sort -u有的环境里这个接口只要任意给一个不存在的指标名就能返回相近列表,有的环境则需要靠 Ambari 页面里"Metrics"标签页辅助。总之,遇到no data或者empty response时,不要先怀疑 Infinity,先怀疑指标名。
3. Grafana 安装 Infinity,并把 AMS 配成数据源
3.1 插件安装:两种方式
Infinity 插件的仓库 ID 是yesoreyeram-infinity-datasource。如果你用的是 Grafana 官方安装包的环境,命令行直接装就行:
grafana cli plugins install yesoreyeram-infinity-datasource systemctl restart grafana-server如果是 Docker 方式跑的 Grafana,可以用环境变量预装插件:
docker run -d \ --name=grafana \ -p 3000:3000 \ -e "GF_INSTALL_PLUGINS=yesoreyeram-infinity-datasource" \ grafana/grafana:latest装完去 Grafana 的"Administration -> Plugins"里确认一下插件状态是 enabled。插件版本不同,界面字段会有细节差异,但总体的查询模型没有变。
3.2 数据源配置要点
进入 "Configuration -> Data sources -> Add data source",搜索Infinity。我习惯把数据源名称命名为Ambari Infinity,方便后面在面板里区分。
配置项里最重要的几个:
| 配置项 | 推荐值 | 说明 |
|---|---|---|
| Name | Ambari Infinity | 自定义 |
| URL | http://<AMS_HOST>:6188 | 作为默认 URL,后续查询里可以再覆盖 |
| Authentication | None 或 Basic Auth | 内网快速验证用 None,生产按需开启 |
| Allowed Hosts | http://<AMS_HOST>:6188 | 如果设置了默认 URL,建议加上白名单 |
| Default URL | 勾选启用 | 让查询只写路径部分即可 |
配完点 "Save & test",Grafana 会发一个探活请求。如果返回 JSON 数组或至少没报连接失败,基本就通了。
这里有个小提示:数据源页面的 URL 如果填了http://<AMS_HOST>:6188,后续 Infinity 查询里可以只写/ws/v1/timeline/metrics?...这种相对路径。但如果你在查询里写全路径,插件会优先使用全路径。我个人的习惯是数据源里配默认地址,查询里只写路径,这样以后换集群地址只需要改一处。
3.3 统一时区,避免时间偏移
AMS 返回的时间戳本身是绝对时间,但 Grafana 面板左上角有时区设置。如果 Grafana 用的是本地时区,而 AMS 集群跑在 UTC,看起来曲线会偏移 8 个小时。DEMO 阶段为了省事,直接在仪表板设置里把时区固定成 UTC,等确认数据对上了,再切换成自己熟悉的时区。
4. 用 Infinity 查询编辑器把 REST 响应变成可绘图的时间序列
很多人在这一步卡住。其实 Infinity 插件并不复杂,关键是搞清楚它面对一份 JSON 时如何"选行"和"选列"。
4.1 Infinity 查询的基本套路
新建一个 Panel 后,数据源选择Ambari Infinity,然后会看到查询编辑器。核心字段通常是这几个:
Query Type: JSON URL: /ws/v1/timeline/metrics?metricName=cpu_user&appId=HOST&startTime=$__from&endTime=$__to&precision=300 Parser: UQL 或 Backend Rows: [*].metrics[*] Columns: 这里写字段映射Rows表示从 JSON 里取哪些节点作为"一行"。以 AMS 返回结构为例,最外层是数组,数组每个元素里又有一个metrics数组,真正包含timestamp和value的是最里层。所以我常用的 Rows 路径是:
[*].metrics[*]意思很直白:先遍历最外层,再展开每个对象的metrics数组。这样每一行就对应一个{ timestamp, value }对象。
Columns是用来把行里的字段映射成 Grafana 面板字段的。常用的写法类似:
timestamp -> time value -> value有的插件版本是自动识别字段,有的则需要手动加。如果自动识别出来没有正确转换,就手动写映射。
4.2 UQL 和字段提取的常见写法
Infinity 在较新版本里提供了 UQL 这种过滤语言。假如你想直接过滤某个指标名并把 value 转成数字,可以这样写:
rows = [*].metrics[*] | { time: .timestamp, value: number(.value), metricname: .metricname }但要注意,[*].metrics[*]取出内层对象后,metricname在外层,如果像我上面那样直接取,能取到吗?不同版本 Infinity 对上下文的处理不太一样。我在实际项目中更稳妥的写法是先在 Rows 里把外层信息带下来,再展开内层,例如:
rows = [*] | { metricname: .metricname, instances: .metrics[*] } rows = .instances[] | { time: .timestamp, value: number(.value), metricname: upper(.metricname) }整体思路就是先把外层字段保存成临时字段,再展开内层数组。如果你用的 Infinity 版本界面不支持这么复杂的 UQL,也没关系,退回到最原始的[*].metrics[*],然后在 Columns 里手动补一个常量字段,或者干脆用外层数组 index 区分多个序列。
4.3 时间宏的传递
Grafana 面板的时间范围会通过$__from和$__to传给查询。AMS 需要的是 Unix 毫秒时间戳,Grafana 在 Infinity 里把这些宏解析出来时,一般也是毫秒,所以可以直接拼:
startTime=$__from&endTime=$__to如果你想按 Grafana 的 Interval 变量动态决定聚合粒度,可以用:
precision=$__interval不过有个细节:Grafana 的$__interval会是类似30s、5m的可读格式,AMS 要的是秒数,所以更稳妥的方式是使用 Infinity 支持的$__interval_ms之类的毫秒宏并自己除以 1000,或者直接在 URL 里写死成60。DEMO 阶段写死 60 完全够用,等产品化时再优化成动态。
列一个"能直接抄"的查询配置模板:
| 配置 | 示例 |
|---|---|
| Query Type | JSON |
| URL | /ws/v1/timeline/metrics?metricName=${metric}&appId=${appId}&startTime=$__from&endTime=$__to&precision=60 |
| Rows | [*].metrics[*] |
| Columns | timestamp映射 time,value映射 value |
有了这个模板,一个指标就能出图了。下面进入实操。
5. 实战演练:从零拼出一个 Ambari 集群监控面板
这个部分我会带着你完整拼 3 个 Panel:CPU 使用率、HDFS 容量、YARN 应用数。都跑通之后,你就拥有一套可以由点及面的 AMS 监控 DEMO 看板。
5.1 先建仪表板和下拉变量
新建 Dashboard,名字叫Ambari Metrics DEMO。先不要急着一个画图,先去 Dashboard Settings -> Variables 里加两个模板变量,这样面板才能灵活切换。
变量 1:
Name: appId Label: 应用 Type: Custom Values: HOST, HDFS, YARN变量 2:
Name: metric Label: 指标 Type: Custom Values: cpu_user, mem_free, dfs_capacity_used, dfs_capacity_remaining, YarnMetrics.RunningApps自定义变量的好处是 DEMO 阶段不依赖 Grafana 去探测真实指标名,避免因为接口权限或指标名差异直接导致下拉列表为空。
5.2 Panel 1:HDFS 容量趋势
这是最直观的一个面板。查询配置如下:
Query Type: JSON URL: /ws/v1/timeline/metrics?metricName=dfs_capacity_used,dfs_capacity_remaining&appId=HDFS&startTime=$__from&endTime=$__to&precision=300 Rows: [*].metrics[*] Columns: timestamp -> time value -> value Result format: Time series这里用逗号同时传两个指标名,AMS 会把两个指标都返回来。但问题在于,Infinity 怎么区分两条时间序列?默认情况下,里层metrics数组展开后只有timestamp和value,面板区分序列名会很吃力,所有点可能串在一起。
解决办法是让 Rows 多保留一层外层信息。我的经验是直接用这样的 UQL:
rows = [*].metrics[*] | { time: .timestamp, value: number(.value), metricname: upper(.metricname) }如果你的插件版本支持metricname上下文透传,那序列名就会自动带上DFS_CAPACITY_USED和DFS_CAPACITY_REMAINING。如果不支持,就在 Columns 里手动增加一个metricname字段,并选择对应的外层路径,这需要试验一次。
面板的Unit建议选择bytes,这样 Y 轴会自动显示 TB/GB,而不是一串裸数字。Legend 里开启Last,能在图例上直接看到最新值。
5.3 Panel 2:CPU 用户态利用率
CPU 指标在 Ambari 里很常见,但要注意,cpu_user这个指标在部分版本里表示的是累计 CPU 时间或百分比需要看具体口径。我建议把它当作"用户态 CPU 使用百分比"来展示,如果发现数值不符合常识,就去 AMS 原始返回里核对单位。
查询配置:
Query Type: JSON URL: /ws/v1/timeline/metrics?metricName=cpu_user&appId=HOST&startTime=$__from&endTime=$__to&precision=60 Rows: [*].metrics[*] Columns: timestamp -> time value -> value Result format: Time series如果集群里有多台机器,AMS 默认返回的是每条主机一个对象。展开后会在同一个图里看到很多条线。这是好事,但 DEMO 阶段可以先在查询里加上hostname=<你某个节点名>把范围缩小,等确认图没问题后再放开到全集群。
CPU 面板的Unit选择percent,Min填 0,Max填 100,看起来更符合直觉。
5.4 Panel 3:YARN 运行中应用数
YARN 的指标名在不同 Ambari 版本里差异比较大,常见的有YarnMetrics.RunningApps、runningApps、ResourceManager.RunningApps这类。我建议先到 AMS 接口里跑一下:
curl -s "http://<AMS_HOST>:6188/ws/v1/timeline/metrics?metricName=YarnMetrics.RunningApps&appId=YARN&startTime=<毫秒_start>&endTime=<毫秒_end>&precision=60" | jq .如果返回空,就换成metricName=runningApps再试。找到真实指标名后,把 URL 里的指标名替换掉即可。
这个面板我把Result format设为Table,因为运行中应用数更适合用实时表格突出当前值,而不是看趋势。Rows 和 Columns 不变,最后在面板右侧加一个stat类型的可视化,并选择最新值字段。
5.5 三个面板拼完后的整体检查
面板都能出数之后,用仪表板右上角的刷新按钮,从 1h 范围逐步扩展到 6h、24h,重点观察:
- 时间轴是否有空档
- 曲线是否有锯齿形跳跃
- Legend 里是否出现大量无法识别的序列名
正常情况下,1h 范围配合precision=60能拿到 60 个点,最适合肉眼验证。如果 24h 范围下数据点过多,AMS 响应会很慢,这时候把precision调成3600,面板依旧流畅。
我把这套配置整理成一个可复制的对照表,方便你按需修改:
| 面板 | URL 关键参数 | Result format | Unit |
|---|---|---|---|
| HDFS 容量 | metricName=dfs_capacity_used,dfs_capacity_remaining&appId=HDFS&precision=300 | Time series | bytes |
| CPU 利用率 | metricName=cpu_user&appId=HOST&precision=60 | Time series | percent |
| YARN 应用数 | metricName=YarnMetrics.RunningApps&appId=YARN&precision=60 | Table | none |
6. 实际运行中总会踩到的几个坑,我从日志里挑着讲
这个 DEMO 我前后做过两次,第一次几乎每一步都卡住。下面这几条不是理论,是真实踩完后的记录。
6.1 Grafana 升级后提示 old query 找不到数据源
你可能会遇到这样一个报错:
failed to upgrade legacy queries datasource im7_otuvz was not found这不是 Infinity 的专属问题,而是 Grafana 在升级时旧面板里记录的数据源 UID 失效了。Grafana 的 Dashboard JSON 里,每个 Panel 的 datasource 会带一个 uid。如果你把 Grafana 从旧版本大版本升级,或者删掉旧数据源重新建了一个同名数据源,uid 就会对不上。
解决办法有两个:
- 把旧面板导出的 JSON 里
datasource的uid改成新数据源的 uid; - 更省事的做法是重新创建一个 Panel,从零选择新数据源,然后重新粘贴查询配置。
我第二次做的时候直接走第二条路,因为面板数量本来就不多,DEMO 阶段没必要在迁移上花时间。
6.2 AMS 不返回历史数据,只有最近一段
AMBARI Metrics Collector 默认对原始指标数据有保留时间,超过保留窗口的数据会被压缩或删除。所以 DEMO 阶段如果选 30 天前的范围,很可能是一片空白,这不是 Grafana 或 Infinity 的问题,而是 AMS 里已经查不到那么老的数据了。
这个可以在 AMS 的配置项里调,具体参数名在不同版本略有不同,一般是timeline.metrics.service.default.retention或类似的轮转周期配置。DEMO 阶段建议先把时间范围控制在最近 7 天内,避免浪费时间去查一个根本不存在的数据。
6.3 value 是字符串,绘图全乱
前面提到过,AMS 返回的value经常是字符串"1024000000"。Grafana 对字符串类型的时序数据态度很保守,默认可能把它当离散值处理,画出来就是一堆跳变点。
Infinity 的处理方法就是在 UQL 里显式转换:
value: number(.value)如果不用 UQL,在 Columns 映射时看看有没有transform或者Type选项,选择Number。
6.4 数据源测试通,但面板 No data
这是最高频的问题。测试通过只能说明 Grafana 到 AMS 的网络是通的,不能说明查询路径对。遇到 No data,我建议按这个顺序排查:
- 先在浏览器里手动拼一下同样的 URL,确认返回 JSON 有内容;
- 在 Infinity 查询编辑器里,查看 Raw Response 或 Response Preview,看拉回来的原始数据是否为空;
- 如果原始数据有内容,但 Grafana 面板还是 No data,重点看 Rows 路径是否正确;
- 如果 Rows 路径正确,再看 Columns 字段名是否和 JSON 里完全一致,特别是大小写。
Infinity 一般会在查询编辑器下方显示请求响应预览,这功能我几乎每次都用,比一遍遍刷新面板高效得多。
6.5 时间范围对不上,老是少一个点
Grafana 在传$__from和$__to时包含边界。AMS 对边界处理也有自己的逻辑,导致首尾可能出现一个点差。这个不用太在意,如果非要精确,可以在 URL 里给 startTime 加上一个偏移,比如把开始时间整体减掉precision毫秒,让曲线首尾更完整。
少一个点不影响 DEMO 展示,但如果做容量趋势分析,边界点缺失会让人误判起止值,这个注意一下即可。
7. DEMO 完成后的延伸:从演示到可维护监控
到这里,你已经可以用 Grafana + Infinity 把 Ambari-Metrics 的数据画成面板了。这套方案最大的价值是"快":不用在集群里安装任何新的 Agent,不改变 Ambari 既有架构,Grafana 侧一个插件就搞定。如果你只是需要临时演示、给领导看一版监控效果、或者排查某个指标是否真的有问题,这个 DEMO 完全可以胜任。
但我也得说点实在话:它并不适合直接当成生产级长期监控方案。原因有几个。
第一,Infinity 是边查边拉数据,每个面板刷新都会打一次 AMS REST 接口。面板一多、刷新频率一高,AMS 本身会感受到压力。虽然短期不会拖垮集群,但长期没必要这么依赖一个可视化工具去定时轰炸采集服务。
第二,AMS 的保留时间有限,长期趋势分析迟早会遇到数据查不到的问题。正经的监控架构应该在采集端就把数据落到 Prometheus 或 Elasticsearch 这类长期存储里。
第三,告警配置虽然能用,Infinity 的数据源在 Grafana Alerting 里并不是最优选择。像 HDFS 容量突增这种告警,更合理的做法是让 Prometheus 周期性抓取指标,然后在 Alertmanager 里触发规则。
所以我的建议是:DEMO 做出来之后,把它当成"需求确认工具",用来快速和团队对齐到底要看哪些指标、哪些维度、哪些粒度。等需求冻结了,再用 Prometheus + node_exporter 或者 Spark 对外暴露 PushGateway 的方式,把这些指标用更稳的通道接到 Grafana。
如果你是第一次接触 Grafana + Infinity 这套组合,我的个人体会是:不要被界面上的各种字段吓到,先手动 curl 接口,再按"Rows 选行、Columns 选列"的直觉去套,最后用响应预览对照,90% 的问题都能自己解决。真正难的不是插件,而是你对自己数据接口的理解程度。
这个 DEMO 后续还有很多可以扩展的地方:比如把 hostname 变成 Grafana 下拉模板变量、把多指标查询封装成统一模板、通过 Dashboard Provisioning 把看板 JSON 提交到 Git 仓库做版本管理。你完全可以把今天的成果作为底座,慢慢长成一套符合自己团队习惯的监控体系。