摘要:分佈式異步對象存儲 (DAOS) 是一個開源的對象存儲系統,專爲大規模分佈式非易失性內存 (NVM, Non-Volatile Memory) 設計,利用了 SCM(Storage-Class Memory) 和 NVMe(Non-Volatile Memory express) 等的下一代 NVM 技術。
本文分享自華爲雲社區《DAOS 分佈式異步對象存儲》,原文做者:debugzhang 。算法
分佈式異步對象存儲 (DAOS) 是一個開源的對象存儲系統,專爲大規模分佈式非易失性內存 (NVM, Non-Volatile Memory) 設計,利用了 SCM(Storage-Class Memory) 和 NVMe(Non-Volatile Memory express) 等的下一代 NVM 技術。數據庫
DAOS 是一種橫向擴展的對象存儲,能夠爲高性能計算應用提供高帶寬、低延遲和高 IOPS 的存儲容器,並支持結合仿真、數據分析和機器學習的下一代以數據爲中心的工做流程。express
與主要針對旋轉介質設計的傳統存儲堆棧不一樣,DAOS 針對全新 NVM 技術進行了從新構建,可在用戶空間中端對端地運行,並能徹底繞開操做系統,是一套輕量級的系統。後端
DAOS 提供了一種爲訪問高細粒度數據提供原生支持的 I/O 模型,而不是傳統的基於高延遲和塊存儲設計的 I/O 模型,從而釋放下一代存儲技術的性能。數組
與傳統的緩衝區不一樣,DAOS 是一個獨立的高性能容錯存儲層,它不依賴其它層來管理元數據並提供數據恢復能力。DAOS 服務器將其元數據保存在持久內存中,而將批量數據直接保存在 NVMe 固態盤中。安全
DAOS 特性
DAOS 依靠 OFI(OpenFabric Interface) 繞過操做系統,將 DAOS 操做交付給 DAOS 存儲服務器,充分利用架構中的任何遠程直接內存訪問 (RDMA, Remote Direct Memory Access) 功能,進行低延遲、高消息速率的用戶空間通訊,並將數據存儲在持久內存和 NVMe 固態盤中。服務器
DAOS 的鍵值存儲接口提供了統一的存儲模型,經過遷移 I/O 中間件庫實現對 DAOS API 的原生支持後,就能利用 DAOS 豐富的 API 和先進功能,例如 HDF五、MPI-IO 和 Apache Arrow。網絡
DAOS 還提供 POSIX 的仿真。POSIX 再也不是新數據模型的基礎,而是像其餘 I/O 中間件同樣,POSIX 接口將構建爲 DAOS 後端 API 頂部的庫。數據結構
DAOS 的 I/O 操做會被記錄並存儲到 SCM 維護到持久索引中,每次 I/O 都用一個特定時間戳標記,並與數據集的特定版本關聯。內部不執行讀-修改-寫 (read-modify-write) 操做,寫入操做是無損的,對對齊不敏感。在讀取請求時,DAOS 服務器遍歷持久索引,建立聚合 RDMA 描述符,從而直接在應用程序提供的緩衝區中重建所請求的版本的數據。架構
SCM 直接映射到 DAOS 服務地址空間的內存,DAOS 服務經過直接加載/存儲來管理持久索引。根據不一樣 I/O 的特性,DAOS 服務能夠決定將 I/O 存儲在 SCM 或 NVMe 中:
- 對延遲敏感的I/O(如應用程序元數據和字節粒度數據)一般存儲在 SCM 中;
- 檢查點和批量數據存儲在 NVMe 中。
這種方法容許 DAOS 將數據流式傳輸到 NVMe 中,並在 SCM 中維護內部元數據索引,爲批量數據提供原始 NVMe 帶寬。持久內存開發工具包 PMDK 管理對 SCM 的事務性訪問,存儲性能開發工具包 SPDK 對 NVMe 設備進行用戶空間 I/O 操做。
DAOS 能夠提供:
- 超高細粒度、低延遲和真正零拷貝的 I/O
- 非阻塞型數據和元數據操做,以支持 I/O 和計算重疊
- 先進的數據放置,以解決故障域
- 由軟件管理冗餘,可經過在線重建,支持複製和擦除代碼
- 端到端 (E2E) 數據完整性
- 可擴展的分佈式事務,提供可靠的數據一致性和自動恢復功能
- 數據集快照功能
- 安全框架,用於管理存儲池的訪問控制
- 軟件定義存儲管理,用於調配、配置、修改和監控存儲池
- 經過 DAOS 數據模型和 API,爲 I/O 中間件庫(例如 HDF五、 MPI-IO 和 POSIX)提供原生支持。應用無需移植代碼,便可直接使用 DAOS API
- Apache Spark 集成
- 使用發佈/訂閱 API,實現原生生產者/消費者工做流程
- 數據索引和查詢功能
- 存儲內計算,以減小存儲和計算節點之間的數據移動
- 容災工具
- 與 Lustre 並行文件系統無縫集成,並能擴展到其餘並行文件系統,從而爲跨多個存儲層的數據訪問提供統一的命名空間
- 數據搬運器,用於在 DAOS 池之間遷移數據集,將數據集從並行文件系統遷移到 DAOS,反之亦然
DAOS 組件
一個數據中心可能有數十萬個計算節點,經過一個可伸縮的高性能結構相互鏈接,其中全部節點或稱爲存儲節點的節點子集均可以直接訪問 NVM 存儲。
DAOS 安裝涉及幾個能夠集中或分佈式的組件。
DAOS 系統和存儲節點
DAOS系統由一個系統名標識,它由一組鏈接到同一結構的 DAOS 存儲節點組成。DAOS 存儲節點爲每一個節點運行一個 DAOS 服務實例,該實例爲每一個物理套接字啓動一個 DAOS I/O 引擎進程。這些 DAOS 服務的信息被記錄到系統映射中,該映射爲每一個 I/O 引擎進程分配一個惟一的整數秩。兩個不一樣的 DAOS 系統由兩組不相交的 DAOS 服務器組成,它們之間沒法相互配合。
DAOS 服務
DAOS 服務是一個多租戶守護進程,運行在每一個存儲節點的 Linux 實例上(物理節點、虛擬機或容器)。服務的 I/O 引擎子進程經過網絡導出本地鏈接的 SCM 和 NVM 存儲。服務監聽一個管理端口(由 IP 地址和 TCP 端口號尋址),以及一個或多個結構端點(由網絡 URI 尋址)。
DAOS 服務經過 /etc/DAOS 中的 YAML 文件進行配置,包括其 I/O 引擎子進程的配置。服務的啓動能夠與不一樣的守護進程管理或編排框架集成(systemd 腳本、Kubernetes 服務、或相似 pdsh 和 srun 的並行啓動程序)。
I/O引擎
在 DAOS I/O 引擎中,存儲靜態地跨越多個 Target 分區,加強併發能力。爲了不競爭,每一個 Target 都有其私有存儲、本身的服務線程池以及專用的網絡上下文,這些上下文能夠直接經過結構尋址,而不依賴於託管在同一存儲節點上的其餘 Target 。
SCM 模塊一般以 AppDirect interleaved 模式配置。所以,它們做爲每一個套接字(在 fsdax 模式)的單個 PMEM 命名空間呈現給操做系統。當配置每一個 I/O 引擎的 N 個 Target 時,每一個 Target 都使用該套接字 fsdax 的 SCM 容量的 \frac{1}{N}N1,與其它 Target 獨立。每一個 Target 還使用鏈接到此套接字的 NVMe 驅動器容量的一小部分。
Target
Target 沒有針對存儲介質故障實現任何內部數據保護機制。所以,一個 Target 就是一個單點故障,同時也是故障單元。動態狀態與每一個 Target 相關聯:其狀態能夠是「up and running」,也能夠是「down and not available」。
Target 是性能的單位。與 Target 關聯的硬件組件(如後端存儲介質、CPU 核心和網絡)的能力和容量有限。
DAOS I/O 引擎實例導出的 Target 數是可配置的,取決於底層硬件(I/O 引擎實例的 SCM 模塊數和 NVMe SSD 數)。I/O 引擎的 Target 數的最佳配置是該 I/O 引擎服務的 NVMe 驅動器數的整數倍。
存儲 API、應用程序接口和工具
應用程序、用戶和管理員能夠經過兩個不一樣的客戶端 API 與 DAOS 系統交互。
管理 API 提供了管理 DAOS 系統的接口。它旨在與不一樣供應商的存儲管理或開源編排框架集成。dmg 命令行工具是在 DAOS 的管理 API 上構建的。
DAOS 庫 libdaos 實現了 DAOS 存儲模型,主要提供給但願在 DAOS 系統中存儲數據集的應用程序和 I/O 中間件的開發人員。用戶經常使用的 daos 命令等的也構建在 API 之上,容許用戶經過命令行管理數據集。
應用程序能夠經過本機 DAOS API、I/O 中間件庫(如 POSIX 仿真、MPI-IO、HDF5)或已與本機 DAOS 存儲模型集成的 Spark 或 TensorFlow 等框架直接訪問存儲在 DAOS 中的數據集。
代理
DAOS 代理是駐留在客戶端節點上的守護進程,經過與 DAOS 庫交互驗證應用程序進程。它是一個可信任的實體,支持使用證書對 DAOS 客戶端進行簽名。DAOS 代理支持不一樣的身份驗證框架,並使用 Unix 域套接字與客戶端庫通訊。
存儲模型
DAOS Pool 是分佈在 Target 集合上的存儲資源預留。分配給每一個 Target 上的 Pool 的實際空間稱爲 Pool Shard。
分配給 Pool 的總空間在建立時肯定,後期能夠經過調整全部 Pool Shard 的大小(在每一個 Target 專用的存儲容量限制內)或跨越更多 Target(添加更多 Pool Shard)來隨時間擴展。
Pool 提供了存儲虛擬化,是資源調配和隔離的單元。DAOS Pool 不能跨多個系統。
一個 Pool 能夠承載多個稱爲 DAOS Container 的事務對象存儲。每一個 Container 都是一個私有的對象地址空間,能夠對其進行事務性修改,而且獨立於存儲在同一 Pool 中的其餘 Container。Container 是快照和數據管理的單元。屬於 Container 的 DAOS 對象能夠分佈在當前 Pool 的任何一個 Target 上以提升性能和恢復能力,而且能夠經過不一樣的 API 訪問,從而高效地表示結構化、半結構化和非結構化數據。
下表顯示了每一個 DAOS 概念的目標可伸縮性級別:
DAOS Pool
Pool 由惟一的 Pool UUID 標識,並在稱爲 Pool 映射的持久版本控制列表中維護 Target 成員身份。成員資格是肯定的和一致的,成員資格的變動是按順序編號的。Pool 映射不只記錄活躍 Target 的列表,還以樹的形式包含存儲拓撲,用於標識共享公共硬件組件的 Target。例如,樹的第一級能夠表示共享同一主板的 Target,第二級能夠表示共享同一機架的全部主板,最後第三級能夠表示同一機房中的全部機架。
該框架有效地表示了層次化的容錯域,而後使用這些容錯域來避免將冗餘數據放置在發生相關故障的 Target 上。在任什麼時候候,均可以將新 Target 添加到 Pool 映射中,而且能夠排除失敗的 Target。此外,Pool 映射是徹底版本化的,這有效地爲映射的每次修改分配了惟一的序列,特別是對於失敗節點的刪除。
Pool Shard 是永久內存的預留,能夠選擇與特定 Target 上 NVMe 預先分配的空間相結合。它有一個固定的容量,滿了就不能運行。能夠隨時查詢當前空間使用狀況,並報告 Pool Shard 中存儲的任何數據類型所使用的總字節數。
一旦 Target 失敗並從 Pool 映射中排除,Pool 中的數據冗餘將自動在線恢復。這種自愈過程稱爲重建。重建進度按期記錄在永久內存中存儲的 Pool 中的特殊日誌中,以解決級聯故障。添加新 Target 時,數據會自動遷移到新添加的 Target,以便在全部成員之間平均分配佔用的空間。這個過程稱爲空間再平衡,使用專用的持久性日誌來支持中斷和重啓。
Pool 是分佈在不一樣存儲節點上的一組 Target,在這些節點上分佈數據和元數據以實現水平可伸縮性,並使用複製或糾刪碼 (erasure code) 確保持久性和可用性。
建立 Pool 時,必須定義一組系統屬性以配置 Pool 支持的不一樣功能。此外,用戶還能夠定義將持久存儲的屬性。
Pool 只能由通過身份驗證和受權的應用程序訪問。DAOS 支持多種安全框架,例如 NFSv4 訪問控制列表或基於第三方的身份驗證 (Kerberos)。鏈接到 Pool 時強制執行安全性檢查。成功鏈接到 Pool 後,將嚮應用程序進程返回鏈接上下文。
如前文所述,Pool 存儲許多不一樣種類的持久性元數據,如 Pool 映射、身份驗證和受權信息、用戶屬性、特性和重建日誌。這些元數據很是關鍵,須要最高級別的恢復能力。所以,Pool 的元數據被複制到幾個來自不一樣高級容錯域的節點上。對於具備數十萬個存儲節點的很是大的配置來講,這些節點中只有很小的一部分(大約幾十個)運行 Pool 元數據服務。在存儲節點數量有限的狀況下,DAOS 能夠依賴一致性算法來達成一致,在出現故障時保證一致性,避免腦裂。
要訪問 Pool,用戶進程應該鏈接到 Pool 並經過安全檢查。一旦受權,Pool 就能夠與任何或全部對等應用程序進程(相似 openg() POSIX 擴展)共享(經過 local2global() 和 global2local() 操做)鏈接。這種集體鏈接機制有助於在數據中心上運行大規模分佈式做業時避免元數據請求風暴。當發出鏈接請求的原始進程與 Pool 斷開鏈接時,Pool 鏈接將被註銷。
DAOS Container
Container 表明 Pool 中的對象地址空間,由 Container UUID 標識。
下圖顯示了用戶(I/O 中間件、特定領域的數據格式、大數據或 AI 框架等)如何使用 Container 來存儲相關數據集:
與 Pool 同樣,Container 能夠存儲用戶屬性。Container 在建立時必須傳遞一組屬性,以配置不一樣的功能,例如校驗和。
要訪問 Container,應用程序必須首先鏈接到 Pool,而後打開 Container。若是應用程序被受權訪問 Container,則返回 Container 句柄,它的功能包括受權應用程序中的任何進程訪問 Container 及其內容。打開進程能夠與全部對等進程共享此句柄。它們的功能在 Container 關閉時被撤銷。
Container 中的對象可能具備不一樣的模式,用於處理數據分佈和 Target 上的冗餘,定義對象模式所需的一些參數包括動態或靜態條帶化、複製或糾刪碼。Object 類定義了一組對象的公共模式屬性,每一個 Object 類都被分配一個惟一的標識符,並在 Pool 級別與給定的模式相關聯。一個新的 Object 類能夠在任什麼時候候用一個可配置的模式來定義,這個模式在建立以後是不可變的(或者至少在屬於這個類的全部對象都被銷燬以前)。
爲了方便起見,在建立 Pool 時,默認狀況下會預約義幾個最經常使用的 Object 類:
以下所示,Container 中的每一個對象都由一個惟一的 128 位對象地址標識。對象地址的高 32 位保留給 DAOS 來編碼內部元數據,好比 Object 類。剩下的 96 位由用戶管理,在 Container 中應該是惟一的。只要保證惟一性,棧的上層就可使用這些位來編碼它們的元數據。DAOS API 爲每一個 Container 提供了 64 位可伸縮對象 ID 分配器。應用程序要存儲的對象 ID 是完整的 128 位地址,該地址僅供一次性使用,而且只能與單個對象模式相關聯。
<---------------------------------- 128 bits ----------------------------------> -------------------------------------------------------------------------------- |DAOS Internal Bits| Unique User Bits | -------------------------------------------------------------------------------- <---- 32 bits ----><------------------------- 96 bits ------------------------->
Container 是事務和版本控制的基本單元。全部的對象操做都被 DAOS 庫隱式地標記爲一個稱爲 epoch 的時間戳。DAOS 事務 API 容許組合多個對象更新到單個原子事務中,並基於 epoch 順序進行多版本併發控制。全部版本更新均可以按期聚合,以回收重疊寫入所佔用的空間,並下降元數據複雜性。快照是一個永久引用,能夠放置在特定的 epoch 上以防止聚合。
Container 元數據(快照列表、打開的句柄、對象類、用戶屬性、屬性和其餘)存儲在持久性內存中,並由專用 Container 元數據服務維護,該服務使用與父元數據 Pool 服務相同的複製引擎或本身的引擎,這在建立 Container 時是可配置的。
與 Pool 同樣,對 Container 的訪問由 Container 句柄控制。要獲取有效的句柄,應用程序進程必須打開 Container 並經過安全檢查。而後,能夠經過 Container 的 local2global() 和 global2local() 操做與其餘對等應用程序進程共享此句柄。
DAOS Object
爲了不傳統存儲系統常見的擴展問題和開銷,DAOS 有意將對象簡化,不提供類型和架構以外的默認對象元數據。這意味着系統不維護時間、大小、全部者、權限,甚至不跟蹤開啓者。
爲了實現高可用性和水平伸縮性,DAOS 提供了許多對象模式(複製/糾刪碼、靜態/動態條帶化等)。模式框架是靈活的,而且易於擴展,以容許未來使用新的自定義模式類型。模式佈局是在對象標識符和 Pool 映射打開的對象上經過算法生成的。經過在網絡傳輸和存儲期間使用校驗和保護對象數據,確保了端到端的完整性。
能夠經過不一樣的 API 訪問 DAOS 對象:
- Multi-level key-array API 是具備局部性特徵的本機對象接口。密鑰分爲分發密鑰 (dkey) 和屬性密鑰 (akey)。dkey 和 akey 均可以是可變長度的類型(字符串、整數或其它複雜的數據結構)。同一 dkey 下的全部條目都保證在同一 Target 上並置。與 akey 關聯的值能夠是不能部分修改的單個可變長度值,也能夠是固定長度值的數組。akeys 和 dkey 都支持枚舉。
- Key-value API 提供了一個簡單的鍵和可變長度值接口。它支持傳統的 put、get、remove 和 list 操做。
- Array API 實現了一個由固定大小的元素組成的一維數組,該數組的尋址方式是 64 位偏移尋址。DAOS 數組支持任意範圍的讀、寫和 punch 操做。
事務模型
DAOS API 支持分佈式事務,容許將針對屬於同一 Container 的對象的任何更新操做組合到單個 ACID 事務中。分佈式一致性是經過基於多版本時間戳排序的無鎖樂觀併發控制機制提供的。DAOS 事務是可串行化的,能夠在特定的基礎上獲取部分須要的數據集。
DAOS 版本控制機制容許建立持久的 Container 快照,該快照提供 Container 的實時分佈一致性視圖,該視圖可用於構建生產者-消費者管道。
Epoch 和時間戳
每一個 DAOS I/O 操做都有一個稱爲 epoch 的時間戳。epoch 是一個 64 位整數,它集成了邏輯和物理時鐘(詳見 HLC paper)。DAOS API 提供了輔助函數,用於將 epoch 轉換爲傳統的 POSIX 時間(即 struct timespec,詳見 clock_gettime(3))。
Container 快照
以下圖所示,Container 的內容能夠隨時快照。
DAOS 快照很是輕量級,而且使用與建立快照的時間相關聯的 epoch 進行標記。一旦建立成功,快照將一直保持可讀性,直到它被顯式銷燬。在特定快照未被銷燬前,Container 的內容能夠回滾到該快照。
Container 快照功能支持本機生產者/消費者管道:
一旦成功寫入數據集的一致版本,生產者 (Producer) 將生成一個快照。使用者 (Consumer) 的應用程序能夠訂閱 Container 快照事件,以便在生產者提交更新時能夠處理新的更新。
快照的不變性保證了使用者能夠看到一致的數據,即便生產者繼續進行新的更新。生產者和消費者實際上都在 Container 的不一樣版本上操做,不須要任何串行化操做。一旦生產者生成了數據集的新版本,使用者就能夠查詢兩個快照之間的差別,而且只處理增量修改。
分佈式事務
與 POSIX 不一樣,DAOS API 不強制執行最壞狀況下的併發控制機制來處理衝突的 I/O 操做。相反,各個 I/O 操做被標記爲不一樣的 epoch,並按照 epoch 的順序應用,而無論執行順序如何。這個基準模型爲不產生衝突的 I/O 工做負載的數據模型和應用程序提供了最大的可伸縮性和最高的性能。典型的例子是 MPI-IO 集合操做、POSIX 文件讀/寫操做和 HDF5 數據集讀/寫操做。
對於須要將衝突串行化的部分數據模型,DAOS 提供了基於多版本併發控制的分佈式可串行化事務。當不一樣的用戶進程要覆蓋與 dkey/akey 關聯的值時,一般須要該事務。例如 DAOS 上的 SQL 數據庫,或者由非一致的客戶端併發訪問的一致的 POSIX 命名空間。
在同一操做的上下文中提交的全部 I/O 操做(包括讀取)將使用相同的 epoch。DAOS 事務機制自動檢測傳統的讀/寫、寫/讀和寫/寫衝突,並停止其中一個衝突事務(事務在 -DER_RESTART 參數下提交失敗)。而後,用戶/應用程序必須從新啓動失敗的事務。
在目前的實現中,事務 API 具備如下限制,這些限制將在將來的 DAOS 版本中解決:
- 不支持 Array API
- 經過同一上下文環境執行的對象獲取/列表和鍵值獲取/列表操做所進行的事務對象更新和鍵值放入操做不可見。