特殊事件处理篇

高速系统 (cluster) 平行处理运用 NFS 的问题与可能解决

集群架构当中,NFS 恐怕是一个很麻烦的瓶颈点

最近更新时间: 2021/07/12

问题发生的状况说明

鸟哥的研究当中,经常需要使用到集群架构 (cluster) 的系统来进行平行协作处理,这种情况底下,简单的系统架构, 大多是将所有的重要数据通通放置到一个 Storage 当中,再将其他的运算主机通过 NFS 连接到该系统,就简单的达到了平行处理可以操作的环境。 简易架构示意图如下:

             NFS (/home, /data)
  +------+----+-------+--------+
Node1  Node2 Node3  Node4   Node5

请注意,为了『平行处理』的机制,所有的上面系统,全部的系统架构都需要相同!最好 node1~node5 这 5 部能够是相同的硬件设备, 能够拥有相同的帐号/密码 (连 UID/GID 等等都需要相同),相同的操作系统 (连内核版本都要相同),这样运作程序时, 比较不容易出现问题。当然,还需要其他额外的平行处理函数库,那个不在这里讨论,有兴趣的自行 google 一下 mpi 与平行化, 大概就有很多文章说明了。

  • 操作问题发生

既然感觉这么简单,系统操作上面应该没啥问题吧?对啊!如果以每一部 node 自行运算,使用了自己的 mpi 程序自己的 cpu 内核计算, 那几颗 CPU 内核都没啥问题!可以安全的处理所有的程序,不会被中断。鸟哥使用的最新的系统,是 AMD 64 内核的架构, 这种架构下,最多可以用到 64 内核来跑程序,速度还不赖,很是开心。

问题是,既然有这么多部系统,如果只要跑一个案例,能不能大家一起来跑?例如上面说得,如果我有 64 内核的系统 3 部, 难道不能拿 192 个内核来跑嘛?理论上,应该是可以的!所以,鸟哥就拿来跑啦!但是...先不管跑出来的速度会不会比较快, 光是 3 台 64 内核同时跑,就出问题了!没办法顺利运作结束!老是可能随机断到某个运算日期...非常怪。

基本上,运算这种平行处理,大概有两种机制,一种是连续跑,一种是分日跑。假设我要运作的平行处理程序,需要运作 30 日仿真, 第一种方式是,30 日的仿真量,一次跑完,从第 1 日跑到第 30 日这样连续跑;第二种则是分 30 次跑,每次跑 1 日的份量。

  • 连续跑 30 日:这种情境下,所有需要用到的文件名称数据,都在初始化的时候就完成了,接下来这些文件们就一直长大而已。 好处是 NFS 的 cache 数据不容易出错,缺点是因为程序是一直跑,如果出错时,很多时刻需要从头再跑,因此,为了避免这种情况发生, 目前大多使用底下的方式来跑比较多。
  • 分日跑:每次跑 1 天,每个天都需要重新初始化,也就是说,第 1 日需要多少文件,第 2 日就需要多少文件,所以, 文件的数量会非常多,但是文件的容量则大多每日固定。缺点是每次都需要在 NFS storage 底下初始化, server/client 的 cache 问题非常大。 好处是,当某跑完结果出问题,可以从某个中断点重新开始跑,而且文件容量小,携带与处理上,都比较方便。

各有优缺点,只是站在开发者的角度,目前大多使用 1 日跑的情境来处理。那么这种情境会发生什么问题呢?为什么这种方法老是产生中断呢? 大致上我们可以这样看:

  1. node1 系统开始运算平行处理,同时提供 node2, node3 这两部作为协同处理。
  2. node1 产生初始化文件,然后发派命令给 node2, node3 ,准备开始运算
  3. node1, node2, node3 通过刚刚初始化的文件,开始读写与运算程序
  4. node1, node2, node3 运算过程中,同步写入/同步读出各种需要的数据,一直到程序结束。
  5. node1 开始第 2 日的模式初始化...

