草庐IT

一次线上存储节点进程假死的分析

存储之厨 2023-03-28 原文

背景

某大厂分布式存储程序猿大半夜收到电话报警,某个存储节点进程和主控节点心跳丢了。 穿着小内裤起来 检查了一番:

  • ping 主控节点:网络正常
  • telnet 主控节点端口: 能telnet 上
  • ps -aux 检查存储节点进程: 进程还在
  • 检查 日志: 日志居然不前进了!!!! 又看了一遍 存储节点主进程的状态:居然不是正常状态!!! 根据线上故障先止损后追因的原则,马上发命令check 无误后把这个节点从存储集群中下掉了。

分析过程

蛛丝马迹

看到业务恢复、故障报警消除后,开始分析root cause。 进程处于func状态, gdb attach 上去失败, gcore 也失败,根本就无法看到堆栈。 继续检查业务日志:发现故障在磁盘包障后的日志后出现。 检查内核日志:发现有块nvme 盘掉线了,和pcie root 断开了!!!

和厂商及系统部同学确认了下,出现NVME盘掉线的情况,短时不影响buffer IO ,只影响direct IO。

日志迷云

这就奇怪了,因为我们的数据流都是走page cache,而且落盘的时候用pthread, 即便磁盘坏掉了,也只应该影响这个pthread, 不应该导致整个存储进程上所有其他的线程,持续周期任务日志都打印不出来的情况。

到底是个什么鬼?

好事多磨

现有的日志和现场已经无法提供更多线索了,为了分析这个问题,只有想办法复现这个现象了。

怎么复现了?既然是坏盘影响IO,那么我就模拟坏盘吧。 搜索了一卷: 参考 磁盘故障模拟, 发现验证机器上没有相关的工具,也无法安装包,之好作罢。怎么办?

喝了一杯茶回来,想到既然是坏盘影响IO, 那么就把有IO的地方mock住!

说干就干,把raft 中和业务中数据流上pwrite/pread的地方都替换成无限sleep, 加个开关,启动后验证了一遍,一切正常,没有复现哪个问题。

后来仔细分析了下,不能一直sleep后,应该用while(1)啊,因为碰到磁盘故障,DIO执行到这里也不会返回,并且不会切换线程。 于是把上面的 sleep换成while。

重新编译执行,打开开关测试 ,还是一切正常啊!

直捣长龙 (pwrite --> sync)

又分析了一遍,既然NVME盘掉线,直接影响的是DIO,那么mock的时候:所有相关的这类操作都应该换成while(1)无法返回,DIO的操作有哪些?

write(O_DIRECT); sync grep 了一遍,没有O_DIRECT, 只有sync有几个,也换成了while(1), 重新启动,先正常跑了一通,然后把替换sync为 while(1)的开关打开,没过几分钟,就出现了和线上一模一样的假死现象。

根因

仔细分析了下我们的业务,原来在每次元数据更新完都会sync下元数据文件。这个操作在bthread里,为了避免同时操作这个文件,前面有锁。而这个操作又是在一个bthread里, 它做这个又会去拿磁盘基本的锁,这个锁又和很多业务线程互斥!!!

这样一旦这个sync huang住无法返回,就会导致存储进程假死!

总结

通过上面的分享可以看到,分析和模拟磁盘故障,需要知道它的本质影响然后想办法模拟手影响的地方,这样才能彻底定位根因!

有关一次线上存储节点进程假死的分析的更多相关文章

  1. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  2. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

  3. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

    在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

  4. ruby - 在 jRuby 中使用 'fork' 生成进程的替代方案? - 2

    在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',

  5. ruby - 通过 ruby​​ 进程共享变量 - 2

    我正在编写一个gem,我必须在其中fork两个启动两个webrick服务器的进程。我想通过基类的类方法启动这个服务器,因为应该只有这两个服务器在运行,而不是多个。在运行时,我想调用这两个服务器上的一些方法来更改变量。我的问题是,我无法通过基类的类方法访问fork的实例变量。此外,我不能在我的基类中使用线程,因为在幕后我正在使用另一个不是线程安全的库。所以我必须将每个服务器派生到它自己的进程。我用类变量试过了,比如@@server。但是当我试图通过基类访问这个变量时,它是nil。我读到在Ruby中不可能在分支之间共享类变量,对吗?那么,还有其他解决办法吗?我考虑过使用单例,但我不确定这是

  6. ruby - Rack:如何将 URL 存储为变量? - 2

    我正在编写一个简单的静态Rack应用程序。查看下面的config.ru代码:useRack::Static,:urls=>["/elements","/img","/pages","/users","/css","/js"],:root=>"archive"map'/'dorunProc.new{|env|[200,{'Content-Type'=>'text/html','Cache-Control'=>'public,max-age=6400'},File.open('archive/splash.html',File::RDONLY)]}endmap'/pages/search.

  7. ruby-on-rails - Rake 任务仅调用一次时执行两次 - 2

    我写了一个非常简单的rake任务来尝试找到这个问题的根源。namespace:foodotaskbar::environmentdoputs'RUNNING'endend当在控制台中执行rakefoo:bar时,输出为:RUNNINGRUNNING当我执行任何rake任务时会发生这种情况。有没有人遇到过这样的事情?编辑上面的rake任务就是写在那个.rake文件中的所有内容。这是当前正在使用的Rakefile。requireFile.expand_path('../config/application',__FILE__)OurApp::Application.load_tasks这里

  8. ruby-on-rails - 为什么在 Rails 5.1.1 中删除了 session 存储初始化程序 - 2

    我去了这个website查看Rails5.0.0和Rails5.1.1之间的区别为什么5.1.1不再包含:config/initializers/session_store.rb?谢谢 最佳答案 这是删除它的提交:Setupdefaultsessionstoreinternally,nolongerthroughanapplicationinitializer总而言之,新应用没有该初始化器,session存储默认设置为cookie存储。即与在该初始值设定项的生成版本中指定的值相同。 关于

  9. ruby - 无法在 Ruby 中将 ffmpeg 作为子进程运行 - 2

    我正在尝试使用以下代码通过将ffmpeg实用程序作为子进程运行并获取其输出并解析它来确定视频分辨率:IO.popen'ffmpeg-i'+path_to_filedo|ffmpegIO|#myparsegoeshereend...但是ffmpeg输出仍然连接到标准输出并且ffmepgIO.readlines是空的。ffmpeg实用程序是否需要一些特殊处理?或者还有其他方法可以获得ffmpeg输出吗?我在WinXP和FedoraLinux下测试了这段代码-结果是一样的。 最佳答案 要跟进mouviciel的评论,您需要使用类似pope

  10. Ruby 守护进程导致 ActiveRecord 记录器 IOError - 2

    我目前正在用Ruby编写一个项目,它使用ActiveRecordgem进行数据库交互,我正在尝试使用ActiveRecord::Base.logger记录所有数据库事件具有以下代码的属性ActiveRecord::Base.logger=Logger.new(File.open('logs/database.log','a'))这适用于迁移等(出于某种原因似乎需要启用日志记录,因为它在禁用时会出现NilClass错误)但是当我尝试运行包含调用ActiveRecord对象的线程守护程序的项目时脚本失败并出现以下错误/System/Library/Frameworks/Ruby.frame

随机推荐