1. 为什么把ES8和ZooKeeper放进同一套监控体系
我在整理监控这份工作的时候,最开始是把Elasticsearch和ZooKeeper分开看的。毕竟这两个组件做的事完全不一样:ES8负责搜索引擎、日志分析、指标存储,ZooKeeper负责分布式协调、服务注册、配置管理和分布式锁。一个偏数据面,一个偏控制面,看起来没什么交集。
但实际跑了一段时间后我发现,这两个组件在绝大多数团队的架构里,往往是命运共同体。Kafka依赖ZooKeeper做broker元数据管理和controller选举,Dubbo这类微服务框架用ZooKeeper做注册中心做服务发现;而ES8集群的监控数据要落到Kafka,日志采集链路要经过ES,业务流程要依赖注册中心。也就是说,ZooKeeper一挂,Kafka全挂,Kafka一挂,整个日志采集管道断开,ES这边也就只能查到存量数据,增量全断。这个连锁反应链,决定了这两个组件必须放在同一个监控视角下看,而不是各看各的。
另一个原因是运维成本。很多中小团队根本没有专职的ES DBA或者大数据运维,监控这事往往是一个人扛。如果ES一套监控看板、ZK一套监控看板、告警平台再各接一套,出问题的时候需要在几个系统之间来回切,光是判断"到底是ZK先挂还是ES先挂"就要折腾半天。把它们统一到同一套监控体系里,用同一个时间轴去对比指标曲线,排查根因的效率会高很多。
所以这篇文章的核心思路不是讲"ES8怎么调优"或者"ZooKeeper源码怎么读",而是从监控这个切入点,把这两个组件的关键指标、采集方式、看板搭建、告警规则串起来,形成一套能直接落地的方案。适合正在搭监控平台的同学参考,也适合那些已经有一套监控但总觉得"指标很多、有效信息很少"的团队。
2. 选型思路:Prometheus加Exporter为什么是最省事的组合
2.1 监控组件的选型对比
先说说为什么我没用Zabbix而选了Prometheus。不是Zabbix不好,而是它的模型对这两个组件的适配度不如Prometheus高。Zabbix的核心是模板+触发器,采集方式以agent主动推、server主动拉为主,对动态扩容的集群来说,每次加节点都要在Zabbix里维护主机,非常痛苦。而Prometheus的服务发现机制配合Kubernetes、Consul或者最简单的文件发现,新增节点后指标自动就能抓到,不需要人工介入。
从指标模型来看,Prometheus的多维数据模型(metric + label)也更适合表达ES和ZK的指标。比如ES的分片状态,我可以用elasticsearch_indices_status{cluster="es-prod", index="nginx-log"}这样一个时序来表示,查询的时候按label过滤即可。Zabbix的item key体系做这种多维标签要麻烦得多。
再一个考虑是Exporter生态。Elasticsearch和ZooKeeper在开源社区都有非常成熟的Prometheus Exporter,不需要自己开发采集器,配置一下连接信息就能拉到指标。作为监控体系的搭建设计,能站在已有轮子上工作,没必要自己造轮子。
2.2 采集架构设计
我的实际部署结构是这样的:
- Prometheus主节点一台,负责拉取所有Exporter的指标并存储
- Grafana一台,对接Prometheus数据源,做可视化看板
- 每个ES节点部署一个
elasticsearch_exporter,监听9108端口(默认端口) - 每个ZK节点部署一个
zookeeper_exporter,监听9141端口(默认端口) - 通过Prometheus的file_sd_configs做静态服务发现,维护两个yml文件,分别记录ES和ZK的节点列表
这套结构的好处是,采集链路非常干净:Prometheus定时去目标Exporter拉指标,Exporter负责到ES/ZK做协议转换。即使ES或ZK本身出问题,Exporter进程依然活着,Prometheus的up == 0机制能够立刻感知目标失联,而不是等指标超时。
顺带说一下,最开始的架构我比较贪心,想把ES和ZK的指标都塞到同一个Exporter里采集,后来发现完全不值得。因为这两个组件的指标体量和维度差异很大,分开采集,告警规则可以独立控制,也更方便定位是哪一侧的问题。
3. ES8监控落地:从指标分类到告警阈值设计
3.1 访问ES8的关键前置条件:安全认证
ES8和之前的版本最大的区别之一,就是安全功能默认开启。这意味着如果直接用原来的连接方式,Exporter连上来就会报unauthorized或者证书校验失败。我记得第一次部署的时候,启动elasticsearch_exporter指定--es.uri=http://localhost:9200,Prometheus抓取到的全部是connection refused之类的问题,调试了很久才发现是ES8默认只开放HTTPS而且强制认证。
对应到Exporter的启动参数,需要这样处理:
./elasticsearch_exporter \ --es.uri=https://elastic:your_password@es-node1:9200 \ --es.ssl-skip-verify \ --es.all \ --es.indices \ --es.indices_settings \ --es.shards \ --es.cluster_settings--es.ssl-skip-verify是跳过证书校验,生产环境如果ES用的是自签证书,这个参数很有必要。--es.all表示采集所有节点的统计信息,--es.indices采集索引级别的指标,--es.indices_settings采集索引配置,--es.shards采集分片级别的指标,这些都要加上,否则看板很多图标都是空的。
如果你的ES集群开启了RBAC,建议单独创建一个只读监控账号,不要用超级管理员来跑Exporter。操作上可以这样:
PUT /_security/user/monitor_user { "password": "a_strong_password", "roles": ["monitor"], "full_name": "Prometheus Exporter" }monitor角色在ES8中已经默认包含了对集群监控指标和节点统计信息的只读权限,足够Exporter使用。
3.2 ES8的关键监控指标分类
ES的监控指标数量非常多,如果全采回来,看板上一堆图,反而干扰判断。我从实际使用中梳理出了四类对我来说最关键的指标:
集群层面:
elasticsearch_cluster_health_status:这是最核心的布尔量,值为1表示green,0表示yellow,-1表示redelasticsearch_cluster_health_unassigned_shards:未分配分片数量,这个指标在节点宕机或新节点加入后最容易异常elasticsearch_cluster_health_active_shards与relocating_shards:分片迁移数量短期内激增,说明集群在做重大调整
节点层面:
elasticsearch_jvm_memory_heap_used_percent:JVM堆内存使用率,超过85%就要警惕elasticsearch_jvm_gc_collection_time_seconds_total的速率:GC时间,通常关注ES进程的Young GC和Old GC时间elasticsearch_os_cpu_percent:节点CPU使用率elasticsearch_filesystem_data_available_bytes和elasticsearch_filesystem_data_size_bytes:磁盘可用空间,这两个指标比直接用系统磁盘指标更准确,因为ES会统计到其数据路径
索引层面:
elasticsearch_indices_docs_count:文档总数elasticsearch_indices_store_size_bytes:存储大小elasticsearch_indices_search_query_total的速率:查询QPSelasticsearch_indices_search_query_time_seconds的速率:查询耗时,结合QPS可以算出平均延迟elasticsearch_indices_merges_total的速率:段合并操作频率,这个指标如果在短时间内暴增,说明写入量大幅上升或段策略需要调整
线程池层面:
elasticsearch_thread_pool_search_queue_size:搜索线程池的队列积压elasticsearch_thread_pool_write_queue_size:写入线程池的队列积压elasticsearch_thread_pool_search_rejected_total和elasticsearch_thread_pool_write_rejected_total:拒绝数,一旦出现拒绝,说明线程池已经不堪重负,这是最紧急的告警信号
为了帮助理解,我做了一张简单的监控指标速查表:
| 层面 | 指标 | 告警阈值参考 | 说明 |
|---|---|---|---|
| 集群 | 健康状态 | green | 非green即告警 |
| 集群 | 未分配分片 | >0持续5分钟 | 一般伴随节点异常 |
| 节点 | JVM堆使用率 | >85%持续10分钟 | 堆压力过大 |
| 节点 | 磁盘可用空间 | <20% | 水位触发前的预警 |
| 索引 | 查询延迟P99 | >1000ms持续5分钟 | 需要检查慢查询 |
| 线程池 | write拒绝数 | >0 | 写入严重过载 |
| 节点 | CPU使用率 | >90%持续15分钟 | 可能在做段合并或热点查询 |
3.3 Grafana看板设计思路
Grafana看板我没有用社区模板直接抄,而是根据监控诉求重新排了一版。核心逻辑是"一屏看集群趋势,二屏看节点明细,三屏看索引排行"。
集群总览页放4个大图:集群状态时间线、JVM堆使用率汇总、磁盘使用率汇总、查询/写入吞吐量趋势。这张页面的作用是每天上班扫一眼,确认整体健康。
节点明细页用一个下拉变量的方式选择具体节点,然后展示该节点的CPU、堆内存、GC耗时、磁盘IO、线程池队列深度等指标。这样在定位单节点问题的时候,不用在多个看板间切换。
索引排行页则通过Impala的topk语法,把写入量最大或查询最慢的前10个索引列出来。这一步在排查"某个业务索引拖垮集群"这类问题时非常有用。生产环境中曾出现过一次日志索引的映射爆炸,字段数从几十涨到上千,导致写入性能急剧下降,就是靠这个页面找到的元凶。
4. ZooKeeper监控落地:四字命令与JMX的配合实用价值
4.1 ZooKeeper监控的特殊性
ZooKeeper不像ES那样提供丰富的REST API,它的监控数据主要通过四字命令和JMX接口暴露。四字命令是ZooKeeper最早提供的监控手段,通过TCP连接在clientPort上发送简短的命令(比如ruok、stat、mntr),ZooKeeper返回对应的文本信息。而JMX是Java层面的标准监控接口,可以通过jconsole或 Prometheus JMX Exporter 来采集。
ZooKeeper 3.5.0之后,四字命令默认被白名单机制限制,不开启的话,最常用的mntr命令会直接返回The command 'mntr' failed to execute...。部署时一定要在zoo.cfg里加上:
4lw.commands.whitelist=ruok,stat,mntr,conf,envi,srvr,cons这样做的目的是避免安全风险,但监控需要的最常用几个命令都放行即可,不建议直接配置成*全开。
4.2 Mntr指标的实际解读
在采集方式上,我首选mntr命令,因为它的输出是键值对格式,对Exporter解析最友好。以下是一台生产ZooKeeper节点echo mntr | nc 127.0.0.1 2181的真实输出:
zk_version 3.6.3--1 zk_server_state leader zk_num_alive_connections 8 zk_outstanding_requests 0 zk_znode_count 4321 zk_watch_count 18 zk_ephemerals_count 67 zk_approximate_data_size 179046 zk_open_file_descriptor_count 48 zk_max_file_descriptor_count 1048576 zk_avg_latency 1 zk_min_latency 0 zk_max_latency 18 zk_packets_received 884231 zk_packets_sent 884230 zk_followers 3 zk_synced_followers 3 zk_pending_syncs 0对于ZooKeeper监控,我最关注的是下面几个指标:
zk_server_state:节点角色,leader或follower。这个指标没有数值,但在Prometheus中可以通过记录转换来映射成可查询的label。如果多个节点同时变成leader,那就是脑裂了,这是最严重的故障。zk_num_alive_connections:实时连接数。连接数突然暴跌或暴涨都需要注意,暴跌可能意味着客户端集体断连,暴涨可能是某些客户端出现连接泄漏。zk_outstanding_requests:堆积的未处理请求数。正常情况下这个值应该是0,如果持续大于0,说明ZooKeeper处理能力跟不上,通常是因为磁盘IO性能瓶颈或GC停顿。zk_avg_latency:请求处理平均延迟。正常情况下个位数毫秒,如果这个值开始爬升到几十毫秒甚至上百毫秒,基本可以断定ZooKeeper节点已经不健康。zk_znode_count:节点数量。这个指标更多反映容量规划,如果业务方在使用过程中不断创建临时节点,可能造成Znode数量过大,拖慢ZooKeeper处理。
4.3 Zookeeper Exporter部署配置
使用Prometheus的官方ZooKeeper Exporter时,部署方式有两种:一种是直接对mntr输出做解析,另一种是走JMX接口。我选择的是前者,原因是JMX需要额外开启JMX端口,而且暴露的信息虽然全面,但用起来麻烦一些,光是各类MBean名字就有一大堆。zookeeper_exporter最常见的部署方式是写入Prometheus配置文件,指向每个ZooKeeper节点的2181端口。
- job_name: 'zookeeper' static_configs: - targets: - zk1:2181 - zk2:2181 - zk3:2181启动Exporter的时候,它是通过TCP发送四字命令给ZooKeeper,所以Prometheus只需要访问Exporter的抓取端口即可,Exporter再主动连ZooKeeper的2181端口。如果ZooKeeper和Prometheus之间存在网络ACL,注意放行Exporter到ZooKeeper的2181端口。
另外部署时要关注ZooKeeper Exporter本身的资源占用。我在一个生产环境遇到过Exporter进程内存持续攀升的问题,后来定位到是ZooKeeper的watch数量非常大的场景下,Exporter采集一次需要拉取大量数据,内存开销不小。建议给Exporter的JVM设置一个合理的堆上限:
java -Xmx256m -jar zookeeper_exporter.jar 9141 :2181不需要给Exporter分配过多内存,因为它只是做指标转换,不是缓存数据。
4.4 Zookeeper集群层面的额外监控
单个节点的指标只能反映节点自身的健康状态,但ZooKeeper是集群,必须站在集群视角看。集群层面需要额外监控:
zk_followers和zk_synced_followers:这两个指标只在leader节点上有值,它们表示follower数量以及已同步的follower数量。如果synced_followers小于followers,说明有些节点同步落后,可能很快就会丢投票权。- 节点之间的时钟偏差和网络延迟:ZooKeeper对节点间的网络稳定性要求极高,特别是leader选举期间。如果哪台节点和leader之间的网络延迟超过几十毫秒,很容易导致频繁的leader切换。这个指标可以通过Prometheus的
probe模块对2888端口做TCP探测来监测。
ZooKeeper最容易出问题的场景之一,就是业务方在使用过程中频繁创建临时Znode并设置watch。大量watch在节点故障时的清理过程会给ZooKeeper带来很大的瞬时压力。所以zk_watch_count这个指标也很重要,虽然平时不至于告警,但出现瞬时增长的时候要能查得到历史趋势。
5. 部署过程中的坑与排查链路
5.1 ES8证书校验失败的排查全过程
第一次启动ES8的elasticsearch_exporter时,Prometheus的Target页面显示Get "https://es-node1:9200/": x509: certificate signed by unknown authority。当时第一反应是证书有问题,换成了CA签发的证书后依然报错。
排查链路:
- 先用curl测试ES接口:
curl -k -u elastic:password https://localhost:9200这个命令能正常返回JSON,说明ES本身没问题。
- 再用Exporter的调试模式启动:
./elasticsearch_exporter --es.uri=https://elastic:password@es-node1:9200 --web.listen-address=:9108 --log.level=debug日志里能看到Exporter在建立TLS连接时拿到的证书指纹,和ES节点实际的证书指纹对比,发现完全不一致。
- 最后确认问题出在Exporter默认校验了ES集群的证书链,而ES8自签证书的证书链在Exporter的JVM信任库里不存在。解决办法就是在启动参数里加
--es.ssl-skip-verify=true,让Exporter跳过证书校验。
这个坑其实只花了半小时就定位了,但当时不理解的人可能会在证书配置上绕很久。ES8默认安全策略带来的改变,对监控侧最直接的影响就是所有采集端的认证和TLS配置全部要跟着调整。
5.2 ZooKeeper四字命令被白名单拦截
ZooKeeper 3.5之后的版本,任何四字命令默认都不会被处理,除非在配置文件中显式声明。我第一次部署时没有注意这个变化,启动zookeeper_exporter后,Prometheus抓到的指标全部是空的,看zookeeper_exporter的日志,发现连接和命令发送都成功,但返回的内容不对。
我手动执行了一次:
echo mntr | nc 127.0.0.1 2181返回一大堆The command 'mntr' failed to execute...的错误。当时就知道是白名单问题。修改zoo.cfg,加上4lw.commands.whitelist配置后重启ZooKeeper节点,再用mntr验证,数据就出来了。
修复后我还额外做了一件事:把ruok返回的imok和srvr返回的节点角色都纳入监控。ruok虽然只能确认进程是否存活,但它是最轻量的探活方式;srvr能看到节点的角色和版本信息,对告警时判断主从状态比较有用。
5.3 Prometheus抓取超时导致的数据断点
监控跑起来一段时间后,我发现ES的看板上偶尔会出现某个时间段的指标完全空白,就像"断点"一样。刚开始以为是Exporter进程OOM退出,检查后发现Prometheus的scrape_interval是15秒,而某个ES节点的Exporter在采集大量索引指标时,单次抓取耗时超过了15秒。
Prometheus对每次抓取请求的默认超时时间是10秒(scrape_timeout默认继承自scrape_interval),实际抓取如果超过了限制,这次抓到的数据就会被丢弃,从而在图上形成空白。
解决办法有两种:
- 调整Prometheus的抓取配置:
scrape_configs: - job_name: 'elasticsearch' scrape_interval: 30s scrape_timeout: 25s static_configs: - targets: ['es-node1:9108']- 给Exporter加
--es.indices参数的过滤,比如只采集白名单内的索引,减少单次抓取的数据量。
我最后两个调整都做了:一方面把ES的抓取周期调成30秒,另一方面在Exporter启动参数里加了--es.indices=logstash-*,nginx-*这类过滤规则,只采集业务相关的索引,避免一次拉取全量索引导致Exporter处理不过来。
5.4 告警风暴与降噪机制
监控体系搭建完成后的第一个星期,我最大的困扰是告警太多。ES的堆使用率超过85%的告警,一天能触发几十次;ZK的连接数指标稍微抖动就告警。排查下来发现两个问题:一是阈值设置没有结合业务实际,二是告警规则里没有加持续时间(for)参数。
我的调整策略是这样的:基础健康类的告警,比如集群变成yellow、节点掉线,这些高危指标立即告警,不加for参数;需要观察的指标,比如JVM堆使用率、磁盘空间、GC耗时,加上for: 10m或者for: 15m,降低抖动的误报。
在告警分级上,我也做了明确的区分:
- P0(立即处理):ES集群red、ZK节点全部不可用(过半节点掉线)、线程池拒绝数大于0
- P1(5分钟内处理):ES集群yellow、JVM堆使用率超过90%、ZK连接数超过基线的3倍
- P2(30分钟内处理):磁盘可用空间低于15%、ZK平均延迟超过20ms、旧版本YGC耗时超过1秒
最终的效果是告警量从每天几十条降到了每天三五条,每一条都是真正需要人工介入的。
6. 告警规则与监控项设计的一些调整心得
6.1 从"监控指标"到"监控效果"的转变
部署这套监控体系之后,我最大的体会是:指标采集只是开始,真正有价值的是把这些指标串成"业务可感知的监控效果"。
ES集群健康状态从green变成yellow,如果不看未分配分片的数量,只收到一个yellow的告警,可能还以为是哪块分片暂时没就绪。但如果同时关联了节点掉线数、磁盘使用率、线程池拒绝数,就能很快判断出yellow的本质原因。
ZK的状态也一样,watching一个Znode的业务如果发现服务列表刷新变慢了,从ZK监控图上看连接数和延迟曲线,能很快锁定是ZK节点性能问题还是网络问题。
6.2 容量规划视角的监控补充
监控不仅仅是告警,还要为容量规划提供数据支持。在ES这边,我额外保留了一张"未来一个月磁盘水位预测"的图,用简单的线性回归来预测磁盘满的时间节点。方式很粗暴,用当前磁盘增长速率的平均值算一个趋势线,虽然不算精确,但足以提醒团队"三个月后是否需要扩容节点"。
ZK这边,我会定期观察zk_znode_count和zk_approximate_data_size的增长。这两个指标虽然不会直接影响性能,但当数据量超过某个阈值后,ZooKeeper的FIFO队列和同步机制会开始变得吃力。提前知道数据一直在增长,就能在业务方注册大量临时节点之前提醒他们清理。
6.3 告警通知渠道与值班衔接
最后说一下告警通知的实操细节。我们的告警分为两个渠道:P0/P1的告警通过Webhook推送到企业微信机器人并同步发短信,P2的告警只发企业微信。这样既保证了重大故障期间的强提醒,又不至于让所有人被低级别告警轰炸。
同时,我每次都把告警消息里的日志片段和当前的监控图链接带上。值班人员不用先登录Grafana看半天再判断,点开消息里的图就能第一时间看到趋势。
如果你也在维护ES和ZK的监控体系,我的建议是别急着把几百个指标全部采集下来,先确定你想回答的运维问题,再倒推需要哪些指标。监控系统是给人用的,不是指标越多越好。