XFS:kmem_alloc中可能的内存分配死锁

今天一台生产环境在使用的业务虚机忽然故障了,现象是系统中的所有应用程序都不能正常的写磁盘,但是诡异的是系统的磁盘I/O是的跑满了的。
根据系统日志中的一些错误信息,我查找到了下面这篇由一位德国的云平台工程师撰写的技术分析文章。经过对照错误信息后,发现是一致的问题,我们参考这篇文章并有效解决了设备的故障事件,恢复了业务服务。

有感于这位德国的工程师在研究问题时的一种敬业和执着,不断深入挖掘的精神,也很佩服他分析问题的方法和思路,故花了一点时间将其翻译为中文,希望更多人受益。这里所谈的受益完全不仅仅是怎样解决类似的技术故障,更多地是向Daniel Schneller学习研究问题时的那种锲而不舍,打破砂锅问到底的精神,我觉得作为一个技术人,非这样做才能有真进步。

也许您想看原文:原文链接

以下为译文。

几周前,我们对几个虚拟机上看似随机的I/O挂起感到惊讶。任何试图写入其数据卷的尝试都会被阻止,使平均负载上升到满载状态,并且(稍微更不可避免地)会使Elasticsearch或MongoDB崩溃。
查看hypervisor的日志,我注意到dmesg和syslog中有很多这样的消息:

...
Mar  5 22:42:57 node06 kernel: XFS: possible memory allocation deadlock in kmem_alloc (mode:0x250)
Mar  5 22:42:59 node06 kernel: XFS: possible memory allocation deadlock in kmem_alloc (mode:0x250)
...

这篇文章描述了问题的分析、原因和解决方法。

需要立即做的第一件事情:一个快速的修复

最高优先级是让VMs中的数据库再次运行。Google提出了在hypervisor上删除页面缓存的建议,因为不知何故,这个问题似乎与内存碎片有关,但至少可以通过这种方式暂时解决:

% echo 1 > /proc/sys/vm/drop_caches

实际上,这样做立即使VMs再次响应。在一个例子中,Elasticsearch需要在VM内部重新启动,但是集群很快就恢复了。但是在接下来的几天里,这个问题不断重复出现,频率越来越高,并且发生在多个虚拟化的宿主机上。

更新内核版本以得到修复?

我注意到我们运行的内核在XFS中有一个已知的问题,实际上它比某些操作所需要的内存更浪费(请参阅这篇2013年XFS邮件列表文章)。根据这篇文章描述,这个问题在后来的Ubuntu内核中得到了修复,所以我们在hypervisor上安装了最新的一个。有几天,它似乎解决了这个问题,因为我们没有看到挂起再次。不幸的是,它还是回来了,只是晚了一点。

临时措施:使用xfs-guard

在做更多的研究时,我总是遇到内存碎片的问题,不明白为什么这会突然成为一个问题,因为这些机器在VMs内部和hypervisor上都以不变的配置运行了几个月。(这毕竟是有道理的,但在这一点上,我还没有理解底层的机制。)事实证明,删除页面缓存并不是真正必要的,毕竟删除slab缓存(echo 2>/proc/sys/vm/drop_caches)就足够让VMs响应了。我认为删除页面缓存的副作用是释放足够的(不相关的)内存,让更多的slab分配成功了。
知道这是有效的,为了给我争取一些时间,我很快创建了一个小的“xfs-guard”守护程序,并将其安装到hypervisor上。基本上,它所做的只是不断跟踪系统日志,查找错误消息并反复删除slab缓存,直到消息停止重复。你可以在Github和Ansible Galaxy上找到它。
一旦部署了它(它在推出几个小时后就第一次被启动了),我就有更多的时间来找出根本原因。

继续深入挖掘

进一步阅读材料,所有东西都指向文件系统碎片,进而指向内核内部的(slab)内存碎片。我觉得有点让人想起90年代,当时运行Norton Speed Disk之类的东西来整理FAT文件系统是一项相当常见的任务。我不太愿意相信这在近30年后仍然是一个问题,特别是因为XFS在控制碎片方面有着良好的声誉。

