
1. 从零搭建 Elasticsearch 监控体系为什么裸奔的集群迟早要出事刚接手一个搜索集群的时候我最怕看到的不是报错日志而是一切正常这四个字。因为 Elasticsearch 这东西它不像传统数据库那样CPU 打满了会立刻告警、连接数爆了会直接拒绝服务。它更像一个慢性病患者——今天写入慢一点明天查询抖一下你觉得还能用等到某天早上打开 Kibana 发现索引变红、分片分配失败、节点掉出集群那基本就是 ICU 抢救的节奏了。elasticsearch-monitor这个项目说白了就是给 ES 集群装上一套体检监护系统。它要解决的核心问题有三个第一让你在集群还没挂之前就知道它快撑不住了第二出问题的时候能快速定位是哪个节点、哪个索引、哪类操作在捣鬼第三把历史数据留下来方便做容量规划和性能趋势分析。适合谁来参考如果你手头维护着至少一个 ES 集群不管是三节点的测试环境还是几十个节点的大集群只要你不希望半夜被电话叫醒这套东西就值得花时间搭起来。我见过太多团队的做法是ES 部署完Kibana 能打开就觉得监控已经有了。实际上 Kibana 自带的 Monitoring 面板只能算体温计它能告诉你发烧了但不会告诉你为什么发烧、该吃什么药。真正要做的监控体系得覆盖指标采集、日志聚合、告警规则、可视化看板、以及事后排查的完整链路。下面我就按自己实际搭建和踩坑的顺序把整套东西拆开讲清楚。2. 监控指标到底该盯哪些别被几百个 metrics 晃花了眼2.1 集群级指标先看大局有没有崩ES 通过_cluster/health和_cluster/stats暴露的指标有上百个但真正需要设置告警的我认为不超过十五个。集群健康状态status是最粗粒度的信号green/yellow/red 三档但这里有个坑yellow 在很多单副本场景下是常态如果你把 yellow 直接设成告警运维群会被轰炸到麻木。我的做法是yellow 持续超过 10 分钟才触发低优先级通知red 则立即电话告警。分片相关指标里unassigned_shards是最关键的。它一旦大于 0 且持续增长说明有分片分配不出去常见原因是磁盘水位线触发、节点掉线、或者分片分配规则配置冲突。另一个容易被忽略的是initializing_shards和relocating_shards短时间有值是正常的但如果某个分片长时间处于 initializing 状态大概率是恢复过程卡住了需要人工介入。节点数量number_of_nodes和数据节点数量number_of_data_nodes要对比着看。如果两者差值突然变大说明有数据节点掉线但主节点还在这时候集群可能还能响应但数据完整性已经受威胁了。2.2 节点级指标找出拖后腿的那一台节点级指标我重点关注四类JVM、磁盘、线程池、以及网络。JVM 这块heap_used_percent超过 75% 就要警惕超过 85% 基本离 OOM 不远了。但光看堆使用率不够还要看 GC 情况。老年代 GC 频率和耗时是更灵敏的指标——如果 full GC 每分钟超过一次或者单次耗时超过 1 秒说明堆内存已经严重不足这时候调大堆或者优化查询才是正事重启只能续命几个小时。磁盘方面ES 默认的低水位线是 85%高水位线是 90%。一旦某个节点磁盘使用率超过 85%ES 就不会再往这个节点分配新分片了超过 90%它会尝试把现有分片迁走。所以磁盘告警阈值我一般设在 80%留出缓冲时间。这里有个经验磁盘使用率的增长往往不是线性的日志类索引在业务高峰期可能一天涨 10%所以容量规划要按峰值算不能按平均值。线程池指标里write和search两个队列的 rejected 数量是核心。rejected 大于 0 说明线程池满了请求被拒绝用户侧会直接感知到失败。search队列的 rejected 通常意味着查询太慢或者并发太高write队列的 rejected 则可能是批量写入过大或者磁盘 IO 跟不上。2.3 索引级指标定位到具体的问题索引集群和节点都正常但业务方反馈搜索变慢了这时候就得下钻到索引级别。我重点看三个指标索引的search.query_total和search.query_time_in_millis的比值平均查询耗时、indexing.index_total和indexing.index_time_in_millis的比值平均写入耗时、以及segments.count和segments.memory_in_bytes。平均查询耗时突然飙升常见原因有某个大查询打满了缓存、分片数过多导致协调节点压力大、或者有慢查询在拖后腿。平均写入耗时变长可能是 refresh 频率太高、translog 刷盘慢、或者 merge 压力大。segments 数量过多单分片超过 50 个会导致查询变慢因为每个 segment 都要单独搜索再合并结果。下面这张表是我在实际运维中总结的指标-阈值-动作对照表可以直接拿去配告警指标警告阈值严重阈值建议动作cluster.statusyellow 持续 10minred检查分片分配、节点状态unassigned_shards 0 持续 5min 10检查磁盘水位、分配规则heap_used_percent 75% 85%排查大查询、考虑扩容disk_used_percent 80% 85%清理旧索引、扩容磁盘thread_pool.search.rejected 0 100/min优化查询、增加节点thread_pool.write.rejected 0 100/min降低批量大小、检查磁盘index.search.avg_time 100ms 500ms分析慢查询、优化映射3. 采集方案选型Exporter、API 轮询还是 Agent 直采3.1 三种采集方式的本质区别给 ES 做监控采集方式无非三种用现成的 exporter 拉指标、自己写脚本轮询 REST API、或者在每个节点上部署 agent 直接读本地指标。Exporter 方案最省事社区有成熟的 elasticsearch_exporter它通过调用 ES 的_nodes/stats、_cluster/health、_cluster/stats等接口把指标转成 Prometheus 格式暴露出来。优点是开箱即用指标覆盖全和 Prometheus 生态无缝集成。缺点是它只能看到 ES 主动暴露的指标像操作系统层面的 CPU、内存、磁盘 IO 这些拿不到而且如果 ES 集群本身响应慢exporter 的采集也会超时。自己写脚本轮询的好处是灵活你想采什么就采什么想怎么聚合就怎么聚合。比如你可以写个 Python 脚本每 30 秒调一次_cat/indices把每个索引的文档数、存储大小、健康状态存到自己的监控库里。但缺点是维护成本高ES 版本升级接口变了你得跟着改而且轮询频率高了会给集群增加额外负担。Agent 直采适合对指标精度要求高的场景。在每个 ES 节点上跑一个轻量 agent直接读/proc下的系统指标同时通过本地回环调用 ES 的 API。这样既能拿到系统层指标又能避免网络开销。但部署和升级麻烦节点多了之后 agent 的生命周期管理是个问题。3.2 我为什么最终选了 Exporter 自定义脚本的组合纯 exporter 方案有个硬伤它拿不到操作系统的磁盘 IO 和网络指标而这两个恰恰是排查 ES 性能问题的重要线索。比如你发现写入变慢了如果只看 ES 的 indexing 指标你只能知道慢了但不知道是磁盘 IO 饱和了还是网络带宽打满了。所以我的做法是用 exporter 采集 ES 自身的指标用 node_exporter 采集系统指标再写一个轻量脚本采集一些 exporter 不提供的业务指标比如特定索引的文档增长速率。这里有个细节要注意exporter 的采集频率不要设得太高。我见过有人设成 5 秒一次结果 exporter 自己成了集群的负载来源。ES 的_nodes/stats接口在节点多的时候响应很慢频繁调用会占用大量协调节点资源。我的经验值是 30 秒一次对于大多数场景足够了。如果确实需要更高精度可以针对关键指标单独提高频率而不是全量拉取。3.3 采集端的资源隔离不管用哪种采集方式都要记住一个原则监控系统本身不能成为被监控系统的负担。我一般会把 exporter 部署在独立的监控节点上不要和 ES 数据节点混部。如果资源实在紧张至少要给 exporter 设置 CPU 和内存限制防止它失控。另外采集超时时间要设置合理。ES 集群在压力大的时候API 响应可能从几十毫秒变成几秒。如果 exporter 的超时设得太短会频繁采集失败导致监控数据出现空洞设得太长又会拖慢整个采集周期。我的建议是超时时间设为采集间隔的三分之一左右比如 30 秒采集一次超时设 10 秒。4. 告警规则设计怎么做到既不漏报又不骚扰4.1 告警分级的核心逻辑告警设计最怕两种极端一种是狼来了天天报警但都是误报最后大家都不看了另一种是沉默的羔羊出大事了却一声不吭。要避免这两种情况核心是做好分级。我把告警分成三级P0 是必须立即处理的比如集群 red、所有数据节点掉线、磁盘使用率超过 95%P1 是需要在工作时间内处理的比如 yellow 持续超过 30 分钟、堆使用率超过 85%、查询拒绝率超过 1%P2 是记录备查的比如单次查询超时、segment 数量偏多。P0 走电话短信P1 走即时通讯工具P2 只写日志和看板。分级的关键在于持续时间这个维度。瞬时尖峰大多数时候是正常的比如业务高峰期查询量突增导致队列短暂排队这不需要告警。但如果某个指标持续超过阈值一段时间那就说明系统真的有问题了。我一般给 P0 设置 1 分钟的持续时间P1 设置 5 分钟P2 设置 15 分钟。4.2 那些年我踩过的告警坑第一个坑是磁盘使用率告警风暴。有段时间我们集群每天凌晨都会收到一堆磁盘告警查了半天发现是日志索引的定时滚动任务在凌晨执行滚动瞬间会创建新索引导致磁盘使用率短暂上升。后来我把磁盘告警改成了预测 4 小时后会超过阈值才触发而不是看当前值。这个用 Prometheus 的predict_linear函数就能实现效果立竿见影。第二个坑是JVM 堆告警误报。ES 的堆使用率是波动的GC 之后会降下来GC 之前会升上去。如果只看瞬时值你会收到大量无意义的告警。正确的做法是看GC 后堆使用率或者看一段时间内的平均值。Prometheus 里可以用avg_over_time来做平滑。第三个坑是分片告警漏报。我们曾经遇到过某个索引的主分片分配失败但集群状态还是 yellow因为副本分片是正常的。如果只监控集群 status这个情况就漏掉了。后来我加了一条规则监控unassigned_shards的变化率如果 5 分钟内从 0 变成大于 0就触发告警不管集群状态是什么颜色。4.3 告警收敛与静默策略节点批量重启或者滚动升级的时候告警会集中爆发。这时候需要做告警收敛把同一时间窗口内的同类告警合并成一条。Prometheus 的 Alertmanager 支持分组group_by和抑制inhibit规则可以把集群 red和节点掉线这两类告警关联起来只发一条根因告警。静默策略也很重要。计划内的维护操作提前设置静默窗口避免误报。但静默时间不要太长我一般设 2 小时到期自动恢复。如果维护没做完手动续期这样能防止静默了但忘了取消导致真正的问题被掩盖。5. 可视化看板让数据自己说话5.1 看板分层设计我习惯把 ES 监控看板分成三层全局概览、节点详情、索引详情。全局概览放在最上面一屏之内要能看到集群健康状态、节点数量、分片总数、总文档数、总存储大小、当前写入和查询 QPS、以及最近 24 小时的告警数量。这一层是给值班人员看的目的是一眼判断有没有问题。节点详情层展示每个节点的 CPU、内存、磁盘、JVM 堆、GC 次数、线程池队列长度。这一层是给运维人员排查用的目的是定位到具体哪台机器有问题。索引详情层展示每个索引的文档数、存储大小、查询耗时、写入耗时、segment 数量。这一层是给开发人员优化用的目的是找到哪个索引拖慢了整体性能。5.2 那些容易被忽略但很有用的图表除了常规的折线图我强烈建议加几个非典型图表。第一个是分片分布热力图。用表格形式展示每个节点上的分片数量颜色越深表示分片越多。这个图能快速发现分片分布不均的问题。我曾经遇到过一个节点上分了 200 多个分片而其他节点只有几十个导致那个节点查询特别慢。热力图一眼就能看出来。第二个是查询耗时 P99 曲线。平均值会骗人P99 才能反映真实用户体验。如果 P99 突然飙升但平均值没变说明有少量请求遇到了严重延迟这往往是慢查询或者 GC 导致的。第三个是索引增长趋势图。把每天的索引存储大小画成曲线再叠加一条线性预测线能直观看到什么时候会触发磁盘水位线。这个图对于容量规划特别有用比看当前使用率有价值得多。5.3 看板的性能优化看板本身也会消耗资源。如果看板查询的数据量太大每次刷新都会给 ES 造成压力。我的做法是对于时间范围超过 7 天的图表使用预聚合数据比如 Prometheus 的 recording rules而不是直接查原始指标。另外看板的自动刷新间隔不要设得太短30 秒足够了设成 5 秒除了让屏幕闪得更快之外没有任何实际意义。6. 从告警到定位一次真实的排查过程复盘6.1 问题现象某天下午告警群收到一条 P1 告警集群查询拒绝率超过 1%持续 5 分钟。同时业务方反馈搜索接口偶尔超时。登录看板一看thread_pool.search.rejected在几分钟内从 0 涨到了 300 多search队列长度也满了。6.2 排查链路第一步看集群和节点状态。集群是 green所有节点都在线磁盘和 JVM 都正常。这说明不是资源耗尽导致的。第二步看索引级别的查询指标。发现有一个索引的平均查询耗时从平时的 20ms 涨到了 800msP99 更是超过了 5 秒。问题定位到这个索引上。第三步看这个索引的 segment 数量。发现单分片的 segment 数量达到了 120 多个远超正常水平。segment 太多会导致查询时需要遍历大量小文件性能急剧下降。第四步查为什么 segment 没有合并。看 merge 线程池的指标发现thread_pool.force_merge队列里有大量任务积压。再查操作记录发现前一天有人手动触发了一次全量 force merge但没等它完成就下班了导致 merge 任务一直排队新的写入又不断产生新 segment形成了恶性循环。6.3 解决方案与后续预防当时的紧急处理是先取消积压的 force merge 任务让 ES 自己按默认策略做后台 merge。然后对那个索引做了一次滚动重启rollover把写入切到新索引旧索引不再写入后 segment 数量自然稳定下来。最后对旧索引做了一次 force merge 到 1 个 segment查询耗时恢复到 15ms。后续预防措施有三条第一force merge 操作必须走审批流程且只能在业务低峰期执行第二给 force merge 线程池的队列长度设置告警超过 10 就通知第三把 segment 数量纳入日常巡检指标单分片超过 50 个就预警。这个案例告诉我们监控不只是看有没有挂更要看有没有在变坏。很多故障不是突然发生的而是有一个渐进的过程只是我们没有在早期发现而已。7. 监控系统自身的维护别让保安先倒下监控系统跑起来之后它自己也需要被监控。我见过不止一次因为 exporter 挂了或者 Prometheus 磁盘满了导致整个监控体系失效而运维人员浑然不知。最基本的要给监控组件本身加上存活检测。exporter 的/metrics接口如果连续 3 次采集失败就触发告警。Prometheus 的磁盘使用率也要监控因为它的数据是只增不减的默认保留 15 天如果磁盘规划不足很容易写满。另外监控数据的保留策略要合理。ES 的指标数据量很大如果全量保留一年存储成本会很高。我的做法是原始指标保留 15 天5 分钟粒度的降采样数据保留 90 天1 小时粒度的降采样数据保留 1 年。这样既能满足短期排查需求又能支持长期的趋势分析。还有一个容易被忽略的点监控配置的版本管理。告警规则、看板配置、采集配置这些都应该纳入版本控制。每次修改都要有记录出问题的时候可以快速回滚。我见过有人直接在 Grafana 界面上改看板改错了想恢复都找不到之前的版本只能凭记忆重建。最后分享一个我自己的小习惯每个月做一次监控盲区检查。具体做法是随机挑一个历史故障案例假设它现在重新发生看看现有的监控体系能不能在 5 分钟内发现并定位。如果发现某个环节缺失就补上对应的指标或告警。这个习惯帮我堵住了不少漏洞推荐你也试试。