花掉大笔经费建设的超算机群为什么存储性能这么差?怎么向领导交待?
存储产品那么多,听起来都很好,但谁都不说里面都什么坑,真是难选。
我只想安静地写程序做科研,却被存储设备折磨得头疼,浪费时间,怎么避开那些大坑呢?
在NFS环境上测试MPI程序,并行读写同一个文件怎么结果不对?
编程语言、操作命令很容易学会,但背后的原理不清楚。简单明了的参考资料好难找,一上来就用一堆代码讲解,看起来费时费力。
这篇文章试图解决这几个问题。着急的读者可以只看结论,然后转发了事:-)
超算应用的并行读写模式与常用存储测试用例差别很大,从而导致存储设备的演示测试性能高,而实际应用性能差。
MPI-IO的开发单位说了,决不推荐NFS存储,性能差。NFS协议为串行读写设计,多个客户端之间相互不协调,导致负载不均、延时增大、元数据开销增多等后果(能比正常带宽低80%)。从原理上看,非对称架构的存储产品更适合超算软件的MPI并行IO。
在NFS上并行读写同一个文件时,一定要用NFSv3版本的客户端,并在挂载的时候加上noac选项,否则文件数据可能不正确。
如果串行读写文件,可用NFS存储;如果并行读写文件,用NFS存储要能忍受它的低性能。Lustre等非对称文件系统并行读写性能较高。
本文力求浅显易懂,说清NFS存储并行读写性能差的背后原因。
存储的功能、性能指标很多,超级计算机看重哪些指标呢?
存储的可靠性永远第一位,不能丢数据、不能频繁故障。第二位的就是读写性能,带宽越高越好。克隆、备份、远程复制等功能只是锦上添花,不太重要。
流行的存储产品就可以认为足够稳定,读写性能则强烈地依赖于存储的接口协议。
市面上的文件系统很多,但存储接口协议不多:标准的NFS协议和私有协议。NFS协议应用广泛,计算节点上安装的NFS客户端也由专业机构开发,与操作系统的兼容性好。为了得到更好的性能,Lustre这样的文件系统选择自行设计私有协议,自行开发私有客户端;私有客户可能对操作系统的版本有一定要求,更新速度也慢,兼容性较差。
标准的并不总是好的,对超算机群来说,NFS文件系统就是噩梦,具体表现如下:
负载不均
小型NFS文件系统只有1个或2个存储服务器(或称为存储机头),不在本文讨论范围内。
超算机群的数据量很大,需要使用分布式文件系统(3个及以上存储服务器)。
NFS协议要求每台计算服务器都在挂载一个NFS服务器,见图1,一旦挂载成功,就不再改挂其它NFS服务器;通常在计算服务器开机的时候执行挂载动作,关机的时候再断开连接。
超算机群的计算服务器的数量通常远远多于NFS服务器的数量,例如5:1或10:1甚至更高,因此多台计算服务器会挂载同一台NFS服务器。为获得最好性能,计算服务器与NFS服务器之间的对应关系要保持均衡,见图1,计算服务器C0和C3都挂载NFS服务器S0,C1和C4都挂载S1。
图1
假设一个超算机群有台计算服务器,3台NFS服务器。存储产品的域名挂载功能通常能使每台NFS服务器被33或34台计算服务器挂载,负载相当均衡。
机群上运行的计算任务,绝大部分不会占用超过一半的计算服务器(否则这个机群就该扩容了)。假设一个计算任务占用8台计算服务器,这8台计算服务器是由作业调度软件分配的,有一定的随机性。最坏的情况下,这8台计算服务器挂载的都是同一台NFS服务器。那么8台计算服务器上的MPI集合IO操作都会压到1台NFS服务器上,而另外2台NFS服务器空闲,浪费了2/3的IO潜力。
这样的极端不均衡情形出现的几率也许不太大,但中等不均衡状况出现的几率相当大,例如4/2/2台计算服务器分别挂载NFS服务器C0/C1/C2。MPI-IO中的集合IO结束时会有一个进程间同步,因此,聚合IO操作的速度取决于最慢进程的速度,进而取决于负载最重的NFS服务器C0的速度。
NFS的挂载机制决定了负责不均不可避免:一旦挂载成功,客户端中途不会自动改变所挂载的NFS服务器。想中途更改的话,要么管理员手动改变NFS客户端与NFS服务器之间的挂载关系,要么编写脚本根据作业情况自动挂载。中途更改会带来3个严重问题:
1.同一台计算服务器上可能运行着两个或两个以上的作业,为了一个作业而更改挂载关系,会使其它作业中断;
2.每个NFS客户端更改挂载关系不是瞬间完成的,通常会花费几秒钟甚至1分钟,频繁更改会给存储设备带来压力;
3.超算机群上通常同时运行着很多个作业,找出一个对所有作业都均衡的挂载方案也是不一件容易的事情。
而Lustre等非对称文件系统只挂载元数据服务器,不挂载IO服务器,Lustre客户端可以直接访问任意的IO服务器,因此不存类似的负载不均问题。
数据中转耗时多
NFS客户端读写一个文件的时候只能向已挂载的NFS服务器发起请求,因为文件数据被打散分布在多个NFS服务器上,所以NFS服务器上会有一个中转操作,见图2。
图2
图2中,计算服务器C0需要向NFS服务器S0写入3个连续数据块D0~D2,而数据块D0~D2应该分别放置在NFS服务器S0~S2上。因此,S0需要在缓冲区暂存数据D0~D2,将D0写入自己的本地硬盘,通过网络将D1和D2分别发送给NFS服务器S1和S2。等D0~D2都正确地写入硬盘,S0才会向C0返回写成功的信号。
不妨将计算服务器与NFS服务器之间的数据流量称为外部流量,将NFS服务器之间的数据流量称为内部流量。显然图2中的外部流量为9(9个数据块),内部流量为6。容易推算,NFS服务器的数量为M的时候,外部流量与内部流量的比例是M:M-1。在M较大的时候,例如10,有效流量(外部流量)的比例稍稍超过50%,等价于将网络带宽上限降低于一半,严重影响文件系统性能。一个解决办法是增加一套网络交换机、每台NFS服务器增加一个网口,专走内部流量,当然这样会增加成本。
即使配备内部网络,仍然无法解决IO路径长的问题:文件数据从NFS客户端走到NFS服务器上的硬盘上,几乎都要走一个中转过程(比例(M-1)/M),必然导致延时变长。作为对比,看看Lustre的IO路径,见图3,数据块的最终目的地是哪个IO服务器,计算服务器上的Lustre客户端就将数据块发送到哪个IO服务器,不但节省了内部流量(从而省了交换机和网卡)而且减小了延时。
图3
也许有人会想,能不能将图2中内部流量在NFS服务器的内存中暂存一下啊,数据一旦写入内存就告诉NFS客户写成功了。这个思路正确,但需要将内部流量暂存在保电的内存中(例如NVDIMM或NVRAM,就是内存条上配备电容或电池),防止服务器突然掉电而丢失暂存的数据。保电内存会增加成本,价格比普通内存条贵不少。
保电内存仍然不能消除读操作的数据中转延时,见图4。仍然拿Lustre文件系统来对比,见图5,安装在计算服务器上的Lustre客户端先从元数据服务器(MDS)获得文件分布图(layout),然后就知道了每一个数据块都存放在哪个IO服务器的哪个位置,Lustre客户端直接从相应的IO服务器上读取数据块,没有中转,节省内部流量而且消除了中转延时。
图4
图5
最严重的问题还是一致性和文件锁
ROMIO官方文档[1]说,决不鼓励在NFS存储上运行MPI-IO程序,原文件如下:
ItisworthfirstmentioningthatinnowaydoweencouragetheuseofROMIOonNFSvolumes.NFSisnotahigh-performanceprotocol,norareNFSserverstypicallyverygoodathandlingthetypesofconcurrentaccessseenfromMPI-IOapplications.
必须首先强调,我们决不鼓励在NFS卷上使用ROMIO。NFS不是一个高性能的协议,NFS服务器通常也不擅长处理MPI-IO应用里的并发访问。
由于NFS协议非常流行,ROMIO必须硬着头皮支持NFS存储。但有下面3个前提要求[1]:
NFSv3客户端
在挂载的NFS目录上,文件锁函数fcntl必须能够正常工作
计算服务器挂载NFS存储的时候必须使用选项noac
其实不强制要求v3版本的NFS客户端,只要文件锁函数fcntl正常工作就行,但用NFSv3能够保证fcntl正常工作,省去了验证的麻烦。