
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{clusteres-prod, indexnginx-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.urihttp://localhost:9200Prometheus抓取到的全部是connection refused之类的问题调试了很久才发现是ES8默认只开放HTTPS而且强制认证。对应到Exporter的启动参数需要这样处理./elasticsearch_exporter \ --es.urihttps://elastic:your_passwordes-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表示green0表示yellow-1表示redelasticsearch_cluster_health_unassigned_shards未分配分片数量这个指标在节点宕机或新节点加入后最容易异常elasticsearch_cluster_health_active_shards与relocating_shards分片迁移数量短期内激增说明集群在做重大调整节点层面elasticsearch_jvm_memory_heap_used_percentJVM堆内存使用率超过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%水位触发前的预警索引查询延迟P991000ms持续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、mntrZooKeeper返回对应的文本信息。而JMX是Java层面的标准监控接口可以通过jconsole或 Prometheus JMX Exporter 来采集。ZooKeeper 3.5.0之后四字命令默认被白名单机制限制不开启的话最常用的mntr命令会直接返回The command mntr failed to execute...。部署时一定要在zoo.cfg里加上4lw.commands.whitelistruok,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.urihttps://elastic:passwordes-node1:9200 --web.listen-address:9108 --log.leveldebug日志里能看到Exporter在建立TLS连接时拿到的证书指纹和ES节点实际的证书指纹对比发现完全不一致。最后确认问题出在Exporter默认校验了ES集群的证书链而ES8自签证书的证书链在Exporter的JVM信任库里不存在。解决办法就是在启动参数里加--es.ssl-skip-verifytrue让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.indiceslogstash-*,nginx-*这类过滤规则只采集业务相关的索引避免一次拉取全量索引导致Exporter处理不过来。5.4 告警风暴与降噪机制监控体系搭建完成后的第一个星期我最大的困扰是告警太多。ES的堆使用率超过85%的告警一天能触发几十次ZK的连接数指标稍微抖动就告警。排查下来发现两个问题一是阈值设置没有结合业务实际二是告警规则里没有加持续时间for参数。我的调整策略是这样的基础健康类的告警比如集群变成yellow、节点掉线这些高危指标立即告警不加for参数需要观察的指标比如JVM堆使用率、磁盘空间、GC耗时加上for: 10m或者for: 15m降低抖动的误报。在告警分级上我也做了明确的区分P0立即处理ES集群red、ZK节点全部不可用过半节点掉线、线程池拒绝数大于0P15分钟内处理ES集群yellow、JVM堆使用率超过90%、ZK连接数超过基线的3倍P230分钟内处理磁盘可用空间低于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的监控体系我的建议是别急着把几百个指标全部采集下来先确定你想回答的运维问题再倒推需要哪些指标。监控系统是给人用的不是指标越多越好。