分散式檔案系統的組織方式大致分為兩類:多數系統遵循傳統的主從式架構(client-server architecture),但也存在完全去中心化的解法。本節依序考察這兩種組織。

主從式架構#

許多分散式檔案系統採主從式架構,其中 Sun Microsystems 的**網路檔案系統(Network File System, NFS)**是 UNIX 系統上部署最廣的代表。本章以 NFS 作為伺服器式分散式檔案系統的典型範例,聚焦於廣泛使用的第三版 NFSv3 與最新的第四版 NFSv4,並比較兩者差異。

NFS 的基本理念:每台檔案伺服器對外提供其本地檔案系統的標準化視圖。無論本地檔案系統如何實作,每台 NFS 伺服器都支援同一套模型,並附帶一個通訊協定讓用戶端存取伺服器上的檔案——如此一來,跑在不同作業系統與機器上的異質行程便能共享同一個檔案系統。

遠端存取模型與上傳/下載模型#

  • 遠端存取模型(remote access model):NFS 採用的模型,又稱遠端檔案服務(remote file service)。用戶端獲得對遠端伺服器所管理檔案系統的透明存取,通常不知道檔案實際位置;用戶端只拿到一組類似本地檔案系統的檔案操作介面,操作的實作則由伺服器負責
  • 上傳/下載模型(upload/download model):用戶端先從伺服器下載整個檔案、在本地存取,用完再上傳回伺服器供其他用戶端使用。網際網路的 FTP 服務即可如此使用:下載完整檔案、修改後放回。

圖 11-1:(a) 遠端存取模型。(b) 上傳/下載模型。

NFS 的分層架構#

現代 UNIX 系統上的 NFS 幾乎都採以下分層實作:

  • 用戶端透過本地作業系統的系統呼叫存取檔案系統,但本地 UNIX 檔案系統介面被替換為**虛擬檔案系統(Virtual File System, VFS)**介面——VFS 已是介接各種(分散式)檔案系統的事實標準。
  • VFS 介面上的操作,或者交給本地檔案系統,或者交給稱為 NFS client 的元件處理遠端檔案存取。NFS 中所有主從通訊都透過 RPC 完成:NFS client 把檔案操作實作成對伺服器的 RPC。
  • 伺服器端組織對稱:NFS server 負責處理進來的請求,RPC stub 解封裝(unmarshal)請求後,NFS server 將其轉為一般 VFS 檔案操作,再交由 VFS 層的本地檔案系統執行。

圖 11-2:UNIX 系統上的基本 NFS 架構。

這套方案的重要優點是 NFS 與本地檔案系統大幅解耦:用戶端或伺服器的作業系統實作的是 UNIX、Windows 2000 還是舊式 MS-DOS 檔案系統,原則上都無所謂;唯一的要求是這些檔案系統能相容於 NFS 提供的檔案系統模型。例如 MS-DOS 的短檔名就無法完全透明地實作 NFS 伺服器。

檔案系統模型#

NFS 提供的檔案系統模型幾乎與 UNIX 相同:

  • 檔案被視為未經解讀的位元組序列,以命名圖(naming graph)階層式組織,節點代表目錄與檔案。
  • 支援硬連結(hard link)與符號連結(symbolic link)。
  • 檔案有名稱,但實際存取靠類 UNIX 的檔案控制代碼(file handle):用戶端須先透過命名服務查出名稱對應的 file handle。
  • 每個檔案有若干屬性(attribute)可供查詢與修改。

