上周我们线上的Nacos配置中心出了一次严重的故障,导致十几个服务的配置无法更新,部分服务因为读取不到配置而启动失败,整个故障持续了将近两个小时,影响了好几个业务线。这是我经历过的最惊心动魄的一次故障,也是最有收获的一次故障复盘。
事情已经过去几天了,现在想起来还是心有余悸。故障发生的时候,整个团队都很紧张,几个人轮流排查,电话一个接一个,业务方一直在催,那种压力和紧迫感,没有经历过的人很难体会。但也正是这次故障,让我们对Nacos的理解深入了很多,也发现了我们架构和运维中的很多问题,收获很大。
今天来完整记录这次故障的发生、排查、处理过程,以及复盘后的经验教训和改进措施。这不是一篇Nacos的教程,而是一次真实的故障复盘,希望能给用Nacos或者类似配置中心的朋友一些参考和警示,也希望我们踩过的坑,你们不要再踩。
一、背景:我们的Nacos架构
先简单介绍一下我们的Nacos架构,方便大家理解故障的原因。
我们团队从去年开始全面微服务化,服务发现和配置管理用的是Nacos,这是阿里巴巴开源的一个服务发现和配置管理工具,功能比较全面,既可以做服务注册发现,也可以做配置中心,而且和Spring Cloud、Dubbo都有很好的集成,用起来比较方便。
我们的Nacos集群是三个节点,部署在三台独立的物理机上,用的是Nacos自带的集群模式,数据存储用的是内嵌的Derby数据库(这是后来故障的根源之一,后面会详细说)。前端挂了一个Nginx做负载均衡,所有的服务都通过Nginx的VIP来访问Nacos集群。
配置管理方面,我们所有的微服务的配置都放在Nacos配置中心里,包括数据库连接、Redis连接、各种业务参数、开关配置等等,服务启动的时候从Nacos拉取配置,运行过程中如果配置有变更,Nacos会主动推送给服务,服务动态刷新配置,不需要重启。
这套架构用了大半年,一直比较稳定,没出过什么大问题,我们也就慢慢放松了警惕,觉得Nacos很稳定,不会出什么问题。直到上周的这次故障,才给我们敲响了警钟。
二、故障发生:配置突然不更新了
故障发生在上周二的下午,大概三点多钟。
最先发现问题的是一个业务开发同学,他说他改了一个服务的配置,在Nacos控制台发布了之后,服务那边没有收到配置变更的通知,配置没有刷新。他以为是自己操作有问题,又重新发布了一次,还是没有反应。然后他重启了服务,结果服务启动失败了,报错说读取不到配置。
这个同学一开始以为是自己服务的问题,查了半天没找到原因,就来找我帮忙看。我一开始也没太在意,觉得可能是网络波动或者服务的问题,让他先看看服务的日志,有没有连接Nacos的报错。
结果一看日志,发现服务连接Nacos的时候超时了,而且是间歇性的,有时候能连上,有时候连不上。我这才意识到可能是Nacos那边出了问题,赶紧登录Nacos控制台看,发现控制台也很卡,打开一个配置详情要等好几秒,有时候甚至直接报错504。
这时候,又有好几个业务同学反馈,说他们的服务配置也不更新了,有的服务重启之后启动失败,读取不到配置。我意识到问题严重了,Nacos集群出问题了,而且影响范围很大。
我赶紧在团队群里发了通知,说Nacos配置中心出了故障,大家暂时不要发布配置,不要重启服务,避免更多的服务启动失败。然后拉了几个人,开始紧急排查。
那时候是下午三点半,我们还不知道问题出在哪里,也不知道多久能恢复,业务方一直在问什么时候能好,那种压力,真的很大。
三、排查过程:一步步找到根源
排查的过程很曲折,我们走了不少弯路,现在回想起来,很多地方如果一开始就想到,能节省很多时间。但故障排查就是这样,在信息不充分、压力很大的情况下,很难一开始就找到正确的方向,都是一步步试错,一步步排除,最后才能找到根源。
第一步:检查网络和服务器状态
我们首先排查的是网络和服务器状态,因为最常见的故障原因就是网络问题或者服务器负载过高。
我们登录了三台Nacos的服务器,看了一下CPU、内存、磁盘IO、网络带宽,发现三台机器的CPU都不高,内存也够用,网络带宽也正常,没有明显的异常。但磁盘IO有点高,尤其是其中一台机器,磁盘IO util一直在80%以上,有时候甚至到100%。
我们一开始以为是磁盘IO高导致的Nacos响应慢,但不确定为什么磁盘IO会这么高。我们看了一下那台机器上的进程,Nacos进程的磁盘读写确实很高,但不知道在读写什么。
这时候,我们犯了一个错误,就是没有继续深入查磁盘IO高的原因,而是转而去查Nacos的日志和集群状态了,导致后面走了不少弯路。
第二步:检查Nacos集群状态和日志
接下来我们查了Nacos的集群状态,看了一下控制台的集群节点列表,发现三个节点都是在线的,状态看起来正常,没有节点掉线。但节点之间的通信延迟有点高,尤其是磁盘IO高的那台节点,和其他节点之间的通信延迟经常超过1秒,有时候甚至超时。
我们又看了Nacos的日志,发现了大量的报错,主要是这几类:
- 数据库连接超时,获取连接失败
- 集群节点之间通信超时,数据同步失败
- 配置推送失败,客户端连接超时
- Derby数据库的锁等待超时,事务回滚
看到Derby数据库的报错,我们才意识到,可能是数据存储层出了问题。我们用的是Nacos内嵌的Derby数据库,这是一个Java写的嵌入式数据库,性能和稳定性都不如MySQL,之前就听说过Derby在高并发下可能会有问题,但我们一直没当回事,觉得我们的并发量不高,应该没问题。
我们赶紧去看Derby数据库的文件,发现Derby的数据文件已经很大了,而且有很多的事务日志文件。我们又查了一下Nacos的配置表,发现里面有很多历史版本的配置,因为Nacos会保留配置的历史版本,方便回滚,我们从来没有清理过,日积月累,数据量已经很大了。
这时候,我们初步判断,故障的原因可能是Derby数据库的数据量太大,加上高并发的配置读写,导致数据库性能下降,响应变慢,进而导致整个Nacos集群响应慢,配置推送失败,服务读取配置超时。
但这只是初步判断,我们还需要验证,而且即使判断正确,怎么解决也是一个问题,因为不能随便重启Nacos,重启可能会导致数据丢失,而且重启期间所有服务都无法读取配置,影响更大。
第三步:临时缓解,先恢复服务
在找到根本原因之前,我们需要先想办法缓解故障,让业务先恢复。
我们做了几个临时的操作:
- 把磁盘IO高的那台节点从Nginx负载均衡里摘掉,不让客户端访问它,让它只做集群内部的数据同步,减轻它的压力。
- 重启了那台磁盘IO高的节点,让Derby数据库重新加载,释放一些资源。
- 对于启动失败的服务,我们临时把配置回滚到上一个版本,或者直接把配置写在服务的本地配置文件里,先让服务启动起来,恢复业务。
这些操作做完之后,情况有所缓解,Nacos控制台的响应速度快了一些,大部分服务都能正常读取配置了,配置变更也能推送了。但我们知道,这只是临时缓解,根本问题还没有解决,Derby数据库的问题还在,随时可能再次出问题。
这时候已经是下午五点多了,故障持续了将近两个小时,业务基本恢复了,但我们还不能松气,需要继续排查根本原因,制定长期的解决方案。
第四步:深入分析,找到根本原因
业务恢复之后,我们继续深入分析,终于找到了故障的根本原因。
根本原因有三个,叠加在一起,导致了这次故障:
1. Derby数据库性能瓶颈
这是最核心的原因。Nacos内嵌的Derby数据库,是一个嵌入式的Java数据库,设计初衷是用于开发测试和小规模场景,不适合大规模的生产环境。Derby的并发处理能力有限,当配置读写并发较高的时候,很容易出现锁等待、事务超时、性能下降的问题。
我们的Nacos集群有几十个服务,每个服务有多个实例,都在频繁地从Nacos拉取配置和监听配置变更,加上我们经常发布配置,读写并发其实不低。Derby在这种压力下,性能逐渐下降,响应越来越慢,最终导致了超时和故障。
而且,Derby是嵌入式数据库,和Nacos运行在同一个JVM里,数据库的性能问题会直接影响Nacos的性能,两者互相影响,形成恶性循环。
2. 配置历史版本数据过多,没有清理
Nacos配置中心有一个功能,就是保留配置的历史版本,每次发布配置都会生成一个历史版本,方便回滚。这个功能本身很好,但历史版本是需要存储的,会占用数据库空间。
我们从上线Nacos到现在,大半年的时间,从来没有清理过配置历史版本,几十个服务,每个服务有多个配置文件,每个配置文件可能发布了几十次甚至上百次,日积月累,历史版本的数据量已经非常大了,Derby数据库里的配置历史表已经有几十万条记录了。
数据量大了之后,查询和写入的性能都会下降,尤其是Derby这种嵌入式数据库,对大数据量的处理能力更弱,这也是导致数据库性能下降的一个重要原因。
3. 监控和告警缺失,没有提前发现问题
第三个原因,也是我们反思最深的,就是监控和告警缺失。我们的Nacos集群,除了最基本的进程监控,几乎没有什么业务层面的监控,数据库的性能、集群节点的状态、配置推送的成功率、服务连接的成功率,这些关键指标我们都没有监控,更没有告警。
其实,在故障发生之前,Derby数据库的性能就已经在下降了,Nacos的响应时间已经在变长了,如果我们有监控,就能提前发现这些异常,提前处理,就不会演变成这么严重的故障。但因为没有监控,我们完全没有察觉,直到故障爆发了才知道,错过了最佳的处理时机。
而且,故障发生之后,因为没有监控数据,我们排查问题也很困难,很多指标都要临时去查,浪费了很多时间。如果有完善的监控,我们能更快地定位问题,更快地恢复。
这三个原因叠加在一起,最终导致了这次故障。Derby的性能瓶颈是根本原因,历史版本数据过多是加剧因素,监控缺失是我们没有提前发现和快速处理的原因。
四、处理和改进措施
找到根本原因之后,我们制定了一系列的处理和改进措施,分短期、中期、长期三个阶段来实施。
短期措施(一周内完成)
- 清理配置历史版本:我们写了一个脚本,清理了Nacos配置中心里的历史版本,只保留最近20个版本,清理之后,数据库的数据量减少了80%以上,性能明显提升。
- 增加Derby数据库的监控:临时加了一些监控指标,包括数据库的连接数、响应时间、锁等待、事务超时等,设置了告警,有问题能及时发现。
- 制定Nacos运维规范:明确了配置发布的规范,避免频繁发布配置;明确了历史版本清理的周期,每月清理一次;明确了Nacos集群的运维操作规范,避免误操作。
中期措施(一个月内完成)
- 从Derby迁移到MySQL:这是最核心的改进。Nacos支持使用MySQL作为数据存储,我们计划把Nacos的数据存储从内嵌的Derby迁移到MySQL,用我们专门的数据库集群,性能和稳定性都有保障。MySQL的并发处理能力、稳定性、可维护性都比Derby强很多,能从根本上解决数据存储层的性能瓶颈。
- 完善Nacos监控体系:建立完善的Nacos监控体系,包括集群节点状态、数据库性能、配置推送成功率、服务连接成功率、配置发布频率等关键指标,接入我们的监控平台,设置合理的告警阈值,有问题第一时间告警。
- Nacos集群扩容和优化:根据业务增长情况,考虑把Nacos集群从三个节点扩容到五个节点,提升集群的可用性和性能。同时优化Nacos的JVM参数、线程池参数、连接池参数,让Nacos运行在最优状态。
长期措施(持续进行)
- 配置中心多活容灾:考虑建设多活的配置中心,跨机房部署,当一个机房出问题的时候,另一个机房能接管,提升配置中心的可用性。配置中心是微服务架构的核心组件,一旦出问题影响很大,必须要有高可用和容灾能力。
- 配置本地化和降级方案:在服务端增加配置本地化缓存,当配置中心不可用的时候,服务能使用本地缓存的配置继续运行,而不是启动失败。同时制定配置中心故障的降级预案,当配置中心出问题的时候,怎么快速降级,保障业务的基本可用。
- 定期故障演练:定期进行配置中心的故障演练,模拟各种故障场景,检验我们的监控、告警、应急处理能力,发现问题及时改进,确保真正出故障的时候能快速、正确地处理。
这些措施,短期的已经完成了,中期的正在进行中,长期的会持续推进。我们希望通过这些改进,让Nacos配置中心更加稳定可靠,不再出现类似的故障。
五、经验教训和反思
这次故障,给我们的教训很深刻,也让我们反思了很多。在这里分享几点最重要的经验教训,希望大家能引以为戒。
1. 核心组件一定要用生产级的方案
Nacos配置中心是我们微服务架构的核心组件,所有的服务都依赖它,它一旦出问题,影响的是整个系统。对于这样的核心组件,我们一开始竟然图省事,用了内嵌的Derby数据库,而不是生产级的MySQL,这是一个很大的失误。
核心组件,一定要用生产级的方案,不要用开发测试级别的方案,不要图省事,不要抱有侥幸心理。你觉得不会出问题的地方,往往就是最容易出问题的地方。核心组件的稳定性,怎么重视都不为过,因为它一旦出问题,影响是全局性的,损失是巨大的。
2. 数据一定要定期清理,不要无限积累
很多系统都有历史数据、日志数据、临时数据,这些数据如果不定期清理,会无限积累,最终导致系统性能下降,甚至出故障。我们的Nacos配置历史版本就是一个典型的例子,大半年没清理,数据量积累到几十万条,最终压垮了Derby数据库。
所以,任何系统,上线的时候就要考虑数据的生命周期,哪些数据需要长期保留,哪些数据只需要保留一段时间,定期清理,不要让数据无限积累。数据清理不是可选项,而是必须项,是系统运维的基本操作。
3. 监控和告警是系统的眼睛,没有监控就是裸奔
这次故障,我们最大的失误就是监控缺失。如果有完善的监控,我们能提前发现Derby性能下降的趋势,提前处理,就不会演变成这么严重的故障。故障发生之后,因为没有监控,我们排查问题也很困难,浪费了很多时间。
监控和告警,是系统的眼睛,没有监控的系统,就像在黑夜里裸奔,你不知道什么时候会出问题,出了问题也不知道在哪里。任何系统,上线的时候就要配套完善的监控和告警,这不是可选项,而是必须项。
而且,监控不能只监控进程是否存活,还要监控业务层面的关键指标,比如响应时间、成功率、错误率、队列长度、资源使用情况等。进程存活不代表服务正常,很多时候进程还在,但服务已经不可用了,只有业务层面的监控,才能真正反映系统的健康状态。
4. 故障应急预案很重要,要提前制定并演练
故障发生的时候,我们一开始有点慌乱,没有明确的应急预案,不知道该先做什么、后做什么,走了不少弯路。如果有提前制定好的应急预案,我们能更快地做出正确的决策,更快地恢复业务。
所以,对于核心组件和关键系统,一定要提前制定故障应急预案,明确各种故障场景下的处理步骤、责任人、沟通机制,并且定期演练,确保应急预案是有效的,相关人员都熟悉处理流程。这样,真正出故障的时候,才能有条不紊地处理,而不是手忙脚乱。
5. 故障复盘要深入,不要停留在表面
故障发生之后,复盘很重要,而且要深入复盘,不要停留在表面,不要只找直接原因,要找根本原因,要从技术、流程、规范、人员等多个层面去分析,找到所有的问题,然后制定改进措施,并且跟踪落实。
很多团队故障复盘,只是走个形式,找个表面原因,改个bug就完事了,结果类似的故障一而再再而三地发生。真正有价值的故障复盘,是要深入到根因,从根本上解决问题,并且从故障中学习,提升整个团队的技术能力和运维水平。
我们这次故障复盘,就花了整整一天的时间,从技术、流程、规范、监控、人员等多个层面深入分析,找到了所有的问题,制定了短期、中期、长期的改进措施,并且明确了责任人和时间节点,跟踪落实。我们希望,这次故障的代价,能换来系统长期的稳定,能换来团队能力的提升。
六、写在最后
这次Nacos配置中心的故障,是一次惊心动魄的经历,也是一次宝贵的学习机会。故障本身是坏事,它影响了业务,给用户带来了不好的体验,也让我们团队承受了很大的压力。但如果我们能从故障中学习,深入复盘,持续改进,那故障就变成了好事,它让我们发现了系统的问题,发现了团队的不足,推动我们去改进,去提升,让系统更稳定,让团队更成熟。
做技术,就是这样,没有永远不出故障的系统,只有不断改进、不断提升的团队。每一次故障,都是一次成长的机会,关键是你能不能从中学到东西,能不能真正改进。
最后,想对所有做技术的朋友说几句话:
第一,对核心组件要有敬畏之心,它们的稳定性怎么重视都不为过,不要图省事,不要抱侥幸心理。
第二,监控和告警是系统的眼睛,没有监控就是裸奔,任何系统上线都要配套完善的监控。
第三,数据要定期清理,不要让数据无限积累,很多性能问题都是数据量过大导致的。
第四,故障应急预案要提前制定并演练,真正出故障的时候才能有条不紊。
第五,故障复盘要深入,不要停留在表面,要找到根因,持续改进,让每一次故障都成为成长的机会。
希望我们踩过的坑,你们不要再踩;希望我们的经验,能对你们有所帮助。也希望大家的系统都能稳定运行,少出故障,多出价值。
评论(0)
暂无评论,快来抢沙发~
评论功能仅对会员开放,请先登录
登录