1. 为什么这套监控组合值得你花时间搭
Elasticsearch 集群这东西,用起来爽,出事的时候是真让人抓狂。我见过太多团队,ES 集群平时跑得好好的,一到业务高峰期就各种慢查询、节点掉线、磁盘写满,等到用户反馈搜索卡顿才手忙脚乱去查。问题的根源往往不是 ES 本身不行,而是缺少一套能提前预警、能定位瓶颈的监控体系。
elasticsearch_exporter + Prometheus + Grafana这套组合,是目前社区里做 ES 集群监控最主流、也最经得起生产环境考验的方案。它的分工很清晰:elasticsearch_exporter负责从 ES 的各个接口抓取指标并转换成 Prometheus 能识别的格式;Prometheus 负责定时拉取、存储这些时序数据,并基于规则触发告警;Grafana 则把这些数据渲染成直观的仪表盘,让你一眼就能看出集群健不健康。
这套方案能解决什么问题?简单说,它能让你看到集群级别的健康状态(绿黄红)、节点级别的资源消耗(JVM 堆内存、CPU、磁盘、线程池)、索引级别的读写性能(查询延迟、索引速率、段合并情况),以及分片分配是否均衡。适合谁来参考?运维工程师、后端开发、SRE,以及任何需要对自己维护的 ES 集群负责的人。哪怕你之前没怎么接触过 Prometheus,跟着下面的思路走,也能把这套东西搭起来。
我自己的经验是,监控这东西,搭建只是第一步,真正有价值的是指标选得对不对、告警阈值定得合不合理、出问题时会看哪几个图。所以这篇不会只给你一堆配置文件,我会把每个关键指标背后的含义、为什么这么配、踩过哪些坑都讲清楚。
2. 整体架构设计与组件选型思路
2.1 三个组件各自扮演什么角色
先把这三个东西的关系理清楚,不然后面配置起来容易懵。
elasticsearch_exporter是一个用 Go 写的独立进程,它本质上就是个 HTTP 服务。你启动它的时候告诉它 ES 的地址,它就会定期去调用 ES 的_cluster/health、_nodes/stats、_stats等接口,把返回的 JSON 数据拍平成一堆以elasticsearch_开头的指标,暴露在/metrics路径上。Prometheus 来抓这个路径,就完成了数据采集。
Prometheus 是拉模式(pull-based)的时序数据库。它按照你配置的scrape_interval定时去抓 exporter 的/metrics,把数据存下来。同时它内置了告警规则引擎,你可以写 PromQL 表达式,当条件满足时触发 Alertmanager 发通知。
Grafana 不存数据,它只是查询 Prometheus 然后把结果画出来。社区里有现成的 ES 监控 Dashboard 模板,导入就能用,省去从零画图的时间。
2.2 为什么选这套而不是别的
市面上监控 ES 的方案不止一种。比如 ES 自带的 X-Pack Monitoring,功能确实全,但它是商业功能,集群规模一大就要收费。再比如用 Metricbeat 采集,也能配合 ES 和 Kibana 做监控,但如果你已经在用 Prometheus 体系监控其他服务,再单独维护一套 Metricbeat + ES 存储就有点重复建设了。
选elasticsearch_exporter + Prometheus + Grafana的核心理由有三点。第一,统一监控栈。大部分团队已经在用 Prometheus 监控数据库、中间件、应用服务,ES 接进来就是加一个 job 的事,告警规则、通知渠道都能复用。第二,指标可控。exporter 暴露哪些指标、Prometheus 保留多久、Grafana 怎么展示,全在你手里,不依赖商业授权。第三,社区成熟。这个 exporter 在 GitHub 上维护活跃,Grafana 官方和社区都有现成的 ES Dashboard,遇到问题搜一下基本都有答案。
2.3 部署拓扑的两种常见形态
实际部署时,exporter 放哪里是有讲究的。常见两种做法。
一种是集中式部署,exporter 单独跑在一台监控机上,配置里填 ES 集群的协调节点地址。这种最简单,但有个隐患:如果 ES 集群开启了安全认证,exporter 需要能访问所有节点的 stats 接口,网络策略要放开。而且集中式抓取时,如果 ES 节点多,exporter 串行请求可能比较慢。
另一种是边车式(sidecar)部署,在每个 ES 节点上跑一个 exporter,只抓本节点的指标。Prometheus 通过服务发现或者静态配置抓取所有 exporter。这种方式压力分散,也更符合 Prometheus 的哲学。缺点是部署和维护成本高一些,节点扩缩容时要同步调整。
我的建议是:集群规模在 10 个节点以内,用集中式就够了,配置简单;超过 10 个节点或者对采集实时性要求高,考虑边车式。下面实操部分我以集中式为主来讲,边车式的差异我会单独提。
3. 核心指标解读与采集配置要点
3.1 必须关注的几类核心指标
exporter 暴露的指标有上百个,但真正需要天天盯的其实就那么几类。我把它们分成四组,每组说清楚看什么、为什么看。
集群健康类:elasticsearch_cluster_health_status这个指标最直观,0 是绿色、1 是黄色、2 是红色。还有elasticsearch_cluster_health_number_of_nodes、elasticsearch_cluster_health_unassigned_shards。黄色通常意味着有副本分片没分配,红色意味着有主分片没分配,这两种情况都要立刻查。
JVM 与内存类:elasticsearch_jvm_memory_used_bytes和elasticsearch_jvm_memory_max_bytes配合看堆内存使用率。ES 对堆内存非常敏感,官方建议不超过物理内存的 50%,且不要超过 31GB。堆内存持续高于 75% 就要警惕,高于 85% 基本离 Full GC 不远了。还有elasticsearch_jvm_gc_collection_seconds_count看 GC 次数和耗时。
磁盘与 I/O 类:elasticsearch_filesystem_data_used_percent直接反映磁盘使用率。ES 有个水位线机制,默认 85% 时不再分配新分片,95% 时甚至会把索引设为只读。所以这个指标超过 80% 就该处理了。另外elasticsearch_indices_store_size_bytes看索引占用空间。
性能与线程池类:elasticsearch_indices_search_query_time_seconds看查询耗时,elasticsearch_indices_indexing_index_time_seconds看写入耗时。线程池方面,elasticsearch_thread_pool_rejected_count是关键,一旦有 rejected,说明线程池队列满了,请求被拒绝,用户体验直接受损。elasticsearch_thread_pool_queue_count看排队情况。
3.2 exporter 启动参数怎么配
exporter 的启动参数不多,但有几个直接影响采集效果。
./elasticsearch_exporter \ --es.uri=http://es-coordinating-node:9200 \ --es.all \ --es.indices \ --es.indices_settings \ --es.shards \ --es.snapshots \ --web.listen-address=:9114 \ --collector.clustersettings逐个说。--es.uri指定 ES 地址,如果有认证就写成http://user:password@host:9200。--es.all表示采集所有节点的指标,不加的话只采集你指定的那个节点。--es.indices采集索引级别指标,这个数据量大,如果索引特别多(上千个),要考虑是否开启,否则 exporter 响应会变慢。--es.shards采集分片级别指标,对排查分片分配问题很有用。--es.snapshots如果你用快照备份就开。--collector.clustersettings采集集群设置,能看到水位线等配置。
注意:
--es.indices和--es.shards在索引数量多的时候会显著增加 exporter 的响应时间和 ES 的查询压力。我遇到过索引超过 2000 个的集群,开启这两个采集后 exporter 每次抓取要 10 秒以上,Prometheus 默认 10 秒的 scrape_timeout 直接超时。解决办法是调大 timeout,或者用--es.indices_regex只采集关心的索引。
3.3 Prometheus 抓取配置
Prometheus 这边就是加一个 scrape job。
scrape_configs: - job_name: 'elasticsearch' scrape_interval: 30s scrape_timeout: 25s static_configs: - targets: ['exporter-host:9114'] labels: cluster: 'prod-es'scrape_interval设 30 秒比较合适。ES 的指标变化没那么快,15 秒有点浪费存储,60 秒又可能漏掉短时抖动。scrape_timeout要留足余量,因为 exporter 抓取 ES 数据本身要时间,设成 interval 的 80% 左右比较稳。labels里加个 cluster 标签,多集群场景下在 Grafana 里可以按集群筛选。
如果 exporter 是边车式部署,用服务发现更合适:
- job_name: 'elasticsearch' scrape_interval: 30s consul_sd_configs: - server: 'consul:8500' services: ['elasticsearch-exporter']具体用哪种服务发现取决于你的基础设施,Kubernetes 环境用kubernetes_sd_configs,Consul 环境用consul_sd_configs。
4. 从零搭建的完整实操流程
4.1 环境准备与 exporter 部署
假设你有一台监控服务器,上面已经跑着 Prometheus 和 Grafana。现在要把 ES 监控接进来。
第一步,下载 exporter。去 GitHub releases 页面找对应架构的二进制包,或者用 Docker 镜像。生产环境我倾向于用 systemd 管理二进制,比 Docker 少一层网络开销,排查问题也直接。
wget https://github.com/prometheus-community/elasticsearch_exporter/releases/download/v1.7.0/elasticsearch_exporter-1.7.0.linux-amd64.tar.gz tar xzf elasticsearch_exporter-1.7.0.linux-amd64.tar.gz mv elasticsearch_exporter-1.7.0.linux-amd64/elasticsearch_exporter /usr/local/bin/第二步,创建 systemd unit。
[Unit] Description=Elasticsearch Exporter After=network.target [Service] Type=simple User=prometheus ExecStart=/usr/local/bin/elasticsearch_exporter \ --es.uri=http://10.0.0.10:9200 \ --es.all \ --es.indices \ --es.shards \ --web.listen-address=:9114 Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target第三步,启动并验证。
systemctl daemon-reload systemctl enable --now elasticsearch-exporter curl -s http://localhost:9114/metrics | grep elasticsearch_cluster_health_status如果能看到elasticsearch_cluster_health_status{cluster="es",color="green"} 0这样的输出,说明 exporter 工作正常。
4.2 Prometheus 接入与告警规则
exporter 通了之后,在 Prometheus 的prometheus.yml里加上前面说的 scrape job,然后systemctl reload prometheus。去 Prometheus 的 Web UI 的 Status -> Targets 页面,应该能看到 elasticsearch 这个 job 是 UP 状态。
接下来配告警规则。新建一个es_alerts.yml:
groups: - name: elasticsearch rules: - alert: ESClusterRed expr: elasticsearch_cluster_health_status{color="red"} == 1 for: 1m labels: severity: critical annotations: summary: "ES 集群状态为红色" description: "集群 {{ $labels.cluster }} 存在未分配的主分片,请立即排查" - alert: ESClusterYellow expr: elasticsearch_cluster_health_status{color="yellow"} == 1 for: 5m labels: severity: warning annotations: summary: "ES 集群状态为黄色" description: "集群 {{ $labels.cluster }} 存在未分配的副本分片" - alert: ESJVMHeapHigh expr: elasticsearch_jvm_memory_used_bytes{area="heap"} / elasticsearch_jvm_memory_max_bytes{area="heap"} > 0.85 for: 5m labels: severity: warning annotations: summary: "ES 节点堆内存使用率过高" description: "节点 {{ $labels.node }} 堆内存使用率超过 85%" - alert: ESDiskHigh expr: elasticsearch_filesystem_data_used_percent > 80 for: 5m labels: severity: warning annotations: summary: "ES 节点磁盘使用率过高" description: "节点 {{ $labels.node }} 磁盘使用率 {{ $value }}%" - alert: ESThreadPoolRejected expr: rate(elasticsearch_thread_pool_rejected_count[5m]) > 0 for: 2m labels: severity: warning annotations: summary: "ES 线程池出现拒绝" description: "节点 {{ $labels.node }} 的 {{ $labels.type }} 线程池出现请求拒绝"在prometheus.yml里通过rule_files引入这个文件,reload 后告警规则就生效了。
实操心得:
for字段很关键。ES 集群状态偶尔闪一下黄色很正常,比如某个节点重启期间。如果for设成 0,你会被大量瞬时告警淹没。我一般给红色设 1 分钟,黄色设 5 分钟,堆内存和磁盘设 5 分钟,线程池拒绝设 2 分钟。这个节奏既能及时发现问题,又不会太吵。
4.3 Grafana Dashboard 导入与定制
Grafana 这边最省事的做法是导入现成模板。Grafana 官网 Dashboard 库里搜 "Elasticsearch",有几个高星模板,比如 ID 为 2322 的那个,覆盖了集群概览、节点详情、索引性能等面板。
导入步骤:Grafana 左侧菜单 -> Dashboards -> Import -> 输入模板 ID -> 选择 Prometheus 数据源 -> Import。
导入后你会发现有些面板没数据,通常是两个原因。一是模板里的指标名和你的 exporter 版本对不上,新版本 exporter 改过一些指标名。二是变量(比如$node、$index)的查询语句需要根据你的标签调整。这时候就要手动改面板的查询。
我一般会基于模板做几处定制。把集群健康状态做成 Stat 面板,绿色/黄色/红色直接对应颜色,一眼就能看。把 JVM 堆内存使用率做成 Gauge,阈值设在 75% 和 85%。把线程池 rejected 做成 Time series,按节点和线程池类型分组。再单独加一个磁盘使用率的表格,按使用率降序排列,方便快速定位快满的节点。
4.4 关键面板的 PromQL 写法
模板里的 PromQL 不一定符合你的需求,这里给几个我常用的。
集群健康状态:
elasticsearch_cluster_health_status{color="green"} == 1堆内存使用率:
elasticsearch_jvm_memory_used_bytes{area="heap"} / elasticsearch_jvm_memory_max_bytes{area="heap"} * 100查询速率(每秒查询次数):
rate(elasticsearch_indices_search_query_total[5m])查询平均延迟(毫秒):
rate(elasticsearch_indices_search_query_time_seconds[5m]) / rate(elasticsearch_indices_search_query_total[5m]) * 1000索引速率(每秒写入文档数):
rate(elasticsearch_indices_indexing_index_total[5m])线程池队列积压:
elasticsearch_thread_pool_queue_count这些 PromQL 你可以直接贴到 Grafana 面板的查询框里,改一下图例和单位就能用。
5. 常见问题排查与避坑经验
5.1 exporter 抓取超时或指标缺失
这是最常见的问题。表现是 Prometheus 里这个 target 时好时坏,或者 exporter 的/metrics响应特别慢。
原因通常有三个。一是 ES 集群索引太多,--es.indices采集开销大。解决办法是用--es.indices_regex限制采集范围,比如只采集业务索引。二是 exporter 到 ES 的网络延迟高,或者 ES 本身负载高响应慢。可以在 exporter 所在机器上curl -w "%{time_total}"测一下 ES 接口的响应时间。三是 exporter 单进程串行请求所有节点,节点多的时候慢。这种情况考虑边车式部署。
排查时先看 exporter 日志,加--log.level=debug能看到每次请求的耗时。然后看 Prometheus 的 target 页面,scrape duration 是多少。如果接近 scrape_timeout,就要优化了。
5.2 指标名对不上导致面板空白
exporter 版本升级时改过指标名,比如早期版本用elasticsearch_jvm_memory_used_bytes,某些版本可能加了前缀或改了后缀。Grafana 模板是基于特定版本做的,版本不匹配就空白。
解决办法是先去 Prometheus 里查一下实际有哪些指标。在 Prometheus Web UI 的 Graph 页面输入{__name__=~"elasticsearch_.*"},就能看到所有 ES 相关指标名。然后对照着改 Grafana 面板的查询。
5.3 告警风暴怎么治
刚上线监控时最容易出这个问题。集群状态一抖、某个节点 GC 一下,告警就刷屏。
治标的方法是调for时长和阈值。治本的方法是做告警分组和抑制。在 Alertmanager 里配置group_by: ['alertname', 'cluster'],把同类告警合并。再配inhibit_rules,比如集群红色告警触发时,抑制该集群下所有节点级别的告警,因为根因已经找到了。
还有一个技巧是给告警加severity标签,critical 的发短信或电话,warning 的只发群消息。这样既不会漏掉严重问题,又不会被 warning 吵到。
5.4 磁盘水位线告警的坑
ES 默认磁盘水位线是 85% 和 95%。但如果你在 ES 里改过cluster.routing.allocation.disk.watermark配置,告警阈值就要跟着改。我见过一个案例,运维把水位线调到了 90%,但告警还设在 80%,结果天天告警但没人处理,最后真到 95% 时反而没人注意了。
所以告警阈值一定要和 ES 实际配置对齐。可以用--collector.clustersettings采集集群设置,然后在 Grafana 里把水位线配置和实际磁盘使用率画在一起,这样一眼就能看出离水位线还有多远。
5.5 常见问题速查表
| 现象 | 可能原因 | 排查方向 | 解决思路 |
|---|---|---|---|
| target 频繁 down | exporter 进程崩溃或超时 | 看 exporter 日志和 systemd 状态 | 调大 scrape_timeout,检查 ES 响应 |
| 指标缺失 | 采集开关没开或版本不匹配 | 查/metrics实际输出 | 加对应--es.xxx参数 |
| 面板空白 | 指标名或标签对不上 | Prometheus 里查实际指标 | 改 Grafana 查询语句 |
| 告警刷屏 | 阈值太敏感或 for 太短 | 看告警历史 | 调阈值、加 for、配抑制规则 |
| 堆内存告警但 GC 正常 | 堆内存高但没到 GC 压力 | 看 GC 次数和耗时 | 结合 GC 指标判断,避免误报 |
| 磁盘告警但 ES 没反应 | 告警阈值低于 ES 水位线 | 对比 ES 配置 | 对齐告警阈值和 ES 水位线 |
6. 监控体系持续优化的几个方向
搭起来只是开始,真正让监控产生价值的是持续调优。我分享几个自己实践下来觉得有用的方向。
指标分层。把指标分成"必须告警"、"需要关注"、"仅作参考"三层。必须告警的比如集群红色、磁盘超水位线、线程池拒绝;需要关注的比如堆内存、查询延迟;仅作参考的比如段数量、合并次数。Grafana 首页只放第一层和第二层,第三层放到详情页。这样值班的人一眼就能看到关键信息。
容量趋势预测。用 Prometheus 的predict_linear函数做磁盘容量预测。比如predict_linear(elasticsearch_filesystem_data_used_percent[7d], 86400*30)能预测 30 天后的磁盘使用率。如果预测值超过 85%,就提前扩容或清理索引,而不是等告警响了才动手。
慢查询关联分析。把 ES 的慢查询日志和监控指标关联起来。当查询延迟告警触发时,能快速定位是哪个索引、哪类查询导致的。这个需要在应用层埋点或者开启 ES 的慢查询日志,然后把日志和监控面板联动。
定期回顾告警。每个月花半小时看看这个月触发了哪些告警、哪些是误报、哪些阈值需要调整。监控不是配完就不管了,它需要跟着业务变化一起演进。
我个人在实际操作中的体会是,监控体系的价值不在于图有多好看、指标有多全,而在于出问题时你能不能快速定位、平时能不能提前发现隐患。我见过指标采集了几百个但没人看的集群,也见过只盯十几个关键指标但每次都能提前预警的集群。后者才是我们该追求的状态。所以别贪多,先把集群健康、JVM、磁盘、线程池这四类核心指标盯住,告警阈值调准,再慢慢扩展。这套elasticsearch_exporter + Prometheus + Grafana的组合,只要你把核心指标和告警规则打磨好,它能陪你走很远。