-
一、RADOS的對象尋址
Ceph 存儲集群從 Ceph 客戶端接收數據——不管是來自 Ceph 塊設備、 Ceph 對象存儲、 Ceph 文件系統、還是基於 librados 的自定義實現——並存儲為對象。每個對象是文件系統中的一個文件,它們存儲在對象存儲設備上。由 Ceph OSD 守護進程處理存儲設備上的讀/寫操作。
在傳統架構里,客戶端與一個中心化的組件通信(如網關、中間件、 API 、前端等等),它作為一個復雜子系統的唯一入口,它引入單故障點的同時,也限制了性能和伸縮性(就是說如果中心化組件掛了,整個系統就掛了)。
Ceph 消除了集中網關,允許客戶端直接和 Ceph OSD 守護進程通訊。 Ceph OSD 守護進程自動在其它 Ceph 節點上創建對象副本來確保數據安全和高可用性;為保證高可用性,監視器也實現了集群化。為消除中心節點, Ceph 使用了 CRUSH 算法。
File —— 此處的file就是用戶需要存儲或者訪問的文件。當用戶要將數據存儲到Ceph集群時,存儲數據都會被分割成多個object。
Ojbect —— 每個object都有一個object id,每個object的大小是可以設置的,默認是4MB,object可以看成是Ceph存儲的最小存儲單元。
PG(Placement Group)—— 顧名思義,PG的用途是對object的存儲進行組織和位置映射。由於object的數量很多,所以Ceph引入了PG的概念用於管理object,每個object最后都會通過CRUSH計算映射到某個pg中,一個pg可以包含多個object。
OSD —— 即object storage device,PG也需要通過CRUSH計算映射到osd中去存儲,如果是二副本的,則每個pg都會映射到二個osd,比如[osd.1,osd.2],那么osd.1是存放該pg的主副本,osd.2是存放該pg的從副本,保證了數據的冗余。
-
(1)File -> object映射
這次映射的目的是,將用戶要操作的file,映射為RADOS能夠處理的object。其映射十分簡單,本質上就是按照object的最大size對file進行切分,相當於RAID中的條帶化過程。這種切分的好處有二:一是讓大小不限的file變成最大size一致、可以被RADOS高效管理的object;二是讓對單一file實施的串行處理變為對多個object實施的並行化處理。
每一個切分后產生的object將獲得唯一的oid,即object id。其產生方式也是線性映射,極其簡單。圖中,ino是待操作file的元數據,可以簡單理解為該file的唯一id。ono則是由該file切分產生的某個object的序號。而oid就是將這個序號簡單連綴在該file id之后得到的。舉例而言,如果一個id為filename的file被切分成了三個object,則其object序號依次為0、1和2,而最終得到的oid就依次為filename0、filename1和filename2。
這里隱含的問題是,ino的唯一性必須得到保證,否則后續映射無法正確進行。
- (2)Object -> PG映射
在file被映射為一個或多個object之后,就需要將每個object獨立地映射到一個PG中去。這個映射過程也很簡單,如圖中所示,其計算公式是:
hash(oid) & mask -> pgid
由此可見,其計算由兩步組成。首先是使用Ceph系統指定的一個靜態哈希函數計算oid的哈希值,將oid映射成為一個近似均勻分布的偽隨機值。然后,將這個偽隨機值和mask按位相與,得到最終的PG序號(pgid)。根據RADOS的設計,給定PG的總數為m(m應該為2的整數冪),則mask的值為m-1。因此,哈希值計算和按位與操作的整體結果事實上是從所有m個PG中近似均勻地隨機選擇一個。基於這一機制,當有大量object和大量PG時,RADOS能夠保證object和PG之間的近似均勻映射。又因為object是由file切分而來,大部分object的size相同,因而,這一映射最終保證了,各個PG中存儲的object的總數據量近似均勻。
從介紹不難看出,這里反復強調了“大量”。只有當object和PG的數量較多時,這種偽隨機關系的近似均勻性才能成立,Ceph的數據存儲均勻性才有保證。為保證“大量”的成立,一方面,object的最大size應該被合理配置,以使得同樣數量的file能夠被切分成更多的object;另一方面,Ceph也推薦PG總數應該為OSD總數的數百倍,以保證有足夠數量的PG可供映射。
- (3)PG -> OSD映射
第三次映射就是將作為object的邏輯組織單元的PG映射到數據的實際存儲單元OSD。如圖所示,RADOS采用一個名為CRUSH的算法,將pgid代入其中,然后得到一組共n個OSD。這n個OSD即共同負責存儲和維護一個PG中的所有object。前已述及,n的數值可以根據實際應用中對於可靠性的需求而配置,在生產環境下通常為3。具體到每個OSD,則由其上運行的OSD deamon負責執行映射到本地的object在本地文件系統中的存儲、訪問、元數據維護等操作。
Ceph通過三次映射,完成了從file到object、PG和OSD整個映射過程。通觀整個過程,可以看到,這里沒有任何的全局性查表操作需求。
-
二、數據的操作流程
以file寫入過程為例,對數據操作流程進行說明。
為簡化說明,便於理解,此處進行若干假定。首先,假定待寫入的file較小,無需切分,僅被映射為一個object。其次,假定系統中一個PG被映射到3個OSD上。
基於上述假定,則file寫入流程可以被下圖表示:
如圖所示,當某個client需要向Ceph集群寫入一個file時,首先需要在本地完成上面敘述的尋址流程,將file變為一個object,然后找出存儲該object的一組三個OSD。這三個OSD具有各自不同的序號,序號最靠前的那個OSD就是這一組中的Primary OSD,而后兩個則依次是Secondary OSD和Tertiary OSD。
找出三個OSD后,client將直接和Primary OSD通信,發起寫入操作(步驟1)。Primary OSD收到請求后,分別向Secondary OSD和Tertiary OSD發起寫入操作(步驟2、3)。當Secondary OSD和Tertiary OSD各自完成寫入操作后,將分別向Primary OSD發送確認信息(步驟4、5)。當Primary OSD確信其他兩個OSD的寫入完成后,則自己也完成數據寫入,並向client確認object寫入操作完成(步驟6)。
之所以采用這樣的寫入流程,本質上是為了保證寫入過程中的可靠性,盡可能避免造成數據丟失。同時,由於client只需要向Primary OSD發送數據,因此,在Internet使用場景下的外網帶寬和整體訪問延遲又得到了一定程度的優化。
當然,這種可靠性機制必然導致較長的延遲,特別是,如果等到所有的OSD都將數據寫入磁盤后再向client發送確認信號,則整體延遲可能難以忍受。因此,Ceph可以分兩次向client進行確認。當各個OSD都將數據寫入內存緩沖區后,就先向client發送一次確認,此時client即可以向下執行。待各個OSD都將數據寫入磁盤后,會向client發送一個最終確認信號,此時client可以根據需要刪除本地數據。
分析上述流程可以看出,在正常情況下,client可以獨立完成OSD尋址操作,而不必依賴於其他系統模塊。因此,大量的client可以同時和大量的OSD進行並行操作。同時,如果一個file被切分成多個object,這多個object也可被並行發送至多個OSD。
從OSD的角度來看,由於同一個OSD在不同的PG中的角色不同,因此,其工作壓力也可以被盡可能均勻地分擔,從而避免單個OSD變成性能瓶頸。
如果需要讀取數據,client只需完成同樣的尋址過程,並直接和Primary OSD聯系。
-
三、集群維護
從上面的學習,我們知道由若干個monitor共同負責整個Ceph集群中所有OSD狀態的發現與記錄,並且共同形成cluster map的master版本,然后擴散至全體OSD以及client。OSD使用cluster map進行數據的維護,而client使用cluster map進行數據的尋址。
在集群中,各個monitor的功能總體上是一樣的,其相互間的關系可以被簡單理解為主從備份關系。monitor並不主動輪詢各個OSD的當前狀態。正相反,OSD需要向monitor上報狀態信息。常見的上報有兩種情況:一是新的OSD被加入集群,二是某個OSD發現自身或者其他OSD發生異常。在收到這些上報信息后,monitor將更新cluster map信息並加以擴散。
Cluster map的實際內容包括:
Montior Map: 包含集群的 fsid 、位置、名字、地址和端口,也包括當前版本、創建時間、最近修改時間。要查看監視器圖,用 ceph mon dump 命令。
OSD Map: 包含集群 fsid 、創建時間、最近修改時間、存儲池列表、副本數量、歸置組數量、 OSD 列表及其狀態(如 up 、 in )。要查看OSD運行圖,用 ceph osd dump 命令。
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。
PG Map::** 包含歸置組版本、其時間戳、最新的 OSD 運行圖版本、占滿率、以及各歸置組詳情,像歸置組 ID 、 up set 、 acting set 、 PG 狀態(如 active+clean ),和各存儲池的數據使用情況統計。
CRUSH Map::** 包含存儲設備列表、故障域樹狀結構(如設備、主機、機架、行、房間、等等)、和存儲數據時如何利用此樹狀結構的規則。要查看 CRUSH 規則,執行 ceph osd getcrushmap -o {filename} 命令;然后用 crushtool -d {comp-crushmap-filename} -o {decomp-crushmap-filename} 反編譯;然后就可以用 cat 或編輯器查看了。
MDS Map: 包含當前 MDS 圖的版本、創建時間、最近修改時間,還包含了存儲元數據的存儲池、元數據服務器列表、還有哪些元數據服務器是 up 且 in 的。要查看 MDS 圖,執行 ceph mds dump 。
-
四、OSD增加流程
(1)一個新的OSD上線后,首先根據配置信息與monitor通信。Monitor將其加入cluster map,並設置為up且out狀態,再將最新版本的cluster map發給這個新OSD。收到monitor發來的cluster map之后,這個新OSD計算出自己所承載的PG(為簡化討論,此處我們假定這個新的OSD開始只承載一個PG),以及和自己承載同一個PG的其他OSD。然后,新OSD將與這些OSD取得聯系。如圖:
(2)如果這個PG目前處於降級狀態(即承載該PG的OSD個數少於正常值,如正常應該是3個,此時只有2個或1個。這種情況通常是OSD故障所致),則其他OSD將把這個PG內的所有對象和元數據復制給新OSD。數據復制完成后,新OSD被置為up且in狀態。而cluster map內容也將據此更新。這事實上是一個自動化的failure recovery過程。當然,即便沒有新的OSD加入,降級的PG也將計算出其他OSD實現failure recovery,如圖:
(3)如果該PG目前一切正常,則這個新OSD將替換掉現有OSD中的一個(PG內將重新選出Primary OSD),並承擔其數據。在數據復制完成后,新OSD被置為up且in狀態,而被替換的OSD將退出該PG(但狀態通常仍然為up且in,因為還要承載其他PG)。而cluster map內容也將據此更新。這事實上是一個自動化的數據re-balancing過程,如圖:
(4)如果一個OSD發現和自己共同承載一個PG的另一個OSD無法聯通,則會將這一情況上報monitor。此外,如果一個OSD deamon發現自身工作狀態異常,也將把異常情況主動上報給monitor。在上述情況下,monitor將把出現問題的OSD的狀態設為down且in。如果超過某一預訂時間期限,該OSD仍然無法恢復正常,則其狀態將被設置為down且out。反之,如果該OSD能夠恢復正常,則其狀態會恢復為up且in。在上述這些狀態變化發生之后,monitor都將更新cluster map並進行擴散。這事實上是自動化的failure detection過程。