虚拟机 IO 慢速导致挂载超时进入 emergency mode 问题排查

起因

最近遇到一个问题, 在虚拟机部署的集群上,再套娃一个虚拟机,导致 fstab 中挂载 xfs 设备超时,原因是 boot 分区被 udev 扫描时 IO 太慢导致超时,所以每次开机都会进入 emergency mode, 需要按下 Ctrl + D 才能继续。

首先看一下 fstab 的配置:

1
2
UUID=/dev/mapper/centos-root /                       xfs     defaults        0 0
UUID=9a8652ca-8fa7-4c8a-9345-ea3ed07beaa8 /boot xfs defaults 0 0

报错信息:

1
2
3
4
5
6
7
$ journalctl -u boot.mount

-- Reboot --
Aug 15 16:33:50 localhost.localdomain systemd[1]: Dependency failed for /boot.
Aug 15 16:33:50 localhost.localdomain systemd[1]: Job boot.mount/start failed with result 'dependency'.
Aug 15 16:34:41 localhost.localdomain systemd[1]: Mounting /boot...
Aug 15 16:35:13 localhost.localdomain systemd[1]: Mounted /boot.

尝试的解决方案

fstab 添加 x-systemd.mount-timeout

最开始以为是挂载超时的问题,挂载默认的超时时间是 90 秒,如果比这个时间多,就会挂载失败进入 emergency mode, 所以尝试通过添加该参数:

x-systemd.mount-timeout 是后来才被支持的,需要先看一下系统的 systemd 是否支持该参数:

https://github.com/eworm-de/systemd/commit/9a94ff15c46a3d0c966b8972dbd525cbbdac8b03

查看的方式很简单,通过 man systemd.mount 查看是否有该参数,没有的话就是不支持。

不支持的话可以通过第二种方式设定(假设当前挂载盘为 boot):

1
2
3
4
5
mkdir -p /etc/systemd/system/boot.mount.d
cat <<EOF > /etc/systemd/system/boot.mount.d/timeout.conf
[Mount]
TimeoutSec=600
EOF

设定可接受的超市时间为 10 分钟后,reboot 后发现还是一样的问题,我们也可以从上面的日志看到并不是 mount 过程发生了超时,而是 Dependency failed for /boot,这里的 dependency 到底指什么呢?

首先尝试将 udevadm 的 log level 调整为 info:

udevadm control --log-priority=info 只会临时作用于当前正在运行的 udevd, 如果要永久更改需要修改 /etc/udev/udev.conf 文件。

然后重启后 journalctl -xb 查看系统启动日志,可以发现 udev 是在 kernel 启动后才加载的,因为 udev 是运行在用户态的,但是在这里好像没有发现关于 udev 的什么异常信息,倒是看到了 lvm 的 warning:

1
2
3
Aug 15 16:32:47 localhost lvm: WARNING: Device /dev/vda1 not initialized in udev database even after waiting 10000000 microseconds.
Aug 15 16:33:29 localhost lvm: WARNING: Device /dev/vda1 not initialized in udev database even after waiting 10000000 microseconds.
Aug 15 16:34:23 localhost lvm: WARNING: Device /dev/vda1 not initialized in udev database even after waiting 10000000 microseconds.

到这里就没下文了,暂时不知道超时的点到底发生在哪里,所以只能通过其他方式绕开了。

fstab 添加 nofail,x-systemd.device-timeout=1ms

首先看一下 fstab - ArchWiki (archlinux.org) 中的描述:

External devices that are to be mounted when present but ignored if absent may require the nofail option. This prevents errors being reported at boot. The nofail option is best combined with the x-systemd.device-timeout option. This is because the default device timeout is 90 seconds, so a disconnected external device with only nofail will make your boot take 90 seconds longer, unless you reconfigure the timeout as shown. Make sure not to set the timeout to 0, as this translates to infinite timeout.

nofail 可以让系统启动时无视挂载异常,x-systemd.device-timeout 参数会决定等待的时间,默认为 90 秒,也就是说当挂载异常时整个挂载过程仍会持续 90 秒,所以当配合该参数 1ms 使用后,相当于挂载操作变为异步,当该参数为 0 时,表示挂载操作为阻塞,直到成功。

当修改为这个参数后,reboot 后不再进入 emergency mode, 但系统启动后的一段时间内,df -h 是看不到想挂载的分区 /boot 的,要等待一段时间才会出现,查看相关日志:

1
2
3
4
5
6
7
8
9
10
11
$ journalctl -u boot.mount

-- Reboot --
Aug 15 17:29:43 localhost.localdomain systemd[1]: Mounting /boot...
Aug 15 17:29:53 localhost.localdomain systemd[1]: Mounted /boot.

$ cat /var/log/messages | grep udev

Aug 15 17:27:50 localhost lvm: WARNING: Device /dev/vda1 not initialized in udev database even after waiting 10000000 microseconds.
Aug 15 17:28:31 localhost lvm: WARNING: Device /dev/vda1 not initialized in udev database even after waiting 10000000 microseconds.
Aug 15 17:29:31 localhost container-storage-setup: WARNING: Device /dev/vda1 not initialized in udev database even after waiting 10000000 microseconds.

比普通情况下少了 dependency failed 的相关日志。

fstab 添加 _netdev

该参数出现在 “Tips and tricks” 一节中,先看一下描述:

The same applies to remote filesystem mounts. If you want them to be mounted only upon access, you will need to use the noauto,x-systemd.automount parameters. In addition, you can use the x-systemd.mount-timeout= option to specify how long systemd should wait for the mount command to finish. Also, the _netdev option ensures systemd understands that the mount is network dependent and order it after the network is online.

该参数告诉 systemd 这是一个网络设备,当系统网络可用时才会被挂载,那么具体行为是什么样的呢?

Systemd: When to use _netdev mount option? | Codingberg

从上面的博客中了解到,一般是 nfs 会用到,但其实不用明确指定这个参数,因为 systemd 会自动检测文件系统类型是否需要网络。

添加了该参数以后,行为和添加 nofail,x-systemd.device-timeout=1ms 一致。

尝试 nofail, x-systemd.device-timeout=0

fstab 文档中提到不要把这个参数设为 0,否则会永远等待下去,但我期望的就是忽视错误,并且在成功挂载后再启动系统,如果无法启动,则代表该盘不可用,这是我期望的目标,所以就想看一下设置为 0 的效果是什么样的:

相比于 x-systemd.device-timeout=1ms,系统启动后能马上看到 /boot 分区,符合预期。

总结

由于嵌套虚拟机的 IO 较慢,在 mount 较大的虚拟卷时 IO 时间过长导致开机进入了 Emergency Mode, 通过修改 fstab 的挂载行为 nofail,x-systemd.device-timeout=0 解决了该问题。

虽然得到了预期的结果,但仍然没找到根源超时发生在什么地方,这个 “dependency” 到底是什么呢?上班后问问 mentor 吧。

学习资料