主要檔案操作在 v3 與 v4 間有不少差異:

  • create:v3 用於建立一般檔案(特殊檔案另有 mknod,子目錄用 mkdir,符號連結用 symlink);v4 中 create 改為建立「非一般檔案」(符號連結、目錄、特殊檔案),一般檔案改由新增的 open 操作建立。
  • open / close:v4 新增,是與舊版檔案處理方式的重大分歧。v4 之前 NFS 刻意讓伺服器維持無狀態(stateless),v4 放棄了這項設計準則,假設伺服器通常會在同一檔案的多次操作間維護狀態。開啟不存在的檔案會有建立新檔的副作用;成功開啟後用戶端以 file handle 存取檔案,關檔則告知伺服器可釋放相關狀態。
  • remove:v4 用它移除任何類型的檔案;舊版移除子目錄需另外的 rmdir。移除是按名稱進行,效果是硬連結數減一,降到零時檔案可被銷毀。
  • lookup:查出路徑名稱對應的 file handle。v3 的 lookup 不會跨越掛載點(mount point)解析:解析 /remote/vu/mbox 時若 /remote/vu 是掛載點,只會回傳掛載點的 handle 與剩餘路徑(mbox),用戶端必須自行掛載所需的檔案系統才能完成解析。v4 簡化了此事:lookup 會嘗試解析整個名稱(前提是掛載點上已掛載檔案系統),用戶端可透過回傳的檔案系統識別碼偵測到跨越了掛載點。
  • readdir:讀取目錄項目,回傳(名稱、file handle)配對清單與所要求的屬性值;可指定回傳筆數,並回傳位移量供下次呼叫續讀。
  • readlink:讀取符號連結所存的資料(通常是路徑名稱)。lookup 無法處理符號連結——解析碰到符號連結會停下,用戶端須先呼叫 readlink 得知該從哪裡繼續解析。
  • getattr / setattr:讀取與設定檔案屬性(如檔案類型、長度、所屬檔案系統識別碼、最後修改時間等)。
  • read / write:read 由用戶端指定位移與位元組數,回傳實際讀到的位元組數與狀態資訊(如是否到檔尾);write 指定寫入位置、長度與資料,並可要求伺服器確保資料寫入穩定儲存(stable storage)——NFS 伺服器必須支援能挺過電源、作業系統與硬體故障的儲存裝置。

圖 11-3:NFS 支援的檔案系統操作(不完整列表)。

叢集式分散式檔案系統#

伺服器叢集常用於平行應用,其檔案系統也隨之調整。一種著名技巧是檔案分條(file striping):把單一檔案切分散布到多台伺服器,便能平行抓取不同部分。

分條只有在應用能有意義地平行存取資料時才划算,通常要求檔案內的資料結構非常規則(例如稠密矩陣)。對一般用途或資料結構不規則的應用,較方便的做法是以整個檔案系統為單位做分割:不同檔案放不同伺服器,但不把單一檔案切開。

圖 11-4:(a) 把整個檔案分散到多台伺服器,與 (b) 將檔案分條以供平行存取,兩者的差異。

Google File System(GFS)#

更有意思的是 Amazon、Google 這類超大型資料中心的檔案系統組織。這些公司的 Web 服務要對散布在數以萬計電腦上的巨量檔案做讀取與更新,傳統假設不再成立——例如任一時刻都可預期有電腦故障。Google 據此開發了自己的 Google File System(GFS)。GFS 的設計觀察:檔案通常非常大(常達數 GB,內含大量小物件)、更新多為**附加(append)**而非覆寫、伺服器故障是常態而非例外。

每個 GFS 叢集由單一 master 與多台 chunk server 組成:

  • 每個 GFS 檔案切成 64 MB 的 chunk,散布在各 chunk server 上。
  • master 只被詢問中繼資料(metadata):用戶端把檔名與 chunk 索引傳給 master,取得存放該 chunk 的 chunk server 聯絡位址,之後只與 chunk server 溝通。
  • master 維護名稱空間、檔名到 chunk 的映射,並記錄 chunk 位置。chunk 會被複製以因應故障,但僅止於此。
  • 有趣的是 master 不試圖精確掌握 chunk 位置,而是偶爾輪詢 chunk server 看它們存了哪些 chunk。這麼做的好處是簡單:若要求該視圖隨時一致,每次 chunk server 當機或加入都得通知 master;靠輪詢刷新簡單得多。反正 chunk 有複本,所要資料極可能在至少一台 chunk server 上。

圖 11-5:Google 伺服器叢集的組織方式。

