尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

从业务日志到智能告警:用ELK与Kafka搭建平台监控雷达

从业务日志到智能告警:用ELK与Kafka搭建平台监控雷达 1. 为什么要搭一个PLFM_RADAR从一次半夜故障说起先讲一件真实的事。前阵子我们平台凌晨三点悄无声息地“半瘫”了——不是完全挂掉而是订单接口时不时超时用户在下单页转圈圈。最早发现问题的不是值班同事而是一个在朋友圈吐槽“你们App怎么这么卡”的忠实用户。我们上去看监控大盘CPU、内存、带宽全都在正常区间日志也没刷error整个系统就像装睡一样表面上一切正常实际上已经病得不轻。那次故障之后我花了很长时间复盘发现最扎心的不是“监控没装”而是“监控装了不少但全是瞎子”。传统的监控体系盯的是服务器指标——CPU、内存、磁盘、负载它们只能告诉你“机器累不累”却无法告诉你“业务到底好不好”。当时订单接口慢是因为数据库连接池被某个批量任务占满了机器层面根本没到瓶颈指标层自然毫无反应。这就是我后来动手搭PLFM_RADAR的直接动机。这个项目的核心思路很简单把监控的视角从“机器”翻转到“业务”。不要只问“服务器健康吗”要问“用户在平台上的每一次关键动作是否被及时、正确地响应”。PLFM_RADAR本质上是一套面向业务平台的立体化监测雷达它持续扫描平台产生的日志、指标、状态码、响应时间、异常事件把零散的数据汇聚成一张“会报警的地图”一旦某个业务环节出现劣化趋势就按严重程度分级通知到对应的人。如果你所在的团队也面临这些情况——监控大盘齐全但没人看、告警邮件多到被当垃圾邮件忽略、每逢上线提心吊胆只能靠测试手动点一遍、出了故障要靠用户投诉才知道——那这套东西大概率能帮到你。它可以做得很大也可以先从一个最小可用版本起步我今天分享的就是我自己的落地路径从架构选型到字段设计再到告警阈值和踩坑实录尽量把关键决策背后的逻辑也讲清楚。2. 整体架构设计为什么是ELK加Kafka这套组合PLFM_RADAR第一步不是写代码而是定架构。我当时列了一堆候选Prometheus加Grafana、Zabbix、SkyWalking、自研采集器加时序库。比来比去最终敲定了Filebeat Kafka Elasticsearch Kibana ElastAlert这套组合。先说结论再讲为什么。2.1 从业务日志切入而不是从指标切入传统监控之所以失灵核心原因是数据源的“抽象层级”太低。CPU使用率、内存剩余量是系统资源维度的指标它们和“用户下单失败”之间隔着好几层逻辑——请求从网关进来经过鉴权、库存、支付、风控多个服务每个环节都可能是瓶颈但机器指标一般不会因为这些环节的业务逻辑变化而产生明显波动。而业务日志不一样它是服务处理请求时留下的现场记录里面有状态码、处理耗时、业务错误码、调用链信息几乎可以说凡是用户能感知到的问题一定会在日志里留下痕迹。所以我把PLFM_RADAR的数据源定为“全量业务日志优先系统指标辅助”。业务日志负责回答“服务做了什么、做得怎么样”系统指标负责回答“机器还撑不撑得住”两者结合才能还原一次故障的全貌。2.2 各组件为什么是这样选的先说采集端。选Filebeat而不是Logstash做采集理由非常实在Filebeat是用Go写的内存占用极小资源开销可以忽略不计而且它天然能跟踪日志文件的位置你重启它不会重复采集或者漏采。Logstash更适合做复杂的清洗和转换但它跑起来动辄吃几百MB甚至上G内存放在每一台业务机器上非常不划算。我的分工是Filebeat负责轻量采集和基础清洗重活留给后端的Logstash去干。中间加Kafka是大数据量场景下几乎必然的选择。日志系统的写入流量是典型的“突发型洪峰”日常可能每秒几百条一到活动大促、定时任务时间点就是每秒几万条如果Filebeat直接往Elasticsearch里灌数据ES一扛不住就会触发写入拒绝数据就会丢。Kafka的作用相当于一个蓄水池采集端只负责把数据扔进池子消费端按自己的节奏慢慢处理峰值流量被削平了系统稳定性会好非常多。存储和检索选Elasticsearch没有太多争议。ES的倒排索引天然适合全文搜索和日志检索聚合分析能力也强。很多人纠结“日志数据为什么不用ClickHouse”我的看法是PLFM_RADAR的核心场景是“钻取和检索”——出问题时你要根据trace_id把一条请求的前因后果捞出来ES的检索能力和Kibana的交互式探索比CK方便得多而CK的强项是海量数据的聚合统计两者定位不同现阶段ES完全够用。告警组件我用的ElastAlert一个老牌的开源告警规则引擎。它本身不采集数据而是周期性地拿ES里的数据跑规则匹配到异常就触发通知。选它没有太多高深的理由就是插件化程度高支持频率规则、变化率规则、异常值检测规则等几类常见模式基本覆盖了我对业务告警的需求。2.3 数据流转全景图我把整个链路理成一条流水线直观感受一下业务服务的日志文件 - Filebeat采集与简单处理 - Kafka Topic - Logstash消费并清洗转换 - Elasticsearch索引 - Kibana可视化检索 - ElastAlert周期巡检触发告警 - 企业微信/钉钉/邮件通知对应负责人这套链路每段各司其职我之后每一层都写了详细的配置但先记住这条主线后面所有细节都是在主线上各个节点做深做细。3. 核心细节解析从采集到存储的配置要点架构定下来之后具体的落地才是真正的战场。这一节我把链路中每个节点的关键配置和决策讲透照着操作可以让系统稳稳地跑起来。3.1 采集端Filebeat配置与性能平衡Filebeat配置里有两个参数决定了“采多快”和“吃多少资源”一个是bulk_max_size一个是flush_interval。前者是攒多少条消息批量发给Kafka后者是攒多久必须发一次。我把这两个参数调到“既不浪费带宽又不让堆积延迟过高”的平衡点output.kafka: hosts: [kafka1:9092, kafka2:9092] topic: plfm-biz-log partition.hash: reachable_only: true bulk_max_size: 2048 max_message_bytes: 2000000 queue.mem: events: 8192 flush.min_events: 1024 flush.timeout: 3s注意这里有个容易忽略的点partition.hash加上reachable_only的意思是按照某个key做哈希分区例如按服务名分流这样同一个服务的日志会进入同一个分区消费端按分区处理时能做到日志的顺序性。顺序性在排查问题时非常关键——同一个用户、同一个请求的日志如果乱序看链路时会看到因果颠倒的诡异现象。另外一个高价值配置是multiline。Java服务的异常堆栈会跨多行如果你不做多行合并ES里就会出现半截堆栈的“碎片日志”检索时非常痛苦。我的处理方式是匹配行首模式multiline: pattern: ^[0-9]{4}-[0-9]{2}-[0-9]{2} negate: true match: after意思是不是以“2024-xx-xx”开头的内容都算作前一行的续行这样一条完整异常堆栈就会作为一条日志进入ES检索时看到的是一条完整的错误记录而不是断成十几截的乱码。实操中发现Filebeat还有一个隐藏坑它在启动时会读取日志文件的当前位置如果你用docker restart重启Filebeat它会重新检查文件尾部如果你用docker cp替换了日志文件比如日志轮转策略异常它可能从新文件的开头重新读取导致数据重复。我的应对方案是开启clean_inactive和clean_removed让filebeat自动清理超过48小时不活跃的文件状态减少异常场景下重复采集的概率clean_inactive: 48h clean_removed: true3.2 存储层索引生命周期管理别偷懒日志系统的数据量增长是实实在在的“指数袭城”如果放任索引一直写下去ES的响应会越来越慢直到集群完全卡死。ILM索引生命周期管理是ES自带的机制把索引的生命周期分成几个阶段热阶段正在写入、温阶段不再写入但需快速查询、冷阶段低频查询、删除阶段过期清理。我按“性能优先、成本可控”的思路配置了一套{ policy: { phases: { hot: { actions: { rollover: { max_size: 50gb, max_age: 1d }, set_priority: {priority: 100} } }, warm: { min_age: 3d, actions: { forcemerge: {max_num_segments: 1}, shrink: {number_of_shards: 1} } }, delete: { min_age: 15d, actions: {delete: {}} } } } }这套策略的核心逻辑是这样的索引写满50GB或者超过1天就滚动出新的索引防止单个索引无限膨胀3天之后进入温阶段强制合并segment减少存储占用同时把分片收缩到1个15天后直接删除因为业务日志超过15天基本没有排查价值需要长期保留的审计日志单独走另一个索引。有人会问“ES不是自动就有这个机制吗”确实ILM是内置功能但它默认不开启你要在index template里指定policy才能生效。我犯过的错误是一开始模板里没加lifecycle.nameILM完全不工作数据无限制膨胀后来才发现是因为模板字段漏配了。模板要同时指定index_patterns匹配哪些索引名、settings里挂载ILM策略和分片数这样才能让新建的索引自动套用策略。设置分片数和副本数同样有讲究。日志场景下我觉得5个主分片起步比较合理数据量再上行再加副本数设为1保证一个节点挂了数据不丢。千万别把每个索引的分片数设置成30、50分片过多会带来集群元数据膨胀查询时也要广播到所有分片性能反而更差。3.3 清洗与字段标准化Logstash里的细节活Logstash消费Kafka时我额外做了一层“字段标准化”这一层的重要性远超我的预期。如果没有统一字段规范每个服务打印的日志格式各不相同后续查询和告警根本没法玩。举例来说有的服务打的是response_time: 200.5有的打的是cost: 200.5ms在ES里索引映射出来一个字段是浮点数一个是字符串告警规则就没法统一匹配。我的做法是在Logstash配置中强制清理filter { mutate { rename { cost response_time_ms } } grok { match { message %{TIMESTAMP_ISO8601:log_timestamp} \[%{LOGLEVEL:level}\] %{NOTSPACE:service} - %{GREEDYDATA:message_body} } } date { match [log_timestamp, ISO8601] target timestamp timezone Asia/Shanghai } mutate { convert { response_time_ms float } remove_field [message] } }这段配置做了几件事把各家自定义的耗时字段统一改名为response_time_ms用grok从原始日志里抽出时间、日志级别、服务名再用date插件把字符串时间解析成ES的标准时间戳并指定时区最后把用不到的原始message字段删掉以节省存储。经过这一层之后所有进入ES的日志结构高度一致查询和聚合就变得非常顺手。说到时区这个细节国内服务器日志绝大多数是北京时间但ES默认按UTC存时间。如果不明确指定timezone Asia/Shanghai所有日志在Kibana里看起来都会差8小时。晚上排查问题时会莫名发现“这个时间段没有任何日志”特别误导人。这属于一个配置就能解决的问题但踩过坑的人才知道它有多重要。4. 告警引擎把“雷达”真正转起来采集、清洗、存储搞定了PLFM_RADAR其实才搭好了一半。如果没有告警它就只是一个“能看但没人看”的日志搜索引擎。让系统主动“出声”是关键这里我把告警引擎单独拎出来讲因为它决定了整套系统能不能真正把人叫醒。4.1 告警分级与规则设计我一开始的告警规则设计得很粗暴——只要日志匹配到“error”就发告警。结果系统上线第二天相关人员收到几百条告警通知第三天所有人不约而同把告警群屏蔽了。这就是典型的“狼来了”效应告警太多等于没有告警。后来我按影响面把告警分成三个等级规则设计思路也完全不同告警等级触发示例通知渠道响应时限P0 严重核心接口可用率低于95%、订单错误率突增3倍电话短信群机器人5分钟内响应P1 主要某服务P95响应时间超过800ms、错误日志每分钟超阈值短信群机器人15分钟内响应P2 次要单条慢SQL出现、非核心接口偶发5xx群机器人仅记录当天处理这层分级送给告警规则后告警量立刻降下来了。为什么因为告警规则设计上再补了一层“窗口聚合”逻辑——不是“出现一次就告警”而是“在5分钟窗口内累积达到一定次数或比例才告警”。单次error可能是偶发网络抖动不值得半夜唤醒一个人批量error才是真正需要关注的问题。4.2 阈值设定平滑策略与动态基线阈值是告警系统里最敏感的参数。定得太低天天误报定得太高又变成事后诸葛亮。我自己的经验是不要拍脑袋定“响应时间长于500ms就告警”而要跟着业务数据走。有个很实用的方法叫“动态基线”原理是统计过去7天同一个服务同一个接口的响应时间分布算P95值作为自然基线然后当实时P95达到基线的1.5倍持续5分钟才触发告警。这样工作日高峰和凌晨低峰的差异会被自动吸收不会出现“凌晨2点P95超过500ms就告警”这种必然误报的情况。下面是ElastAlert里一个典型的变化率规则配置name: Response-Time-Spike type: spikiness index: plfm-biz-log-* threshold_control: spike_rate: 1.5 min_threshold: 200 field: response_time_ms timeframe: minutes: 5 alert: - elastalert_modules.notify.WeComAlertspikiness类型的意思是“跟历史比最近5分钟的值比中位数高出多少倍”如果仅超过200ms基线就告警过滤掉了绝对值本来就小、波动无意义的情况。这类规则比固定阈值健壮得多极大降低了误报率。4.3 告警去重与恢复通知告警去重是很多人刚开始做时完全没想到的事情。如果没有去重机制一次持续半个小时的故障会每隔1分钟触发一次告警产生三十次相同的通知群消息瞬间刷屏。我的方案是在ElastAlert里配置realert——同一规则在10分钟内重复触发时不重复发通知而是合并为一条“持续告警中”的消息同时打开aggregation按小时把多条规则命中的结果汇总成一条综合告警。还有一个经常被忽视的点恢复通知。很多人只做了告警触发没做告警恢复故障修复后不会自动报告状态。如果一个人被电话叫醒处理问题处理完了却没有任何反馈他根本不知道“现在是不是已经没事了”只能反复自己看大盘。ElastAlert支持在一个规则里同时定义query_key和alert_on_missing当监控的目标在指定时间不再匹配条件时自动触发一条“恢复”通知。我强烈建议把这条加进去这是让值班人员真正安心的关键。5. 可视化面板像雷达界面一样一目了然告警系统负责“发现问题后叫人”可视化面板负责“被人叫醒后快速看清问题”。PLFM_RADAR的看板设计目标很朴素让一个没看过这套系统的人也能在30秒内说清楚“平台现在到底有没有问题、问题出在哪里”。5.1 看板的四类核心视图我在Kibana里搭了四个核心Dashboard每个对应一个独立视角。第一个是全局健康总览。最上面一行放四个核心数字总请求量、错误率、平均响应时间、活跃告警数量下面用柱状图展示各服务的请求量和错误率排行。这张图的作用只有一个——扫一眼就能定位到“哪个服务有问题”。第二个是链路追踪视图。按服务维度展示P50、P95、P99响应时间用折线图叠加时间维度能直观看到高峰期系统变慢的节奏。排查慢请求时我习惯在这里把时间范围缩小到故障窗口看P99曲线是否出现“尖刺”从而确定故障发生的准确时间点。第三个是错误聚类视图。按异常类型聚合最近一小时出现的错误日志把相同堆栈的错误聚在一起显示“出现了多少次、影响哪些服务”。排查问题时的第一件事就是看这里它比一条一条翻日志高效太多。第四个是流量拓扑视图。用TSVB或Lens画一个简单的服务调用流量图展示每个服务的请求占比、上下游关系、错误率传播方向。这个视图在分析“一个服务变慢后连带拖垮下游服务”时特别有用能看到故障是怎么从单点扩散出去的。5.2 几个高频检索语句使用Kibana最频繁的操作是探索模式的检索我在这里放几个特别常用的检索体按trace_id追踪一条完整请求链路trace_id:a1b2c3d4e5f6a7b8c9d0e1f2 AND service:*按服务聚合近15分钟错误情况service:order-api AND level:error OR level:critical找某条慢请求的明细service:payment-service AND response_time_ms:2000排除健康检查请求的监控可用性查询NOT request_path:/health AND NOT request_path:/metrics这些语句看起来简单但在紧急时刻能省下大量“点来点去”的时间。毕竟告警来了之后每一分钟都在影响线上体验。6. 实操过程记录从零到一搭建PLFM_RADAR完整流程前面讲了“为什么”和“配置是啥”这部分我按时间线完整还原一次落地操作从环境准备到验证通过。如果你想直接照着搭这一节可以当checklist用。6.1 环境准备与初始规划第一步确认资源。我的场景日均日志量约2亿条峰值每秒5万条ES集群用了3个数据节点每个节点配置8核16G内存SSD磁盘1.5T。Kafka是2台4核8Gtopic分区设12个保证消费端有足够的并发度。如果日志量小一个数量级完全不用这么豪华单节点ES加单节点Kafka就能跑得很轻松后面的配置方法是一样的。第二步安装编排。我用Docker Compose把Kafka、ES、Kibana、Logstash编排起来按依赖顺序启动先Kafka接着ES再Kibana和Logstash。日志采集端用细粒度部署逐批在生产机器上装Filebeat先拿几台试点服务器灰度确认没有数据丢失和资源异常后才全面铺开。6.2 核心组件启动Kafka启动后要做两件事创建Topic设置保留时间。日志的主题我设保留时间为2天足以支撑日志积压排查又不至于占太多磁盘。kafka-topics.sh --create \ --topic plfm-biz-log \ --partitions 12 \ --replication-factor 2 \ --config retention.ms172800000ES启动后先加载ILM策略和索引模板等模板生效后再让Logstash开始消费。这里有个执行顺序讲究如果Logstash先跑而模板还没建好ES会按默认配置自动建索引之后你再补模板也治不了已经建立的索引。所以我踩过一次坑之后整理出固定的启动顺序策略 - 模板 - Logstash - Filebeat。6.3 全链路验证方法数据通了没有不用靠肉眼猜。验证从Kafka端开始用消费端命令确认Topic有数据进入。kafka-console-consumer.sh --bootstrap-server kafka1:9092 --topic plfm-biz-log --from-beginning --max-messages 10再到ES端验证打开Kibana的Dev Tools查一下索引计数。GET plfm-biz-log-*/_count如果索引计数每天都在稳步增长而且每分钟的曲线跟业务流量曲线对得上说明链路没有丢数据。更精确的验证方法是用ES的Rollup或者聚合计算每分钟写入条数。最后在Kibana里打开“Discover”模式搜索最近5分钟的数据随便点开一条日志确认字段结构是否完整、时间戳是否正常、耗时字段是否能被识别为数字类型。到这一步一条日志从业务服务打印到ES可检索的完整链路就算跑通了。7. 常见问题与排查技巧实录系统上线后实际运维中一定会遇到几个高频问题。我把踩过最深的几个坑按发生频率排个序附带排查解决思路。7.1 告警风暴怎么根治前面提到过告警太多会变成“狼来了”。告警风暴的根源通常是三个阈值设得过敏感、规则窗口设得太短、缺少去重和聚合机制。排查方法是先把告警规则按触发次数排行把Top3的规则单独拎出来看往往能发现某个接口的错误率虽然只有1%但因为请求量巨大“1%的错误次数”绝对量也很吓人触发了P0告警。我的解决方案是给这类接口加一个“百分比绝对次数”的双重条件错误率确实超过阈值同时错误次数也要达到比如每分钟200次才会触发高等级告警避免小流量服务的抖动直接拉响P0。7.2 时间戳偏差导致数据“看不清”排查问题时发现“故障已经发生了但监控看板上那个时间段就是没有数据”十有八九是时区和时间格式的问题。服务端打印日志用的本地时间往往不带时区信息Logstash解析时如果不指定timezone默认按UTC处理那么在Kibana里看到的日志时间就会比真实时间慢8小时。我在Logstash配置里统一用date插件强制转成Asia/Shanghai并且建议所有应用日志在打印时统一采用ISO8601带时区偏移的格式从源头杜绝歧义。7.3 磁盘增长超出预期ES数据量增长永远比你预估得快。当你发现磁盘很快被占满第一件事是查ILM是否真的在工作。命令是检查索引的日期后缀如果索引名还是plfm-biz-log-2024.10.01这种按天命名但没有自动滚动的迹象说明策略没绑上。另一种情况是ILM在跑但清理阶段不生效多数是因为min_age设置得太长。我后来把删除阶段从30天缩短到15天同时针对大体积索引单独做了forcemerge和shrink磁盘占用率明显回落。另外不要忽略Kafka的磁盘它默认删除策略是“超过7天删除旧数据”对于日志管道来说保留2天就足够及早调短retention.ms可以给磁盘留出大量余量。7.4 查询慢、聚合超时PLFM_RADAR用了一段时间后查询开始明显变慢主要原因有两个要么是索引映射里某个字段是text类型却被频繁做keyword聚合要么是索引分片数设置不合理查询必须广播给几十个分片。排查思路是先确定“是不是必须全文检索的字段用了text”对服务名、状态码、trace_id这类字段我全部映射为keyword类型这样聚合和精确匹配的速度会提升好几倍。分片数方面虽然前面提到新建索引设5个主分片但如果单日索引数据量只有几百MB1~2个分片足够分片数不是越大越好。7.5 Kafka消费积压导致消息延迟日志从产生到ES可检索本来应该只有几秒延迟如果发现延迟到了分钟级很可能是Logstash消费能力跟不上。排查方式看Kafka消费组的consumer-lag积压超过几百万条就要考虑给Logstash加pipeline worker或者直接加Logstash实例把同一个group id下多个消费者实例并起来消费。我在实测中发现Logstash的pipeline.batch.size默认125对于大日志行来说有点保守调成500配合pipeline.workers: 8消费吞吐能提升一倍以上。但注意不要无限调大worker多了对ES写入压力也越来越大ES写入瓶颈变成下一个受限点一切要均衡。结尾最后分享一个我实际摸索出的技巧前面讲完了架构、配置、实践和排坑最后我想分享一个让我后来排查故障效率翻倍的小技巧。在PLFM_RADAR的每个业务日志里我强制加了两个字段trace_id和biz_code。trace_id用于串联一次请求在各个服务间的流转biz_code记录业务层面的执行结果码比如库存不足是BZ1001、支付超时是BZ2003。有了这两个字段排查问题时不再是一个服务一个服务地翻日志而是直接拿trace_id把一次请求的全链路日志一次性捞出来按时间顺序排开一眼就能看到卡在哪个环节。遇到那些“偶发、看起来没有规律的报错”用biz_code做聚合统计往往能很快找出隐藏在数据分布背后的规律。这套系统从搭建到现在已经成为我们平台上线发布、日常巡检、故障处置的必备基础。相比最开始半夜被用户叫醒的狼狈现在的体验是告警先在群里说话值班人员打开面板看两眼就能定位问题。如果你也想摆脱“出了事靠用户提醒”的被动局面不妨按这个思路逐步落地一套属于你自己的平台雷达。搭它的投资不会太大但它带来的安全感绝对值回票价。
返回列表