上述的问题发生在第 2 及第 5 点,当系统初始化的情况。基本上,文件系统为了加速,通常数据不会立刻写入到磁盘当中,而是暂时存放于内存。 这种机制在单一系统上面是没问题的,但是如果用于上述的状况,那就惨了:

  1. node1 初始化文件,这些文件数据先缓存到自己的内存当中,尚未写入到 NFS storage
  2. node1 发派命令给 node2, node3 ,准备协同运作
  3. node1 可以读到自己内存内的数据, node2, node3 前往 NFS storage 尝试读取,结果发现该文件不存在。
  4. 因为 node2, node3 发现文件不存在,因此回报 node1 程序终止。

看到问题的发生了嘛?这是数据访问的问题~当 node1 初始化并且产生大家都要读写的数据时,为了加速,所以尚未写入 NFS storage 当中。 结果这时却又要 node2, node3 去 NFS 读取刚刚创见的文件!夭寿啦!又要马儿跑,又要马儿不吃草,所以,这个时候就挂点了...

问题解决的方针

那么该如何处理这个问题呢?鸟哥测试过每一部 node 系统上面使用 NFS 挂载的 lookupcache=none 的参数,结果虽然有好一些, 不过,还是有可能会随机中断。为什么呢?明明 NFS 文档说, lookupcache=none 代表随时去抓 NFS storage 的 cachec 而不是使用自己的 cache 啊! 话是这么说没错,针对 node2, node3 这点是没问题的!但是针对主办单位的 node1 来说,除非你修改了 Linux kernel 的 filesystem 设置参数, 否则的话,那个 lookupcache 指的是针对 NFS 的读写而已,并不是针对自己本身系统的操作,所以, node1 在 NFS 挂载为 lookupcache=none 的情境下, 依旧会将自己产生的文件暂存于自己的内存中,并不会立刻直接写回 NFS storage 喔!

  • 第一种方法,比较简单且推荐,但是性能不彰

NFS mount 有个机制,通过 sync 这个机制来处理,那么 node1 使用到任何 NFS 的设备数据时,都会同步写入到 NFS 文件系统中, 这不像 lookupcache 喔!只是,如此一来, node1 的读写,就会非常的延迟!因为 sync 对于 NFS 的性能来说,影响非常大!但是,这种设计方式最简单! 而且几乎不会出错!

  • node1 挂载使用 sync 参数
  • node2, node3 挂载使用 lookupcache=pos 或 lookupcache=none 参数
  • 第二种方法,性能较佳,但是设计上需要非常小心,最困难在开机顺序

这样说好了,既然大家都要读写 node1 这个主事者的系统,那么将 storage 换到 node1 不就好了?好啊!但是, NFS storage 通常不会在运算主机上, 这是因为很多很多考量...这里就不写了。那怎办?没关系,我们可以将已经挂载到 node1 的 NFS 目录,再次分享出去,然后让 node2, node3 挂载 node1 的数据,这样 node2, node3 就不需要 lookupcache,而 node1 也不需要 sync 喔! 只是从 node1 分享的 NFS 目录,需要在 /etc/exports 里面加上 crossmnt 这个参数才行!否则就可能读不到挂载的信息。 这点比较需要留意而已。

过去开机都是先将 Storage 开机完毕,之后每部 node 都是独立运作可以自由开机!但是如果使用这种方式,那么就得要先从 NFS storage 开机, 完成之后才能开机 node1,之后才能 node2 与 node3 等其他系统...尤其,如果你想要从 node2 作为主事者,那么整个挂载就得要重来... 在系统的运作上面,就会产生许多困扰!虽然性能上面会比较好一些。

其他链接
环境工程模式篇
鸟园讨论区
鸟哥旧站

今日 人数统计
昨日 人数统计
本月 人数统计
上月 人数统计