overlayfs挂载选项volatile
引入
最近在公司遇到了容器相关的线上问题, 简单来说就是K8s会出现PLEG not healthy的报错. 经过初步的排查, 发现是docker有个goroutine卡在了umount
, 同时这个这个groutine会占用某个锁, 导致查询状态的handler里拿不到锁, 最终导致k8s层面的报错.
通过对umount事件的监控, 以及相关源码代码的阅读, 发现是overlayfs在umount的时候, 会对upper层所在的fs进行一次sync, 导致大量脏页回写. 如果这个机器内存较大, 并且有过频繁的IO, 那么就会脏页较多, overlayfs umount时等待磁盘IO完成而阻塞过久.
umount的stack trace如下:
1 | [<0>] wb_wait_for_completion+0x5a/0x90 |
重点在这个ovl_sync_fs
函数, 它会对整个overlayfs的uppser层所在文件系统进行sync操作:
1 | // from kernel 5.10 |
很显然, 对于k8s的很多场景来说, sync文件系统是多余的操作. k8s每次实例启动都重新挂载rootfs, 实例退出后将rootfs umount并删除(可能描述的不对). 所以最好有办法能够避免overlayfs umount时候的强制刷盘.
解决方案
如果仔细查看ovl_sync_fs
函数, 可以发现它会在函数开头执行两个判断, 一次是判断upper层是否存在, 一次是ovl_should_sync
判断overlayfs是否应该进行sync. 解决问题的关键可能就在于ovl_should_sync能否绕过sync.
kernel
ovl_should_sync函数被包含在2020.8.31提的patch里
[PATCH v5] overlayfs: Provide a mount option “ volatile” to skip sync - Vivek Goyal
这个patch给overlayfs提供了一个新的挂载选项volatile, 当挂载这个选项后, overlayfs会去掉一些sync操作, 包括针对单独文件的sync以及整个文件系统的sync.
影响的地方如下:
umount不会再强制对upper层文件系统执行sync, 也就是针对本次问题出现的场景.
remount的时候, 也可能对upper层所在文件系统进行sync, 这是2020年加内核主线的patch.
[PATCH] ovl: sync dirty data when remounting to ro mode ‒ Union Filesystem
这是因为把overlayfs remount成只读之后, 在umount overlayfs时,kill_anon_super
->generic_shutdown_super
->sync_filesystem
检查overlayfs为只读时会跳过sync_filesystem. 所以把overlayfs从可写remount成只读的时候, 直接进行一次sync_filesystem, 避免最终umount的时候遗漏sync_filesystem操作. volatile选项会取消这一次sync_filesystem操作.针对单个文件fsync调用, 如果带有volatile挂载选项, 会跳过.
当文件copy up到upper层的时候, 也会进行vfs_fsync()操作. 如果带有volatile选项, 会跳过.
O_SYNC的场景, 如果有volatile选项, 也会绕过sync退化成overlayfs默认的写行为.
本质上这些sync操作都是为了避免系统crash造成overlayfs磁盘数据丢失. volatile挂载选项和Kubernetes的使用场景十分契合. 如果内核在向overlayfs写入数据时崩溃, kubelet总是会重新创建新的容器, 而不会复用之前的rootfs. 因此,在 kubernetes中, 容器的rootfs是临时的. 在pod中使用 volatile 选项是安全的, 因为我们没有机会重复使用旧的rootfs. 在有状态容器中使用这种配置也是安全的, 因为需要持久化的数据理应写入外部卷, 在运行时不会受到volatile标志的影响.
contianerd
当然, 新的挂载选项需要runtime的支持, 才能够在挂载rootfs带上这个选项.
在contaienrd社区已经有了许多相关讨论:
- https://github.com/containerd/containerd/issues/8698
- https://github.com/containerd/containerd/issues/6406
- https://github.com/containerd/containerd/pull/4785
- https://github.com/containerd/containerd/pull/8402
- https://github.com/containerd/containerd/pull/8676
这个pr [overlay] add configurable mount options to overlay snapshotter by dmcgowan · Pull Request #8676 允许对overlayfs的挂载选项进行设置, 并且被backport到了1.6.24.
requirements
总结一下, overlayfs volatile特性, 需要的版本如下:
- Linux kernel >= 5.10
对应patch: [PATCH] ovl: sync dirty data when remounting to ro mode ‒ Union Filesystem - containerd >= 1.6.24 or containerd >= 1.7.4
对应PR: [overlay] add configurable mount options to overlay snapshotter by dmcgowan · Pull Request #8676
containerd配置:
1 | # /etc/containerd/config.toml |
reference
- https://lore.kernel.org/all/20200722175024.GA608248@redhat.com/
- https://github.com/containerd/containerd/pull/8676
- https://fuweid.com/post/2023-08-sync-containerd-issue/
- https://www.redhat.com/sysadmin/container-volatile-overlay-mounts
- https://docs.kernel.org/filesystems/overlayfs.html#volatile-mount