记得这个吗:
    诺顿磁盘医生和诺顿磁盘加速不是一回事pic.twitter.com/TSNgCpxobN
- Anatoly Shashkin ? (@dosnostalgic) 2016年8月8日

例如,XFS将推测性地在磁盘上预分配比实际请求的空间更多的空间,假设随后不久将有更多相关数据。这样,它就可以把数据放在第一个块的旁边,而不必把它放在磁盘的其他地方。这一点加上所讨论的文件系统只包含极少数、非常大的镜像文件(作为块设备提供给vm),使得碎片理论看起来更不可能。我需要做更多的数据分析才行。

文件系统必须跟踪磁盘的已分配部分和空闲部分。高度的磁盘碎片化会导致大量小块占用和可用空间需要跟踪。由于文件系统是内核的一部分,所以必需的元数据结构会保存在内核内存中,并由slab分配器管理。即使是使用最有效的数据结构,在某种程度上也不可能为不断增加的磁盘空间片段容纳下全部的元数据。当这种情况发生时,slab内存分配失败,文件系统就会完全阻塞写操作。

幸运的是,XFS提供了一些强大的工具来分析和诊断它在引擎盖下做什么。我的第一步是运行XFS调试器“xfs_db”,并告诉它报告一些关于整体碎片(frag-f)的高级别信息。请注意,本文中提到的所有命令都可以在文件系统挂载并正常运行的情况下运行,这些命令非常简洁。但是,它们将创建额外的磁盘I/O,并至少暂时消耗一些内存。

% xfs_db -r -c "frag -f" /dev/md0p1
actual 42561387, ideal 1089, fragmentation factor 100.00%

大约30秒后,它返回了上述输出。100%看起来相当可怕,但根据XFS文档,“碎片因子”可能会产生误导,因为它往往会很快接近100%,而不一定表明存在严重问题。在这个XFS FAQ条目中有一个很好的图表来解释细节。输出显示了“扩展数据块”(属于文件的数据的单个连续范围)的数量;实际数量和XFS认为理想的数量。即使仅仅看到碎片因子还不够严格,但拥有超过4000万条数据似乎相当庞大,尤其是与更小的理想值相比。
所以很明显出了问题,但还需要更多的信息。

接下来,我想弄清楚卷上的可用空间有多零碎,假设这可能会导致写入新数据的工作停滞,如果无法使其适应可用区域的话。

分配组和可用空间

我了解到XFS会将一个大的卷拆分为“Allocation Groups”,这些组可以被认为是它们自己(主要)独立的“子文件系统”。分配组的数量取决于XFS总容量的大小。它们的目的是允许并行文件操作。这个Novell知识库页面有一个很好的摘要和一些有用的脚本来收集关于每个分配组的可用空间及其碎片的信息。XFS文档则提供了更多的技术细节。

我在所有hypervisor的XFS文件系统上运行了一个循环,没有发现任何特别有用的东西。在所有分配组中都有大量的自由和连续空间。为了完整性,我在VMs中运行了相同的测试,也没有显示出明显的可用空间碎片。

文件碎片

接下来是分析磁盘镜像文件本身的碎片。由于它们是以固定的大小创建的,一次性分配了所需全部空间,并且这是在创建底层XFS文件系统之后不久就已完成的。我认为这里也不会有太多的碎片。但结果令人非常得意外…

此命令用于查找XFS上为特定文件分配的各个扩展数据块及其大小:

% xfs_bmap -v ffc63a70.disk > ffc63a70-bmap-before-defrag.txt

它花了几分钟,尽管运行在一个相当快的SSD磁盘阵列上。当它运行时,它的驻留内存使用率膨胀到了3gb左右。原来,将输出重定向到文件是个好主意!

% wc -l ffc63a70-bmap-before-defrag.txt
34694400 ffc63a70-bmap-before-defrag.txt

仅此单个磁盘映像就有近3500万个扩展数据块。仅.txt文件就大于3 GB!所有服务器上的其他VM映像都出现了相同的总体情况。每个虚拟机监控程序总共有大约5000万个扩展数据块,只包含少数几个大文件。这是其中一个文本文件的前几行:

EXT: FILE-OFFSET     BLOCK-RANGE            AG AG-OFFSET       TOTAL
  0: [0..255]:       2339629568..2339629823 24 (512..767)        256
  1: [256..359]:     2339629208..2339629311 24 (152..255)        104
  2: [360..5135]:    2339715400..2339720175 24 (86344..91119)   4776
  3: [5136..5447]:   2339720256..2339720567 24 (91200..91511)    312
  4: [5448..6143]:   2339721496..2339722191 24 (92440..93135)    696
  5: [6144..7495]:   2339723200..2339724551 24 (94144..95495)   1352
  6: [7496..8159]:   2339725560..2339726223 24 (96504..97167)    664
  7: [8160..9167]:   2339727232..2339728239 24 (98176..99183)   1008

显然,这种严重的碎片化为大量的微小区域是qemu如何写入这些镜像的副作用。它看起来与此2014 XFS邮件列表条目中描述的内容类似。

主动刷新或直接写入会破坏XFS的碎片预防功能。镜像文件在文件系统创建之后就被整体分配了,这一点在本例中并不重要,因为XFS显然总是使用稀疏分配。这意味着它实际上并不立即声明所有空间,而是在实际写入发生时动态声明。看看我们是否能对此做些什么,同时在机器崩溃的情况下不牺牲一致性,仍然是另一天要解决的问题。

考虑到这一点,可以理解为什么问题在创建磁盘镜像后的相当长一段时间才开始出现。在此之前,XFS一直尽职尽责地管理着每个文件中不断增长的扩展数据块数量,直到这样做所需的内存开始接近定期耗尽的临界点。因此,即使有了xfs保护,在虚拟机内部不断发生的写操作也会不可避免的出现延迟。

整理磁盘镜像碎片

XFS附带了一个文件系统重组工具“xfs_fsr”,它可以处理单个文件(也可以处理整个卷)。它尝试创建一个文件的副本(碎片较少),并在完成后用该副本替换原始文件。此操作暂时需要足够的可用空间(理想情况下大部分是连续的)来创建副本。幸运的是,如前所述,我们有大面积的无碎片可用空间。此外,当碎片重组维护时,文件不能被使用。所以在一个周末,我关闭了相关的虚拟机,一次关闭一个,使它们的磁盘镜像文件可供xsr_fsr使用。这是一个磁盘镜像的运行示例:

% time xfs_fsr -v ffc63a70.disk 
ffc63a70.disk extents before:34694398 after:81
xfs_fsr -v ffc63a70.disk   0.13s user 732.62s system 55% cpu 22:01.67 total

对于这个特定的镜像,在最初的3400万个区段中,碎片整理后只剩下81个区段。
所有其他VM镜像的碎片数量都是相似的,每次数据块的数量都会下降几个数量级。请注意,由于重新组织数据存放的工作方式,它将导致大量的磁盘I/O,因此除非您的使用模式允许的话,否则您可能希望在非工作时间运行它。

结论和下一步行动

之前内存分配死锁的现象几乎每天都会发生,但现在已经不再发生了。除了xfs guard watchdog工具之外,我们现在正在准备一个脚本,定期向监控系统报告每个磁盘镜像的扩展数据块数量。因此,即使我们无法找到防止qemu导致碎片增长的方法,我们也可以继续关注它,并在碎片再次达到关键级别之前安排碎片整理的维护任务。

有趣的是,在VMs重新启动后不久,我手动重新检查了碎片。其中一幅镜像就已经达到了40000多个区段。然而,几天后又采集了一些样本,结果显示,在经历了相当急剧的初始增长之后,这种趋势似乎逐渐减弱。不过,更需要密切关注。

作者介绍:Daniel Schneller

Daniel Schneller设计和实现复杂的软件和数据库系统已经超过15年了,他是MySQL Admin Cookbook的作者。他目前的职位是CenterDevice GmbH的首席云工程师,专注于OpenStack和基于Ceph的云技术。他曾在FroSCon、Data2Day和DWX开发者周等场合发表演讲。

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值