我们团队用Prometheus做监控已经一年多了,从最开始的简单服务器监控,到后来的应用性能监控、业务指标监控,Prometheus已经成了我们运维体系的核心。平时,我们通过Prometheus和Grafana,能实时看到系统的运行状态,通过Alertmanager接收告警,出了问题能第一时间发现和处理。
一直以来,Prometheus都很稳定,我们甚至都忘了它也可能出故障。直到最近,Prometheus出了一次严重的故障,导致整个监控系统瘫痪了将近4个小时。
那4个小时,我们就像"盲人"一样,完全不知道系统的运行状态,不知道CPU使用率多少,不知道错误率多少,不知道服务是不是正常。期间,我们的核心业务系统还真的出了一个小问题,但是因为没有监控,我们晚了将近1个小时才发现,差点引发了更大的线上事故。
这次故障惊心动魄,也给我们敲响了警钟:监控系统本身的高可用,和被监控的系统一样重要。监控系统如果挂了,被监控的系统就像在"裸奔",出了问题都不知道。
今天,我想完整复盘这次Prometheus监控故障,包括故障现象、排查过程、根本原因、解决方案、以及我们从中学到的经验教训。希望能给用Prometheus的朋友一些参考,避免踩同样的坑。
一、故障现象
先说说故障发生时的现象。
那天是周三,下午两点多,我们的运维同学首先发现了异常:Grafana的仪表盘打不开了,显示"502 Bad Gateway"。一开始以为是Grafana的问题,重启了Grafana,但是还是不行。然后发现,Prometheus的Web UI也打不开了,访问Prometheus的9090端口,连接超时。
这时候,我们意识到,可能是Prometheus出问题了。我们登录到Prometheus的服务器,发现:
- Prometheus进程还在,但是没有响应:进程还活着,CPU使用率很低(几乎为0),内存使用率很高(占了80%以上),但是访问9090端口没有任何响应,就像进程卡死了一样。
- 告警系统瘫痪:Alertmanager也收不到告警了,因为Prometheus不发送告警了。我们测试了一下,手动触发一个告警,但是Alertmanager没有收到。
- 监控数据停止采集:Prometheus不再采集新的监控数据了,Grafana上的所有图表,都停留在了下午两点多,之后就是空白。
- 服务器IO等待很高:用top命令看,服务器的iowait很高(30%以上),说明磁盘IO有瓶颈。用iostat看,磁盘的读写速度很高,但是IOPS很低,说明磁盘在大量读写大文件。
- Prometheus的日志没有明显错误:看Prometheus的日志,没有明显的错误信息,最后几条日志是正常的TSDB compaction(数据压缩)的日志,然后就没有新的日志了。
这时候,我们还没意识到问题的严重性,以为只是Prometheus暂时卡住了,重启一下应该就好了。但是,重启Prometheus之后,问题更严重了。
重启后的情况:
- Prometheus启动很慢,启动了将近10分钟才起来(平时启动只要几十秒)
- 启动之后,能访问Web UI了,但是查询很慢,一个简单的查询要好几分钟才返回结果
- 内存使用率持续上升,从启动时的20%,很快就升到了80%、90%
- 运行了不到10分钟,Prometheus又卡死了,和之前一样,没有响应
- 反复重启了几次,都是同样的情况:启动慢,运行一会儿就卡死
这时候,我们意识到,这不是简单的重启能解决的问题,Prometheus出了比较严重的故障。而且,因为监控系统瘫痪了,我们完全不知道被监控的业务系统的运行状态,心里非常没底。
更糟糕的是,就在这个时候,我们的客服接到了用户反馈,说我们的核心业务系统(订单系统)响应很慢,有时候还报错。但是因为没有监控,我们不知道是真的有问题,还是个别用户的网络问题,也不知道问题有多严重。只能赶紧登录到业务服务器,手动看日志、看进程、看资源,像"盲人摸象"一样排查。
那几个小时,真的是惊心动魄,一边要抢修Prometheus监控,一边要排查业务系统的问题,人手严重不足,大家都很紧张。
二、排查过程
发现问题严重之后,我们立刻成立了临时小组,分两拨人:一拨人抢修Prometheus,一拨人排查业务系统的问题。
我主要负责抢修Prometheus,下面说说我们的排查过程。
第一步:检查磁盘空间和数据目录
因为之前看到服务器的iowait很高,磁盘IO有瓶颈,我们首先检查了磁盘空间和Prometheus的数据目录。
检查发现:
- 磁盘空间还剩30%,没有满
- Prometheus的数据目录(data/)大小是150GB,比平时大了很多(平时大概80GB)
- 数据目录里,有很多wal(Write-Ahead Log)文件,加起来有50GB,而平时wal文件只有几个GB
- 还有很多临时的文件,文件名是.tmp结尾的
这说明,Prometheus在写大量的wal文件和临时文件,导致磁盘IO很高。但是为什么会写这么多文件呢?我们还不清楚。
第二步:检查Prometheus的配置和采集目标
我们检查了Prometheus的配置文件(prometheus.yml),看看是不是配置有问题。
检查发现:
- 配置文件没有被修改过,和之前一样
- 采集目标(scrape_configs)的数量是120个,和平时一样,没有突然增加
- 采集间隔(scrape_interval)默认是15秒,和平时一样
- 数据保留时间(retention)是30天,和平时一样
配置看起来没问题,不是配置变更导致的。
第三步:检查Prometheus的日志,开启debug模式
我们之前看Prometheus的日志,没有明显的错误。我们决定开启debug模式,看看更详细的日志。
在启动参数里加了--log.level=debug,重启Prometheus。这次,我们看到了大量的debug日志,发现了一个异常:
Prometheus在不停地做TSDB compaction(数据压缩),而且每次compaction都失败了,失败之后又重试,又失败,陷入了死循环。
失败的原因是:"compaction failed: corruption in block XXXXX: out of order sequence"。意思是,某个数据块(block)损坏了,里面的时序数据顺序不对,导致compaction失败。
这就解释了为什么Prometheus会卡死:它在不停地尝试compaction,但是每次都失败,失败了又重试,占用了大量的CPU和磁盘IO,导致正常的查询和采集都无法进行。而且,因为compaction失败,旧的wal文件无法被清理,越积越多,导致数据目录越来越大,磁盘IO越来越高。
第四步:定位损坏的数据块
找到了问题的原因(数据块损坏导致compaction死循环),我们接下来要定位是哪个数据块损坏了,然后想办法修复或者删除。
从日志里,我们看到损坏的block ID是"01CXXXXXX"(具体ID记不清了)。我们在数据目录里找到了这个block,它在data/01CXXXXXX/目录下。
我们检查了这个block的文件,发现:
- 这个block比正常的block大很多,有20GB,而正常的block一般是1-2GB
- 里面的index文件和chunks文件,大小都不正常
- 用Prometheus的tsdb工具(prometheus tsdb analyze)分析,确认这个block确实损坏了,里面有大量的乱序数据
第五步:分析数据块损坏的原因
定位了损坏的数据块,我们接下来要分析:为什么这个数据块会损坏?
我们回忆了一下,故障发生前,有没有什么异常操作。想起来了:故障发生的前一天晚上,我们做了一次服务器维护,Prometheus的服务器意外断电了(因为UPS故障),然后非正常关机了。
我们推测,就是那次意外断电,导致Prometheus正在写数据的时候被中断了,造成了数据块的损坏。Prometheus的TSDB虽然有wal机制,能在一定程度上保证数据安全,但是非正常关机还是有可能导致数据损坏,特别是在写入量大的时候。
后来,我们查了Prometheus的issue,发现确实有类似的问题:非正常关机可能导致TSDB数据块损坏,而且损坏之后,Prometheus会陷入compaction死循环,无法正常启动和运行。这个问题在某些版本的Prometheus里存在,我们用的版本正好中招了。
第六步:临时修复,恢复监控
找到了原因和损坏的数据块,我们接下来要修复,尽快恢复监控。
最简单的修复方法,就是删除损坏的数据块。但是,删除数据块会丢失那一段时间的监控数据,而且,如果删除之后还有其他损坏的数据块,可能还会出问题。
我们考虑了几个方案:
- 删除损坏的block:最简单,但是会丢失数据,而且可能还有其他损坏的block
- 用tsdb repair工具修复:Prometheus有一个tsdb repair工具,可以尝试修复损坏的数据,但是不一定能成功
- 备份数据,重新初始化TSDB:最彻底,但是会丢失所有历史数据
- 升级Prometheus版本:新版本可能修复了这个bug,但是升级也有风险
我们决定,先尝试方案1(删除损坏的block),因为最快,能尽快恢复监控。如果删除之后还有问题,再考虑其他方案。
我们先备份了整个data目录(以防万一),然后删除了损坏的block目录,重启Prometheus。
这次,Prometheus启动正常了,compaction也正常了,没有再陷入死循环。Web UI能访问了,查询也正常了,Grafana的仪表盘也恢复了。监控数据也开始重新采集了。
但是,因为删除了一个block,我们丢失了大约2天的监控数据(那个block覆盖的时间范围)。不过,能恢复监控就好,丢失2天数据可以接受。
我们又观察了几个小时,确认Prometheus运行稳定,没有再出现compaction失败的情况,才松了一口气。
这时候,距离故障发生,已经过去了将近4个小时。监控系统终于恢复了,我们也终于能看到业务系统的运行状态了。
幸运的是,业务系统的问题不算严重,是因为数据库连接池配置不合理,在高峰期连接不够用,导致响应慢。我们调整了连接池配置,问题就解决了。如果监控系统没挂,我们应该能更早发现这个问题,不会让用户受影响这么久。
三、根本原因分析
故障解决之后,我们做了深入的根本原因分析,想搞清楚为什么会出现这个问题,以及以后怎么避免。
根本原因一:Prometheus单点部署,没有高可用
我们的Prometheus是单点部署的,只有一个实例,没有做高可用。一旦这个实例出问题,整个监控系统就瘫痪了。
这是最根本的原因。监控系统作为基础设施,应该和业务系统一样,做高可用部署,不能有单点故障。但是我们之前忽视了这一点,觉得Prometheus很稳定,不会出问题,就没有做高可用。
根本原因二:服务器意外断电,导致TSDB数据损坏
直接原因是服务器意外断电(UPS故障),导致Prometheus非正常关机,TSDB数据块损坏。
虽然Prometheus有wal机制,能在一定程度上保证数据安全,但是非正常关机还是有可能导致数据损坏。而且,我们用的Prometheus版本,在数据损坏后的处理上有bug,会陷入compaction死循环,导致整个系统瘫痪。
根本原因三:没有监控的监控(监控监控系统本身)
我们用Prometheus监控业务系统,但是没有监控Prometheus本身。Prometheus出问题了,我们是通过Grafana打不开才发现的,而不是通过告警发现的。如果我们有监控Prometheus本身的机制,应该能更早发现问题。
而且,我们也没有监控Prometheus服务器的资源使用情况(CPU、内存、磁盘、IO等),如果有监控,应该能在磁盘IO升高、内存使用率升高的时候就发现异常,提前处理,不会等到整个系统瘫痪。
根本原因四:没有定期备份Prometheus数据
我们没有定期备份Prometheus的数据。故障发生的时候,我们只能现场备份,然后删除损坏的数据块,丢失了2天的数据。如果我们有定期备份,就可以用备份恢复,不会丢失数据,或者丢失更少的数据。
根本原因五:Prometheus版本较老,没有及时升级
我们用的Prometheus版本比较老(2.3.x),这个版本在TSDB数据损坏后的处理上有bug。新版本的Prometheus(2.5+)已经修复了这个bug,数据损坏之后不会陷入死循环,而是会跳过损坏的block,继续运行。
我们没有及时升级Prometheus版本,导致遇到了这个已经被修复的bug。
根本原因六:没有应急预案和演练
我们没有针对监控系统故障的应急预案,也没有做过演练。故障发生的时候,大家都有点慌,不知道该怎么处理,排查问题走了一些弯路,恢复监控花了较长的时间。
如果有应急预案,并且定期演练,故障发生的时候就能按流程处理,更快地恢复监控,减少影响。
四、改进措施
故障解决之后,我们做了一系列的改进措施,避免再出现类似的问题。
措施一:Prometheus高可用部署
这是最重要的改进。我们把Prometheus从单点部署改成了高可用部署。
具体方案:
- 部署两个Prometheus实例,采集完全相同的目标,配置完全相同
- 两个实例独立运行,互不影响,一个挂了另一个还能正常工作
- Grafana配置两个Prometheus数据源,一个主一个备,主的挂了自动切换到备的
- Alertmanager也部署两个实例,做高可用
这样,即使一个Prometheus实例出问题,另一个还能正常工作,监控系统不会完全瘫痪。
当然,这个方案的缺点是,两个Prometheus实例的数据是独立的,不是完全一致的(因为采集时间可能有细微差别),查询的时候可能会有细微的差异。但是对于监控来说,这个差异是可以接受的,重要的是监控系统的可用性。
如果需要更严格的数据一致性,可以用Thanos或者Cortex等方案,做Prometheus的集群化部署,但是复杂度更高。我们目前的规模,双实例高可用就够了。
措施二:监控监控系统本身
我们加了"监控的监控",监控Prometheus本身的运行状态。
具体做法:
- 用一个独立的、轻量级的监控实例(可以是另一个Prometheus,或者其他监控工具),监控主Prometheus的运行状态
- 监控Prometheus的进程状态、端口可用性、启动时间、采集目标数量、数据采集延迟等
- 监控Prometheus服务器的资源使用情况(CPU、内存、磁盘、IO、网络等)
- 设置告警,Prometheus不可用、资源使用率过高、采集延迟过大等情况,及时告警
- 告警通道要独立,不要依赖被监控的Prometheus和Alertmanager
这样,Prometheus出问题的时候,我们能第一时间收到告警,及时处理,不会等到用户反馈或者Grafana打不开才发现。
措施三:定期备份Prometheus数据
我们加了定期备份机制,每天备份Prometheus的数据。
具体做法:
- 每天凌晨,在业务低峰期,用Prometheus的snapshot功能(需要开启--web.enable-admin-api),创建数据快照
- 把快照备份到独立的存储(比如对象存储、另一台服务器)
- 保留最近7天的备份,超过7天的自动删除
- 定期测试备份的可用性,确保备份能正常恢复
这样,即使Prometheus的数据损坏了,也可以用备份恢复,不会丢失太多数据。
措施四:升级Prometheus版本
我们把Prometheus升级到了最新的稳定版本(2.6+),新版本修复了TSDB数据损坏后陷入compaction死循环的bug。数据损坏之后,新版本的Prometheus会跳过损坏的block,记录错误日志,继续正常运行,不会导致整个系统瘫痪。
同时,我们也制定了版本升级计划,定期升级Prometheus和相关组件,及时获取bug修复和性能优化,不要用太老的版本。
措施五:完善服务器的电源保护
我们完善了服务器的电源保护,避免意外断电。
具体做法:
- 更换了新的UPS,确保断电之后能供电足够长时间,让服务器正常关机
- 配置了UPS监控,UPS电量低的时候,自动通知运维人员,并且自动安全关机
- 服务器都配置了双电源,接入不同的PDU,避免单点电源故障
- 定期测试UPS,确保UPS正常工作
措施六:制定应急预案和定期演练
我们制定了监控系统故障的应急预案,并且定期演练。
应急预案包括:
- 故障分级:根据影响范围和严重程度,把故障分为不同等级
- 处理流程:每个等级的故障,对应的处理流程和责任人
- 恢复步骤:Prometheus故障的常见恢复步骤(重启、删除损坏block、从备份恢复、切换到备用实例等)
- 沟通机制:故障发生时的沟通方式和通知范围
- 复盘机制:故障解决后的复盘流程
我们每季度做一次故障演练,模拟Prometheus故障,测试应急预案的有效性,让大家熟悉处理流程。这样,真的出故障的时候,就能按流程快速处理,不会慌。
措施七:优化Prometheus的性能和稳定性
我们也做了一些Prometheus本身的性能和稳定性优化:
- 合理设置采集间隔,非核心指标的采集间隔从15秒改成30秒,减少数据量
- 用relabel_configs,去掉不需要的标签和指标,减少数据量
- 合理设置数据保留时间,从30天改成15天(我们的场景,15天就够了),减少存储压力
- 用TSDB的自动compaction,合理设置compaction的参数
- 监控Prometheus的compaction情况,发现异常及时处理
- 给Prometheus分配足够的内存和CPU,避免资源不足导致的问题
五、经验教训
这次故障,给我们团队上了深刻的一课。我们总结了以下经验教训:
教训一:监控系统的高可用,和业务系统一样重要
这是最重要的教训。监控系统是我们的"眼睛",如果眼睛瞎了,我们就不知道系统的运行状态,出了问题也不能及时发现,可能会导致更严重的业务故障。
所以,监控系统的高可用,和业务系统一样重要,甚至更重要。我们在做系统架构的时候,不能只考虑业务系统的高可用,也要考虑监控系统、日志系统、配置中心等基础设施的高可用。
不要觉得监控系统很稳定,不会出问题。任何系统都可能出问题,稳定是相对的,故障是绝对的。只有做好高可用,才能在故障发生的时候,把影响降到最低。
教训二:要有监控的监控,不能只靠监控系统自己
监控系统监控业务系统,但是谁来监控监控系统呢?答案是:要有独立的、轻量级的"监控的监控",监控监控系统本身的运行状态。
不要觉得监控系统自己会告警,当监控系统本身挂了的时候,它就不能告警了。所以,必须有一个独立于主监控系统的、轻量级的监控,监控主监控系统的可用性,并且用独立的告警通道告警。
这个"监控的监控"不需要很复杂,甚至可以是一个简单的脚本,定期检查Prometheus的端口是否可用,不可用就发邮件或者短信告警。重要的是,它要独立于主监控系统,主监控挂了它还能工作。
教训三:基础设施也要有备份和应急预案
不仅仅是业务数据需要备份,监控数据、日志数据等基础设施的数据,也需要备份。而且,不仅仅是业务系统需要应急预案,监控系统、日志系统等基础设施,也需要应急预案和定期演练。
很多团队,对业务系统的备份和应急预案很重视,但是对基础设施的备份和应急预案不够重视,觉得基础设施很稳定,不会出问题。这次故障告诉我们,基础设施也可能出问题,而且基础设施出问题,影响可能更大,因为它影响的是所有业务系统。
所以,基础设施也要有备份和应急预案,并且定期演练,确保故障发生的时候能快速恢复。
教训四:及时升级软件版本,不要用太老的版本
很多团队,为了稳定,不愿意升级软件版本,觉得"能用就好,不要瞎升级"。这种想法有一定道理,升级确实有风险。但是,也不能因此就一直用很老的版本,因为老版本可能有已知的bug和安全漏洞,新版本已经修复了。
我们这次遇到的bug,就是在新版本里已经修复了的,如果我们及时升级,就不会遇到这个问题。
正确的做法是:平衡稳定性和先进性,不要盲目追新版本,也不要一直用太老的版本。定期评估版本升级,对于有重要bug修复或者安全修复的版本,要及时升级。升级之前,先在测试环境测试,确认没问题再升级到生产环境。
教训五:故障排查要冷静,按流程来,不要慌
故障发生的时候,大家很容易慌,特别是监控系统挂了,业务系统也可能有问题的时候,人手不足,压力很大。但是,越慌越容易出错,越容易走弯路,恢复越慢。
故障排查要冷静,按流程来:
- 先评估影响范围和严重程度
- 分优先级处理,先恢复最重要的服务
- 定位问题,先看日志、看监控、看配置变更,不要瞎猜
- 找到原因后,先尝试快速恢复(重启、回滚、切换等),再深入分析根本原因
- 做好沟通,及时同步进展
同时,要分工明确,不要所有人都去做同一件事。比如这次故障,我们分了两拨人,一拨抢修监控,一拨排查业务,这样效率更高。
教训六:故障复盘很重要,要深入分析,持续改进
故障解决之后,一定要做深入的复盘,不能只解决了问题就完事了。复盘的目的是:找到根本原因,制定改进措施,避免再犯同样的错误。
复盘要深入,不要停留在表面。比如这次故障,表面原因是数据块损坏,但是根本原因是单点部署、没有监控的监控、没有备份、版本太老、没有应急预案等。只有找到所有的根本原因,并且逐一改进,才能真正避免再犯。
而且,改进措施要落地,不能只写在文档里。要指定责任人,设定完成时间,定期跟踪改进措施的落实情况。我们这次故障之后,制定了7项改进措施,每项都有责任人和完成时间,每周跟踪进度,确保都落地了。
六、写在最后
这次Prometheus监控故障,是我们团队经历过的最惊心动魄的故障之一。监控系统瘫痪了将近4个小时,期间业务系统也出了问题,差点造成更大的损失。
但是,从另一个角度看,这次故障也让我们成长了很多。我们对监控系统的重要性有了更深刻的认识,我们的监控架构更完善了(高可用、监控的监控、备份、应急预案等),我们的故障处理能力也提升了。
技术成长的路上,难免会遇到故障,会踩坑。重要的是,从故障中学习,总结经验教训,持续改进,让系统越来越稳定,让团队越来越成熟。
Prometheus是一个很好的监控工具,但是再好的工具,也需要合理的架构和运维,才能保证稳定可用。希望我们的这次故障复盘,能给用Prometheus的朋友一些参考,让你们少踩坑,少走弯路。
最后,用一句话总结:"监控系统是运维的眼睛,眼睛的健康和业务的健康一样重要。高可用、备份、监控的监控、应急预案,一个都不能少。"
愿每一个运维工程师,都能打造出稳定、可靠、高可用的监控系统,让业务系统在监控的守护下,稳定运行。
评论(0)
暂无评论,快来抢沙发~
评论功能仅对会员开放,请先登录
登录