GFS 為何能擴展?master 大權在握卻不成為瓶頸,靠兩類措施:

  • **最重要的一點:實際工作大宗由 chunk server 承擔。**用戶端向 master 問到位置後就只跟 chunk server 溝通。chunk 依主備(primary-backup)方案複製:用戶端更新時先把資料推給最近的 chunk server,該伺服器再推給下一台最近的持有者,依此類推;全部傳播完後,用戶端聯絡 primary chunk server,由它為更新操作編定序號並傳給備援。整個過程 master 置身事外。
  • (階層式的)檔案名稱空間用單層表格實作,路徑名稱直接映射到中繼資料(相當於傳統檔案系統的 inode),且整張表連同檔案到 chunk 的映射全放在主記憶體。更新記錄到持久儲存的日誌;日誌太大時做檢查點(checkpoint),讓主記憶體資料能直接映射回來。如此大幅降低 master 的 I/O 強度。

這樣的組織讓單一 master 可掌管數百台 chunk server。再把 Google 這類服務拆成映射到各叢集的較小服務,就不難想像巨量叢集能協同運作。

對稱式架構#

完全對稱、基於點對點技術的組織也存在。現行提案都使用 DHT 系統分發資料,搭配鍵值查找(key-based lookup)機制。重要的分野在於:在分散式儲存層之上蓋檔案系統,還是把整個檔案存放在參與節點上

Ivy:區塊導向的三層設計#

Ivy 是建立在 Chord DHT 之上的分散式檔案系統,由三層構成:最底層是提供去中心化查找的 Chord;中間是完全分散、區塊導向的儲存層;最上層是類 NFS 的檔案系統層。

圖 11-6:Ivy 分散式檔案系統的組織架構。

  • 資料儲存由基於 Chord 的區塊導向儲存系統 DHash 實現。DHash 非常簡單,只認得資料區塊(典型大小 8 KB)。
  • Ivy 用兩種區塊:**內容雜湊區塊(content-hash block)**以區塊內容的安全雜湊為鍵,查到區塊即可立刻驗證拿到的是否正確、有無毀損;**公鑰區塊(public-key block)**以公鑰為查找鍵,內容以對應私鑰簽署。
  • 為提高可用性,DHash 把每個區塊複製到負責節點的 k 個直接後繼;查找過的區塊也會沿查找路徑快取。

檔案是 DHash 之上的獨立資料結構:每個使用者維護一份自己對檔案系統操作的日誌(log)(簡化假設每節點單一使用者)。日誌是不可變記錄的串列,每筆記錄含一次操作的完整資訊;節點只對自己的本地日誌附加記錄,只有日誌頭(head)可變、指向最新記錄。每筆記錄存成 content-hash 區塊,日誌頭存成 public-key 區塊。記錄類型大致對應 NFS 的各種操作,例如寫入操作產生 write 記錄,內含檔案識別碼、位移與寫入的資料。

  • 建立新檔案系統:節點建立新日誌與作為根的新 inode。Ivy 部署 NFS loopback server(本地使用者層級的 NFS 伺服器)接受本地用戶端的 NFS 請求,可把新檔案系統掛載起來,讓應用像存取一般 NFS 檔案系統那樣使用。
  • 讀取時,本地 Ivy NFS 伺服器掃過日誌,蒐集針對同一資料區塊的 write 記錄,取回最近存入的值。由於每筆記錄都是 DHash 區塊,可能需要跨覆蓋網路多次查找。

Kosha:以整檔為單位分發#

另一類設計不用獨立的區塊儲存層,改為分發整個檔案。Kosha 的做法是在特定目錄層級分發檔案:每個節點有掛載點 /kosha,存放要以 DHT 分發的檔案。第 1 層分發表示子目錄 /kosha/a 下所有檔案存於同一節點(以 a 的雜湊為查找鍵決定負責節點);第 2 層分發表示 /kosha/a/aa 下所有檔案存於同一節點,依此類推。

這種做法的潛在缺點是節點磁碟空間可能不夠存放其負責子目錄的所有檔案。簡單的解法:把該子目錄的一個分支放到另一節點,並建立符號連結指向分支的新位置。