1. 簡介
在傳統分布式存儲架構中,存儲節點往往僅作為被動查詢對象來使用,隨著存儲規模的增加,數據一致性的管理會出現很多問題。
而新型的存儲架構傾向于將基本的塊分配決策和安全保證等操作交給存儲節點來做,然后通過提倡客戶端和存儲節點直接交互來簡化數據布局并減小io瓶頸。
RADOS就是這樣一個可用于PB級規模數據存儲集群的可伸縮的、可靠的對象存儲服務。它包含兩類節點:存儲節點、管理節點。它通過利用存儲設備的智能性,將諸如一致性數據訪問、冗余存儲、錯誤檢測、錯誤恢復分布到包含了上千存儲節點的集群中,而不是僅僅依靠少數管理節點來處理。
RADOS中的存儲節點被稱為OSD(object storage device),它可以僅由很普通的組件來構成,只需要包含CPU、網卡、本地緩存和一個磁盤或者RAID,并將傳統的塊存儲方式替換成面向對象的存儲。
在PB級的存儲規模下,存儲系統一定是動態的:系統會隨著新設備的部署和舊設備的淘汰而增長或收縮,系統內的設備會持續地崩潰和恢復,大量的數據被創建或者刪除。RADOS通過 cluster map來實現這些,cluster map會被復制到集群中的所有部分(存儲節點、控制節點,甚至是客戶端),并且通過怠惰地傳播小增量更新而更新。cluster map中存儲了整個集群的數據的分布以及成員。
通過在每個存儲節點存儲完整的cluster map,存儲設備可以表現的半自動化,通過peer-to-peer的方式(比如定義協議)來進行數據備份、更新,錯誤檢測、數據遷移等等操作。這無疑減輕了占少數的monitor cluster(管理節點組成的集群)的負擔。
2. 設計
一個RADOS系統包含大量的OSDs 和 很少的用于管理OSD集群成員的monitors。OSD的組成如簡介所說。而monitor是一些獨立的進程,以及少量的本地存儲,monitor之間通過一致性算法保證數據的一致性。
2.1 Cluster Map
存儲節點集群通過monitor集群操作cluster map來實現成員的管理。cluster map 描述了哪些OSD被包含進存儲集群以及所有數據在存儲集群中的分布。
cluster map不僅存儲在monitor節點,它被復制到集群中的每一個存儲節點,以及和集群交互的client。
當因為一些原因,比如設備崩潰、數據遷移等,cluster map的內容需要改變時,cluster map的版本號被增加,map的版本號可以使通信的雙方確認自己的map是否是最新的,版本舊的一方會先將map更新成對方的map,然后才會進行后續操作。
2.2 Data Placement
首先,如下圖,總體說下RADOS的存儲層次,RADOS中基本的存儲單位是對象,一般為2MB或4MB,當一個文件要存入RADOS時,首先會被切分成大小固定的對象(最后一個對象大小可能不同),然后將對象分配到一個PG(Placement Group)中,然后PG會復制幾份,偽隨機地派給不同的存儲節點。當新的存儲節點被加入集群,會在已有數據中隨機抽取一部分數據遷移到新節點。這種概率平衡的分布方式可以保證設備在潛在的高負載下正常工作。更重要的是,數據的分布過程僅需要做幾次隨機映射,不需要大型的集中式分配表。
對于每個層次的詳細說明:
- File—— 用戶需要存儲或者訪問的文件。
2.Object—— RADOS的基本存儲單元。Object與上面提到的file的區別是,object的最大size由RADOS限定(通常為2MB或4MB),以便實現底層存儲的組織管理。因此,當上層應用向RADOS存入size很大的file時,需要將file切分成統一大小的一系列object(最后一個的大小可以不同)進行存儲。
PG(Placement Group)—— 對object的存儲進行組織和位置映射。具體而言,一個PG負責組織若干個object(可以為數千個甚至更多),但一個object只能被映射到一個PG中,即,PG和object之間是“一對多”映射關系。同時,一個PG會被映射到n個OSD上,而每個OSD上都會承載大量的PG,即,PG和OSD之間是“多對多”映射關系。在實踐當中,n至少為2(n代表冗余的份數),如果用于生產環境,則至少為3。一個OSD上的PG則可達到數百個。事實上,PG數量的設置牽扯到數據分布的均勻性問題。
OSD—— 即object storage device,前文已經詳細介紹,此處不再展開。唯一需要說明的是,OSD的數量事實上也關系到系統的數據分布均勻性,因此其數量不應太少。在實踐當中,至少也應該是數十上百個的量級才有助于Ceph系統的設計發揮其應有的優勢。
各層次之間的映射關系:
file -> object
object的最大size是由RADOS配置的,當用戶要存儲一個file,需要將file切分成幾個object。object -> PG
每個object都會被映射到一個PG中,然后以PG為單位進行備份以及進一步映射到具體的OSD上。
至于具體的映射方式,直接引用論文吧,以防歧義(才不是我懶)。
Each object’s PG is determined by a hash of the object name o, the desired level of replication r, and a bit mask m that controls the total number of placement groups in the system. That is, pgid = (r, hash(o)&m), where & is a bit-wise AND and the mask m = 2k ?1, constraining the number of PGs by a power of two. As the cluster scales, it is periodically necessary to adjust the total number of placement groups by changing m; this is done gradually to throttle the resulting migration of PGs between devices.
- PG -> OSD
根據用戶設置的冗余存儲的個數r,PG會最終存儲到r個OSD上,這個映射是通過一種偽隨機的映射算法 CRUSH 來實現的,這個算法的特點是可以進行配置。具體請看Ceph的另一篇論文。關于如何將數據拷貝到多個OSD上,RADOS有三種方式,具體查閱論文吧,一般的數據一致性思想。
集群維護
前面的介紹中已經提到,由若干個monitor共同負責整個RADOS集群中所有OSD狀態的發現與記錄,并且共同形成cluster map的master版本,然后擴散至全體OSD以及client。OSD使用cluster map進行數據的維護,而client使用cluster map進行數據的尋址。
monitor并不主動輪詢各個OSD的當前狀態。相反,OSD需要向monitor上報狀態信息。常見的上報有兩種情況:一是新的OSD被加入集群,二是某個OSD發現自身或者其他OSD發生異常。在收到這些上報信息后,monitor將更新cluster map信息并加以擴散。其細節將在下文中加以介紹。
Cluster map的實際內容包括:
(1) Epoch,即版本號。cluster map的epoch是一個單調遞增序列。epoch越大,則cluster map版本越新。因此,持有不同版本cluster map的OSD或client可以簡單地通過比較epoch決定應該遵從誰手中的版本。而monitor手中必定有epoch最大、版本最新的cluster map。當任意兩方在通信時發現彼此epoch值不同時,將默認先將cluster map同步至高版本一方的狀態,再進行后續操作。
(2)各個OSD的網絡地址。
(3)各個OSD的狀態。OSD狀態的描述分為兩個維度:up或者down(表明OSD是否正常工作),in或者out(表明OSD是否在至少一個PG中)。因此,對于任意一個OSD,共有四種可能的狀態:
up且in:說明該OSD正常運行,且已經承載至少一個PG的數據。這是一個OSD的標準工作狀態;
up且out:說明該OSD正常運行,但并未承載任何PG,其中也沒有數據。一個新的OSD剛剛被加入Ceph集群后,便會處于這一狀態。而一個出現故障的OSD被修復后,重新加入Ceph集群時,也是處于這一狀態;
down且in:說明該OSD發生異常,但仍然承載著至少一個PG,其中仍然存儲著數據。這種狀態下的OSD剛剛被發現存在異常,可能仍能恢復正常,也可能會徹底無法工作;
down且out:說明該OSD已經徹底發生故障,且已經不再承載任何PG。
(4)CRUSH算法配置參數。表明了Ceph集群的物理層級關系(cluster hierarchy),位置映射規則(placement rules)。
根據cluster map的定義可以看出,其版本變化通常只會由(3)和(4)兩項信息的變化觸發。而這兩者相比,(3)發生變化的概率更高一些。這可以通過下面對OSD工作狀態變化過程的介紹加以反映。
一個新的OSD上線后,首先根據配置信息與monitor通信。Monitor將其加入cluster map,并設置為up且out狀態,再將最新版本的cluster map發給這個新OSD。
收到monitor發來的cluster map之后,這個新OSD計算出自己所承載的PG(為簡化討論,此處我們假定這個新的OSD開始只承載一個PG),以及和自己承載同一個PG的其他OSD。然后,新OSD將與這些OSD取得聯系。如果這個PG目前處于降級狀態(即承載該PG的OSD個數少于正常值,如正常應該是3個,此時只有2個或1個。這種情況通常是OSD故障所致),則其他OSD將把這個PG內的所有對象和元數據復制給新OSD。數據復制完成后,新OSD被置為up且in狀態。而cluster map內容也將據此更新。這事實上是一個自動化的failure recovery過程。當然,即便沒有新的OSD加入,降級的PG也將計算出其他OSD實現failure recovery。
如果該PG目前一切正常,則這個新OSD將替換掉現有OSD中的一個(PG內將重新選出Primary OSD),并承擔其數據。在數據復制完成后,新OSD被置為up且in狀態,而被替換的OSD將退出該PG(但狀態通常仍然為up且in,因為還要承載其他PG)。而cluster map內容也將據此更新。這事實上是一個自動化的數據re-balancing過程。
如果一個OSD發現和自己共同承載一個PG的另一個OSD無法聯通,則會將這一情況上報monitor。此外,如果一個OSD deamon發現自身工作狀態異常,也將把異常情況主動上報給monitor。在上述情況下,monitor將把出現問題的OSD的狀態設為down且in。如果超過某一預訂時間期限,該OSD仍然無法恢復正常,則其狀態將被設置為down且out。反之,如果該OSD能夠恢復正常,則其狀態會恢復為up且in。在上述這些狀態變化發生之后,monitor都將更新cluster map并進行擴散。這事實上是自動化的failure detection過程。
對于一個RADOS集群而言,即便由數千個甚至更多OSD組成,cluster map的數據結構大小也并不驚人。同時,cluster map的狀態更新并不會頻繁發生。即便如此,Ceph依然對cluster map信息的擴散機制進行了優化,以便減輕相關計算和通信壓力:
首先,cluster map信息是以增量形式擴散的。如果任意一次通信的雙方發現其epoch不一致,則版本更新的一方將把二者所擁有的cluster map的差異發送給另外一方。
其次,cluster map信息是以異步且lazy的形式擴散的。也即,monitor并不會在每一次cluster map版本更新后都將新版本廣播至全體OSD,而是在有OSD向自己上報信息時,將更新回復給對方。類似的,各個OSD也是在和其他OSD通信時,將更新發送給版本低于自己的對方。
基于上述機制,Ceph避免了由于cluster map版本更新而引起的廣播風暴。這雖然是一種異步且lazy的機制,但根據論文中的結論,對于一個由n個OSD組成的Ceph集群,任何一次版本更新能夠在O(log(n))時間復雜度內擴散到集群中的任何一個OSD上。
一個可能被問到的問題是:既然這是一種異步和lazy的擴散機制,則在版本擴散過程中,系統必定出現各個OSD看到的cluster map不一致的情況,這是否會導致問題?答案是:不會。事實上,如果一個client和它要訪問的PG內部的各個OSD看到的cluster map狀態一致,則訪問操作就可以正確進行。而如果這個client或者PG中的某個OSD和其他幾方的cluster map不一致,則根據Ceph的機制設計,這幾方將首先同步cluster map至最新狀態,并進行必要的數據re-balancing操作,然后即可繼續正常訪問。