FUSE耗尽LXC容器内存问题解决

05/28/2017

大量网络文件协议需要利用FUSE(用户空间文件系统)挂载成本地目录,例如SSHFS、CurlFtpFS、EncFS等。而此挂载过程随着IO和网络请求的产生必然会形成缓存,其中包括FUSE协议本身使用的缓存以及内核缓存。在物理机和硬件虚拟化(Hypervisor)环境下,这些缓存会在内存中体现为cache,内存耗尽时将会自动被挤出,并不会对系统产生影响。但是,在LXC等容器环境下,受限于请求控制,这些缓存并无法被标记为cache,而是标记为正在使用的实际内存,虽然最终内存耗尽时仍会部分被挤出,但对于运维等服务器管理工作来说,会造成不必要的误会。

Kaijia的服务器利用SSHFS在LXC内挂载并顺序读取位于另一台远程服务器上的数据,每个文件均有数百兆大小。经过数周观察,Kaijia发现即使挂载时已经禁用了FUSE缓存(“cache=no”),在连续读取多个文件后,LXC内部将会显示标记为used的内存已经占满了全部内存总量(例如使用“free -m”查看时)。除非强行卸载并重新挂载SSHFS(“umount /path/to/directory/; mount -a”),该数据并不会下降。这导致的一个问题便是Zabbix经常错误地报告系统内存已经耗尽。

参阅了FUSE的文档后,Kaijia发现了一个简单解决问题的FUSE参数——direct_io,它被描述为:

This option disables the use of page cache (file content cache) in the kernel for this filesystem. This has several affects:
此参数禁止此文件系统使用内核页缓存,效果包括:

  1. Each read() or write() system call will initiate one or more read or write operations, data will not be cached in the kernel.
    每个read()和write()调用将启动一个或多个读写操作,数据将不会被缓存在内核中。
  2. The return value of the read() and write() system calls will correspond to the return values of the read and write operations.
    read()和write()调用的返回值将对应读写操作的返回值。

简单的说,direct_io既是使FUSE读取的内容不写入缓存,直接以先进先出形式传输至IO,从而避免了缓存被视为实际使用中内存的问题,因此,只需要挂载文件是加入direct_io参数,例如:

即可避免FUSE在容器中的内存问题。