【架构实战】监控告警体系:系统的健康体检
一、一次狼来了事件给我的教训2018年,我经历了一次让我刻骨铭心的事æ•。那天凌晨2点,手机疯狂震动,告警短信一条接一条:Redis连接数è¶é™ã€æ•°æ®åº“CPU 99%、接口响应时间è¶è¿‡5秒。我爬起来一看,告警系统显示系统已经挂了。我赶紧爬起来处理,结果发现——什么问题都没有。Redisæ­£å¸¸ï¼Œæ•°æ®åº“æ­£å¸¸ï¼ŒæŽ¥å£å“åº”æ—¶é—´åªæœ‰å‡ åæ¯«ç§’ã€‚ç¬¬äºŒå¤©ä¸€é—®ï¼ŒåŽŸæ¥æ˜¯è¿ç»´åŒå­¦åœ¨å‡Œæ™¨1点做了一次数据库维护,触发了大量的临时告警,然后这些告警在2ç‚¹é›†ä¸­å‘é€å‡ºæ¥ã€‚ä½†è¿™äº›å‘Šè­¦éƒ½æ˜¯æ— æ•ˆå‘Šè­¦â€”â€”ç³»ç»Ÿåœ¨ç»´æŠ¤æœŸé—´æœ¬æ¥å°±æ˜¯ä¸æ­£å¸¸çš„ã€‚ä»Žé‚£ä»¥åŽï¼Œæˆ‘ä»¬å›¢é˜Ÿå¼€å§‹è®¤çœŸæ€è€ƒå‘Šè­¦ä½“ç³»çš„è®¾è®¡ï¼šä»€ä¹ˆæ ·çš„å‘Šè­¦æ‰æ˜¯çœŸæ­£æœ‰ä»·å€¼çš„ï¼ŸäºŒã€æŒ‡æ ‡ä½“ç³»è®¾è®¡ï¼šè®©ç³»ç»Ÿå¯è§åšç›‘æŽ§ï¼Œé¦–åˆè¦æ¸æ¥šç›‘控什么。业界有两个经å¸çš„监控方法论:RED方法和USE方法。2.1 REDæ–¹æ³•ï¼ˆé¢å‘æœåŠ¡ï¼‰é€‚ç”¨äºŽæ— çŠ¶æ€æœåŠ¡ï¼ˆå¦‚HTTP API):Rate:请求速率(QPS/TPS)Error:错误率Duration:响应时间分布(p50/p90/p99)2.2 USE方法(面向资源)适用于系统资源(如CPU、å†å­˜ã€ç£ç›˜ï¼‰ï¼šUtilization:利用率Saturation:饱和度Errors:错误数2.3 æˆ‘ä»¬çš„æŒ‡æ ‡ä½“ç³»æˆ‘ä»¬æœ€ç»ˆè®¾è®¡çš„æŒ‡æ ‡ä½“ç³»å¦‚ä¸‹ï¼š# å¾®æœåŠ¡æŒ‡æ ‡é‡‡é›†éç½®ï¼ˆPrometheusæ ¼å¼ï¼‰# ä¸šåŠ¡æŒ‡æ ‡app_business:order_count:type:counterdescription:订单创建数量labels:[service,status]payment_amount:type:counterdescription:支付金额labels:[service,payment_type]active_users:type:gaugedescription:活跃用户数labels:[service]# HTTPæŒ‡æ ‡http_requests:total:type:counterdescription:HTTP请求总数labels:[method,path,status]duration_seconds:type:histogramdescription:HTTP响应时间labels:[method,path]buckets:[0.005,0.01,0.025,0.05,0.1,0.25,0.5,1,2.5,5,10]# JVMæŒ‡æ ‡jvm:memory_used_bytes:type:gaugedescription:JVMå·²ä½¿ç”¨å† å­˜labels:[area,service]gc_count:type:counterdescription:GC次数labels:[gc_type]thread_count:type:gaugedescription:活跃线程数labels:[thread_type]# æ•°æ®åº“æŒ‡æ ‡database:connections:type:gaugedescription:数据库连接数labels:[pool_name]query_duration_seconds:type:histogramdescription:SQL执行时间labels:[operation,table]# ç¼“å­˜æŒ‡æ ‡redis:commands_total:type:counterdescription:Redis命令总数labels:[command,status]keyspace_keys:type:gaugedescription:Key数量labels:[db]memory_used_bytes:type:gaugedescription:Redisä½¿ç”¨å† å­˜2.4 å³é”®å‘Šè­¦é˜ˆå€¼è®¾è®¡# AlertManager告警规则éç½®groups:-name:business_alertsrules:-alert:HighErrorRateexpr:|sum(rate(http_requests_total{status~5..}[5m])) / sum(rate(http_requests_total[5m])) 0.05for:5mlabels:severity:criticalannotations:summary:服务{{ $labels.service }}错误率过高description:5åˆ†é’Ÿå† é”™è¯¯çŽ‡è¶ è¿‡5%,当前值:{{$value|printf \%.2f\}}%-alert:OrderCountDropexpr:|sum(increase(app_business_order_count[10m])) 100for:5mlabels:severity:warningannotations:summary:订单数量异常下降description:最近10分钟订单数少于100单,可能存在业务问题-name:infrastructure_alertsrules:-alert:HighCPUUsageexpr:|100 - (avg by(instance) (rate(node_cpu_seconds_total{modeidle}[5m])) * 100) 80for:10mlabels:severity:warningannotations:summary:CPU使用率过高description:服务器{{$labels.instance}}CPUä½¿ç”¨çŽ‡è¶ è¿‡80%-alert:JVMHeapMemoryHighexpr:|jvm_memory_used_bytes{areaheap} / jvm_memory_max_bytes{areaheap} 0.85for:5mlabels:severity:warningannotations:summary:JVMå †å† å­˜ä½¿ç”¨çŽ‡è¿‡é«˜description:服务{{$labels.service}}å †å† å­˜ä½¿ç”¨çŽ‡è¶ è¿‡85%-alert:DatabaseConnectionPoolExhaustedexpr:|datasource_connections_active / datasource_connections_max 0.9for:2mlabels:severity:criticalannotations:summary:æ•°æ®åº“è¿žæŽ¥æ± å³å°†è€—å°½description:{{$labels.pool_name}}è¿žæŽ¥æ± ä½¿ç”¨çŽ‡è¶ è¿‡90%三、Grafanaå¤§ç›˜è®¾è®¡å‰æœ‰å‘Šè­¦è¿˜ä¸å¤Ÿï¼Œè¿˜éœ€è¦å¯è§†åŒ–大盘让团队对系统状态一目了然。3.1 大盘分层设计┌─────────────────────────────────────────────────────────────────┐ │ 系统概览大屏 │ ├──────────────┬──────────────┬──────────────┬───────────────────┤ │ 请求QPS │ 错误率 │ 平均响应时间 │ 在线用户数 │ │ â–“â–“â–“â–“â–“â–‘â–‘â–‘ │ 0.12% ✠│ 45ms ✠│ 12,345 │ ├──────────────┴──────────────┴──────────────┴───────────────────┤ │ 各服务健康状态 │ ├──────────────┬──────────────┬──────────────┬───────────────────┤ │ 订单服务 │ 支付服务 │ 用户服务 │ 商品服务 │ │ ● 健康 42ms │ ● 健康 38ms│ ● 健康 25ms │ ● 健康 55ms │ ├──────────────┴──────────────┴──────────────┴───────────────────┤ │ 基础设施状态 │ ├──────────────┬──────────────┬──────────────┬───────────────────┤ │ CPU: 45% │ å† å­˜: 62% │ 磁盘: 78% │ 网络: 正常 │ │ â–“â–“â–“â–“â–‘â–‘â–‘â–‘â–‘ │ â–“â–“â–“â–“â–“â–“â–‘â–‘â–‘ │ â–“â–“â–“â–“â–“â–“â–“â–“â–‘â–‘ │ ● 正常 │ └──────────────┴──────────────┴──────────────┴───────────────────┘3.2 Grafana Dashboard JSONéç½®{dashboard:{title:微服务监控大盘,tags:[microservice,production],timezone:Asia/Shanghai,panels:[{title:服务请求QPS,type:graph,gridPos:{x:0,y:0,w:12,h:8},targets:[{expr:sum(rate(http_requests_total{service~\$service\}[1m])) by (service),legendFormat:{{service}}}],alert:{name:QPS异常告警,conditions:[{evaluator:{params:[10],type:lt},operator:{type:and},query:{params:[A,5m,now]},reducer:{type:avg}}],frequency:1m,noDataState:no_data}},{title:服务错误率,type:stat,gridPos:{x:12,y:0,w:6,h:4},targets:[{expr:sum(rate(http_requests_total{status~\5..\}[5m])) / sum(rate(http_requests_total[5m])) * 100}],fieldConfig:{defaults:{thresholds:{mode:absolute,steps:[{color:green,value:null},{color:yellow,value:1},{color:red,value:5}]},unit:percent,decimals:2}}},{title:JVMå †å† å­˜ä½¿ç”¨çŽ‡,type:gauge,gridPos:{x:18,y:0,w:6,h:4},targets:[{expr:jvm_memory_used_bytes{area\heap\} / jvm_memory_max_bytes{area\heap\} * 100}],fieldConfig:{defaults:{thresholds:{mode:percentage,steps:[{color:green,value:null},{color:yellow,value:70},{color:red,value:85}]},unit:percent,max:100}}},{title:æ•°æ®åº“è¿žæŽ¥æ± ,type:graph,gridPos:{x:0,y:8,w:12,h:8},targets:[{expr:datasource_connections_active{pool_name~\$pool\},legendFormat:活跃连接},{expr:datasource_connections_idle{pool_name~\$pool\},legendFormat:空闲连接},{expr:datasource_connections_max{pool_name~\$pool\},legendFormat:最大连接}]}]}}四、告警分级与收敛4.1 告警分级策略我们把告警分为5个级别:级别名称定义响应时间通知方式P0æœ€é«˜æ ¸å¿ƒä¸šåŠ¡å®Œå¨ä¸å¯ç”¨5分钟å†ç”µè¯ 短信 钉钉P1é«˜æ ¸å¿ƒåŠŸèƒ½å—æŸ15分钟å†çŸ­ä¿¡ 钉钉P2ä¸­éžæ ¸å¿ƒåŠŸèƒ½å¼‚å¸¸1小时å†é’‰é’‰P3低潜在风险工作时间处理邮件P4æç¤ºæ— å³ç´§è¦çš„æç¤ºä¸å¤„理日志4.2 告警收敛策略告警风暴是最大的敌人。我们使用AlertManager的分组和抑制功能:# AlertManageréç½®global:resolve_timeout:5msmtp_smarthost:smtp.example.com:587smtp_from:alertexample.com# 告警路由éç½®route:group_by:[alertname,cluster,service]group_wait:30s# ç­‰å¾30秒分组group_interval:5m# 每5分钟发送一次分组告警repeat_interval:4h# 重复告警间隔4小时receiver:defaultroutes:# P0/P1告警立即发送-match:severity:criticalreceiver:critical-alertsgroup_wait:0srepeat_interval:1h# P2告警收敛后发送-match:severity:warningreceiver:warning-alertsgroup_wait:1mrepeat_interval:4h# 按服务分组-match:service:order-servicereceiver:order-teamroutes:-match:severity:criticalreceiver:order-team-criticalgroup_wait:0sreceivers:-name:critical-alerts# 电话通知(使用è¾è®¯äº‘电话服务)webhook_configs:-url:http://alert-phone.example.com/callsend_resolved:true# 钉钉通知webhook_configs:-url:http://dingtalk.example.com/webhooksend_resolved:trueheaders:Content-Type:application/jsonmax_alerts:10# 邮件通知email_configs:-to:oncallexample.comsend_resolved:true-name:warning-alertswebhook_configs:-url:http://dingtalk.example.com/webhook-warning# 只发钉钉,不发电话和邮件# 告警抑制规则inhibit_rules:# 当服务器宕机时,抑制该服务器上所有服务的所有告警-source_match:alertname:ServerDownsource_labels:[instance]target_match_re:alertname:.*target_labels:instance:{{ $value }}equal:[cluster]# 当整个集群不可用时,抑制该集群上所有服务的所有告警-source_match:alertname:ClusterDownsource_labels:[cluster]target_match_re:alertname:.*target_labels:cluster:{{ $value }}equal:[namespace]五、踩坑实录:告警体系的血泪教训坑1ï¼šå‘Šè­¦é£Žæš´å¯¼è‡´ç‹¼æ¥äº†æ•ˆåº”è¿™æ˜¯æˆ‘ä»¬è¸©è¿‡æœ€å¤§çš„å‘ã€‚æœ‰ä¸€æ¬¡ï¼Œæ•°æ®åº“ä¸»ä»Žåˆ‡æ¢ï¼Œè§¦å‘äº†å‡ ç™¾æ¡å‘Šè­¦ã€‚è¿ç»´äººå‘˜è¢«æ·¹æ²¡åœ¨å‘Šè­¦çš„æµ·æ´‹é‡Œï¼Œé”™è¿‡äº†çœŸæ­£é‡è¦çš„å‘Šè­¦â€”â€”åº”ç”¨æœåŠ¡å™¨ç£ç›˜æ»¡äº†ã€‚è§£å†³ï¼šä½¿ç”¨AlertManagerçš„group_by对同类告警进行聚合设置合理的group_wait(30秒),避åå‘Šè­¦ç¢Žç‰‡åŒ–éç½®æŠ‘制规则,上游æ•障时抑制下游告警坑2ï¼šè¯¯å‘Šè­¦é€ æˆä¸å¿è¦çš„ç´§æ€¥å“åº”æœ‰äº›ç›‘æŽ§æŒ‡æ ‡æœ¬èº«æœ‰æ¯›åˆºï¼ˆæ¯”å¦‚çž¬æ—¶CPU飙升),但我们的告警没有设置合理的for时长,导致瞬时波动就触发告警。解决:所有告警å¿é¡»è®¾ç½®for时长(通常5分钟),避åçž¬æ—¶æ³¢åŠ¨è§¦å‘å‘Šè­¦å³é”®æŒ‡æ ‡å¢žåŠ æ¸è¿›å‘Šè­¦ï¼šåˆwarning,过更长时间再critical定期Review告警规则,æ¸ç†æ— 效告警坑3:夜间告警没人管有一次,P2级别的告警在凌晨3点触发,但只有一个人收到了通知,而这个人睡着了。第二天早上才发现问题。解决:建立值班制度,明确每个时间段的值班人P0/P1告警å¿é¡»ç”µè¯é€šçŸ¥ï¼Œä¸”需要确认收到P2告警在非工作时间发送给值班人,不打扰å¶ä»–人坑4ï¼šå‘Šè­¦é€šçŸ¥æ¸ é“å•ä¸€æˆ‘ä»¬æœ€å¼€å§‹åªç”¨é‚®ä»¶é€šçŸ¥å‘Šè­¦ï¼Œç»“æžœå‘çŽ°ï¼šç´§æ€¥å‘Šè­¦æ²¡äººçœ‹é‚®ä»¶é‚®ä»¶å»¶è¿Ÿå¯¼è‡´å“åº”ä¸åŠæ—¶è§£å†³ï¼šå»ºç«‹å¤šæ¸ é“å‘Šè­¦ï¼šç”µè¯ï¼šP0级别,å¿é¡»æŽ¥å¬çŸ­ä¿¡ï¼šP0/P1级别钉钉/飞书群:所有级别邮件:P3/P4级别,ä»ä¾›è®°å½•å­ã€ä¸šåŠ¡åœºæ™¯ï¼šæŸé‡‘èžå¬å¸æ­å»ºå®Œæ•´å¯è§‚测体系的完整过程这家å¬å¸ï¼ˆæˆ‘们叫他Aå¬å¸ï¼‰æ˜¯ä¸€å®¶åšæ¶ˆè´¹é‡‘融的创业å¬å¸ã€‚2020年,他们从零开始搭建可观测体系。第一阶段:只有基础监控(2020å¹´Q1)当时他们的监控状态是:只有服务器基础监控(CPU、å†å­˜ã€ç£ç›˜ï¼‰æ²¡æœ‰ä»»ä½•应用层监控告警只有邮件每天早上看一次监控大盘问题:经常收到用户投诉系统æ¢äº†ï¼Œä½†å¼€å‘团队完å¨ä¸çŸ¥é“发生了什么。第二阶段:引å¥APM(2020å¹´Q2)引å¥äº†SkyWalking APM,搭建了链路追踪能力:看到了服务间的调用å³ç³»å‘现了大量的æ¢SQL看到了每个接口的响应时间分布改善:能定位问题了,但还是被动——总是在出问题后才知道。第三阶段:建立完整可观测体系(2020å¹´Q3-Q4)建立了完整的三板斧可观测体系:日志:ELK Stack结构化日志(JSONæ ¼å¼ï¼‰TraceIDè´¯ç©¿æ‰€æœ‰æ—¥å¿—æ—¥å¿—èšåˆå’Œæœç´¢æŒ‡æ ‡ï¼šPrometheus GrafanaRED方法覆盖所有HTTP服务USEæ–¹æ³•è¦†ç›–æ‰€æœ‰åŸºç¡€è®¾æ–½è‡ªå®šä¹‰ä¸šåŠ¡æŒ‡æ ‡é“¾è·¯ï¼šSkyWalkingå¨é“¾è·¯è¿½è¸ªæ‹“æ‰‘å›¾è‡ªåŠ¨ç”Ÿæˆæ¢æœåŠ¡åˆ†æžå‘Šè­¦ï¼šAlertManager 钉钉分级告警(P0-P4)告警聚合和收敛值班机制成果:MTTD(平均发现时间)从4小时缩短到5分钟MTTR(平均恢复时间)从2小时缩短到30分钟用户投诉系统æ¢çš„æ•°é‡ä¸‹é™äº†80%七、总结与思考监控告警体系建设的å³é”®è¦ç‚¹ï¼š**分层监控**ï¼šåŸºç¡€è®¾æ–½å±‚ã€åº”ç”¨å±‚ã€ä¸šåŠ¡å±‚éƒ½è¦è¦†ç›–é»„é‡‘æŒ‡æ ‡ï¼šå»¶è¿Ÿã€æµé‡ã€é”™è¯¯çŽ‡ã€é¥±å’Œåº¦æ˜¯æ ¸å¿ƒ**告警分级**ï¼šä¸æ˜¯æ‰€æœ‰å‘Šè­¦éƒ½ä¸€æ ·é‡è¦ï¼Œè¦åˆ†çº§å¤„ç†å‘Šè­¦æ”¶æ•›ï¼šé¿åå‘Šè­¦é£Žæš´æ·¹æ²¡çœŸæ­£é‡è¦çš„告警**值班机制**:确保告警有人响应,不能石沉大海持续优化:定期Review告警规则,æ¸ç†æ— æ•ˆå‘Šè­¦è¡€çš„æ•™è®­ï¼šå‘Šè­¦ä½“ç³»çš„æ ¸å¿ƒä¸æ˜¯å‘çŽ°æ‰€æœ‰é—®é¢˜ï¼Œè€Œæ˜¯å‘çŽ°çœŸæ­£éœ€è¦äººå·¥ä»‹å¥çš„é—®é¢˜ã€‚å‘Šè­¦å¤ªå¤šå’Œå‘Šè­¦å¤ªå°‘åŒæ ·æœ‰å®³ã€‚ç»™ä½ çš„æ€è€ƒé¢˜ï¼šä½ ä»¬å›¢é˜Ÿçš„å‘Šè­¦ä½“ç³»æœ‰æ²¡æœ‰ç‹¼æ¥äº†çš„é—®é¢˜ï¼Ÿå¦‚æžœåŠå¤œæ”¶åˆ°ä¸€ä¸ªP2å‘Šè­¦ï¼Œä½ ä¼šæ€Žä¹ˆå¤„ç†ï¼Ÿä¸ªäººè§‚ç‚¹ï¼Œä»ä¾›å‚考

相关新闻