特殊事件處理篇

高速系統 (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 作為主事者,那麼整個掛載就得要重來... 在系統的運作上面,就會產生許多困擾!雖然效能上面會比較好一些。

伺服器篇文件
各版本彙整說明
CentOS 6.x