分散式儲存系統之Ceph叢集儲存池、PG 與 CRUSH

2022-10-06 06:01:37

  前文我們瞭解了ceph叢集狀態獲取常用命令以及通過ceph daemon、ceph tell動態設定ceph元件、ceph.conf組態檔相關格式的說明等,回顧請參考https://www.cnblogs.com/qiuhom-1874/p/16727820.html;今天我們來聊一聊ceph的儲存池、PG以及CRUSH相關話題;

  一、關於儲存池

  我們知道ceph使用者端儲存物件資料到RADOS叢集上,不是使用者端直接儲存到osd上;首先使用者端會根據ceph叢集的設定,將使用者端儲存的物件資料切分多個固定大小的物件資料,然後再將這些固定大小的資料物件通過一致性hash演演算法將物件資料對映至儲存池裡的PG,然後由CRUSH演演算法計算以後,再將PG對映至對應osd,然後由mon返回osd的ID給使用者端,使用者端拿著mon給的osd相關資訊主動聯絡對應osd所在節點osd程序,進行資料儲存操作;

  什麼是儲存池呢?在ceph上,所謂儲存池是ceph以「儲存池(pool)」的方式,將RADOS儲存叢集提供的儲存服務邏輯分割一個或多個儲存區域;我們可以理解為資料物件的名稱空間;實踐中,管理員可以為特定應用程式儲存不同型別資料的需求分別建立專用的儲存池,例如rbd儲存池、rgw儲存池等,也可以為某個專案或某個使用者建立專有的儲存池;當然,如果我們在一個儲存池裡儲存的資料過多,為了方便管理,儲存池還可以進一步細分為一至多個名稱空間(namespace);使用者端(包括rbd和rgw等)存取資料時,需要事先指定儲存池名稱、使用者名稱和金鑰等資訊完成認證,而後將一直維持與其指定的儲存池的連線,於是也可以把儲存池看作是使用者端的IO介面;

  Ceph儲存池型別

  在ceph上,儲存池有兩種型別;預設情況下,我們不指定什麼型別的儲存池就是副本池(replicated pool);所謂副本池就是儲存在該儲存之上的物件資料,都會由RADOS叢集將每個物件資料在叢集中儲存為多個副本,其中儲存於主OSD的為主副本,副本數量在建立儲存池時 由管理員指定;預設情況下不指定副本數量,對應副本數量為3個,即1主2從;從上面的描述可以看到,當我們儲存一份物件資料時,為了冗餘備份,我們需要將資料儲存3分,即有兩份冗餘;這也意味著,我們磁碟利用率也只有1/3;於是,為了提高磁碟的利用率的同時,又能保證冗餘,ceph還支援糾刪碼池(erasure code);糾刪碼池就是把個物件儲存為 N=K+M 個塊,其中,K為資料塊數量,M為編碼塊數量,因此儲存池的尺寸為 K+M ;糾刪碼是一種向前錯誤更正(FEC)程式碼通過將K塊的資料轉換為N塊,假設N=K+M,則其中的M代表糾刪碼演演算法新增的額外或冗餘的塊數量以提供冗餘機制(即編碼塊),而N則表示在糾刪碼編碼之後要建立的塊的總數,其可以故障的總塊數為M(即N-K)個;類似RAID5;糾刪碼池減少了確保資料永續性所需的磁碟空間量,但計算量上卻比副本儲存池要更貴一些;但我們在使用糾刪碼池的時候需要注意不是所有的應用都支援糾刪碼池,比如,RGW可以使用糾刪碼儲存池,但RBD就不支援糾刪碼池,它只支援副本池;

  副本池IO

  提示:在ceph上,副本池會將一個資料物件儲存為多個副本;為此,在寫入操作時,ceph使用者端使用crush演演算法來計算物件的PG ID和Primary OSD,然後使用者端將資料寫入主OSD,主OSD根據設定的副本數、物件的名稱、儲存池名稱和叢集執行圖(Cluster Map)計算出PG的各輔助OSD,而後由主OSD將資料同步給這些輔助OSD,只有在其他輔助osd和主osd都將對應資料儲存好以後,主osd收到對應輔助osd的確認以後,才會給使用者端確認;

  糾刪碼池IO

  提示:如上圖所示使用者端把包含資料「ABCDEFGHI」的物件NYAN儲存到儲存池中時,假設糾刪碼演演算法會將內容分割為三個資料塊:第一個包含ABC,第二個為DEF,最後一個為GHI,併為這三個資料塊額外建立兩個編碼塊:第四個YXY和第五個QGC;在有著兩個編碼塊設定的儲存池中,它容許至多兩個OSD不可用而不影響資料的可用性。假設,在某個時刻OSD 1和OSD 3因故無法正常響應使用者端請求,這意味著使用者端僅能讀取到ABC、DEF和QGC,此時糾刪編碼演演算法會通過計算重那家出GHI和YXY;

  二、關於PG

  提示:PG是用於跨OSD將資料儲存在某個儲存池中的內部資料結構;相對於儲存池來說,PG是一個虛擬元件,它是物件對映到OSD時使用的虛擬層;出於規模伸縮及效能方面的考慮,Ceph將儲存池細分為歸置組,把每個單獨的物件對映到歸置組,並將歸置組分配給一個主OSD;儲存池由一系列的歸置組組成,而CRUSH演演算法則根據叢集執行圖和叢集狀態,將各PG均勻、偽隨機地分佈到叢集中的OSD之上;所謂偽隨機是指,在osd都健康數量沒有變化的情況下,同一PG始終對映相同的OSD;若某OSD失敗或需要對叢集進行重新平衡,Ceph則移動或複製整個歸置組而無需單獨定址每個物件;歸置組在OSD守護行程和Ceph使用者端之間生成了一箇中間層,CRUSH演演算法負責將每個物件動態對映到一個歸置組,然後再將每個歸置組動態對映到一個或多個OSD守護行程,從而能夠支援在新的OSD裝置上線時動態進行資料重新平衡;

  PG計數

  PG數量由管理員在建立儲存池時指定,而後由CRUSH負責建立和使用,通常,PG的數量應該是資料的合理顆粒度的子集,例如,一個包含256個PG的儲存池意味著每個PG包含大約1/256的儲存池資料;當需要將PG從一個OSD移動到另一個OSD時,PG的數量會對效能產生影響;PG數量過少,Ceph將不得不同時移動相當數量的資料,其產生的網路負載將對叢集的正常效能輸出產生負面影響;即PG過少,那麼對應一個PG儲存的資料就越多,則移動PG會佔用過多的網路頻寬,從而影響正常使用者端的使用;而在過多的PG數量場景中在移動極少量的資料時,Ceph將會佔用過多的CPU和RAM,從而對叢集的計算資源產生負面影響。即PG過多,對應每個PG維護的資料較少,但是叢集需要花費很多CPU和記憶體來維護和追蹤PG的資訊;從而造成叢集的計算資源和記憶體資源造成影響,從而進一步影響使用者端使用;所以在ceph上pg的數量不宜過多和過少;

  PG數量在群集分發資料和重新平衡時扮演著重要作用,在所有OSD之間進行資料持久儲存及完成資料分佈會需要較多的歸置組,但是它們的數量應該減少到最大效能所需的最小數量值,以節省CPU和記憶體資源;一般說來,對於有著超過50個OSD的RADOS叢集,建議每個OSD大約有50-100個PG以平衡資源使用,取得更好的資料永續性和資料分佈,更大規模的叢集中,每個OSD大約可持有100-200個PG至於應該使用多少個PG,可通過下面的公式計算後,將其值以類似於四捨五入到最近的2的N次冪;

  PG計算公式:(Total OSDs * PGPerOSD)/Replication factor => Total PGs;即一個ceph叢集總PG數量=每個OSD上的pg數量×OSD的數量,然後除以副本數量;一個RADOS叢集上可能會存在多個儲存池,因此管理員還需要考慮所有儲存池上的PG分佈後每個OSD需要對映的PG數量;即總pg數量/儲存池個數,就是平均一個儲存的PG數量;簡單講就是所有儲存池的PG數量之和應該等於通過上述公式算出來的值四捨五入到最近的2的N次冪;

  PG狀態

  依據PG當前的工作特性或工作程序所處的階段,它總是處於某個或某些個「狀態」中,最為常見的狀態應該為「active+clean」;

  PG的常見狀態

  1、Active:所謂Active狀態是指主OSD和各輔助OSD均處於就緒狀態,可正常服務於使用者端的IO請求所處於的狀態;一般,Peering操作過程完成後即會轉入Active狀態;

  2、Clean:是指主OSD和各輔助OSD均處於就緒狀態,所有物件的副本數量均符合期望,並且PG的活動集和上行集是為同一組OSD;

    活動集(Acting Set):由PG當前的主OSD和所有的處於活動狀態的輔助OSD組成,這組OSD負責執行此PG上資料物件的存取操作I/O;

    上行集(Up Set):根據CRUSH的工作方式,叢集拓撲架構的變動將可能導致PG相應的OSD變動或擴充套件至其它的OSD之上,這個新的OSD集也稱為PG的」上行集(Up Set)「,其對映到的新OSD集可能部分地與原有OSD集重合,也可能會完全不相干; 上行集OSD需要從當前的活動集OSD上覆制資料物件,在所有物件同步完成後,上行集便成為新的活動集,而PG也將轉為「活動(active)」狀態;

  3、Peering:一個PG中的所有OSD必須就它們持有的資料物件狀態達成一致,而「對等(Peering)」即為讓其OSD從不一致轉為一致的過程;

  4、Degraded:在某OSD標記為「down」時,所有對映到此OSD的PG即轉入「降級(degraded)」狀態;此OSD重新啟動並完成Peering操作後,PG將重新轉回clean;一旦OSD標記為down的時間超過5分鐘,它將被標記出叢集,而後Ceph將對降級狀態的PG啟動恢復操作,直到所有因此而降級的PG重回clean狀態;在其內部OSD上某物件不可用或悄然崩潰時,PG也會被標記為降級狀態,直至物件從某個權威副本上正確恢復;

  5、Stale:每個OSD都要週期性地向RADOS叢集中的監視器報告其作為主OSD所持有的所有PG的最新統計資料,因任何原因導致某個主OSD無法正常向監視器傳送此類報告,或者由其它OSD報告某個OSD已經down掉,則所有以此OSD為主OSD的PG將立即被標記為stale狀態;

  6、Undersized: PG中的副本數少於其儲存池定義的個數時即轉入undersized狀態,恢復和回填操作在隨後會啟動以修復其副本數為期望值;

  7、Scrubbing:各OSD還需要週期性地檢查其所持有的資料物件的完整性,以確保所有對等OSD上的資料一致;處於此類檢查過程中的PG便會被標記為scrubbing狀態,這也通常被稱作light scrubs、shallowscrubs或者simply scrubs;另外,PG還偶爾需要進行deep scrubs檢查以確保同一物件在相關的各OSD上能按位元匹配,此時PG將處於scrubbing+deep狀態;

  8、Recovering:新增一個新的OSD至儲存叢集中或某OSD宕掉時,PG則有可能會被CRUSH重新對映進而將持有與此不同的OSD集,而這些處於內部資料同步過程中的PG則被標記為recovering狀態;

  9、Backfilling:新OSD加入儲存叢集後,Ceph則會進入資料重新均衡的狀態,即一些資料物件會在程序後臺從現有OSD移到新的OSD之上,此操作過程即為backfill;

  三、關於CRUSH

  CRUSH是Controlled Replication Under Scalable Hashing的縮寫,它是一種資料分散式演演算法,類似於一致性雜湊演演算法,用於為RADOS儲存叢集控制資料分佈;在ceph上如果我們把物件直接對映到OSD上會導致二者的耦合度過於緊密;這意味著如果一個OSD的變動,可能導致整個叢集的資料的變動;所以,Ceph將一個物件對映進RADOS叢集的過程分為兩步;首先是以一致性雜湊演演算法將物件名稱對映到PG,而後而後是將PG ID基於CRUSH演演算法對映到OSD;此兩個過程都以「實時計算」的方式完成,而非傳統的查表方式,從而有效規避了任何元件被「中心化」的可能性,使得叢集規模擴充套件不再受限;

  Ceph使用者端IO的簡要工作流程

  在ceph上,使用者端存取物件時,使用者端從Ceph監視器檢索出叢集執行圖,繫結到指定的儲存池,並對儲存池上PG內的物件執行IO操作;儲存池的CRUSH規則集和PG的數量是決定Ceph如何放置資料的關鍵性因素,基於最新版本的叢集執行圖,使用者端能夠了解到叢集中的所有監視器和OSD以及它們各自的當前狀態;這對於使用者端來說,物件儲存在那個位置是一無所知的;執行物件的存取操作時,使用者端需要輸入的是物件標識和儲存池名稱;使用者端需要在儲存池中儲存命名物件時,它將物件名稱、物件名稱的雜湊碼、儲存池中的PG數量和儲存池名稱作為輸入,而後由CRUSH計算出PG的ID及此PG的主OSD;通過將物件標識進行一致性雜湊運算得到的雜湊值與PG點陣圖掩碼進行」與「運算得到目標PG,從而得出目標PG的ID(pg_id),完成由Object至PG的對映; 而後,CRUSH演演算法便將以此pg_id、CRUSH執行圖和歸置規則(Placement Rules)為輸入引數再次進行計算,並輸出一個確定且有序的目標儲存向量列表(OSD列表),從而完成從PG至OSD的對映;

  Ceph使用者端計算PG_ID的步驟

  1、首先使用者端輸入儲存池名稱及物件名稱,例如,pool = pool1以及object-id = obj1;

  2、獲取物件名稱並通過一致性雜湊演演算法對其進行雜湊運算,即hash(o),其中o為物件名稱;

  3、將計算出的物件標識雜湊碼與PG點陣圖掩碼進行「與」運算獲得目標PG的識別符號,即PG_ID,如1701;計算公式為pgid=func(hash(o)&m,r),其中,變數o是物件識別符號,變數m是當前儲存池中PG的點陣圖掩碼,變數r是指複製因子,用於確定目標PG中OSD數量;

  4、CRUSH根據叢集執行圖計算出與目標PG對應的有序的OSD集合,並確定出其主OSD;

  5、使用者端獲取到儲存池名稱對應的數位標識,例如,儲存池「pool1」的數位標識11;

  6、使用者端將儲存池的ID新增到PG ID,例如,11.1701;

  7、使用者端通過直接與PG對映到的主OSD通訊來執行諸如寫入、讀取或刪除之類的物件操作;

  簡單來講,ceph使用者端存取物件資料的過程就是,先提取使用者要存入的物件資料的名稱和儲存到那個儲存池之上;然後通過一致性雜湊演演算法計算出物件名稱的hash值,然後把這個hash值和在對應儲存池PG點陣圖掩碼做「與」運算得到目標PG的識別符號,即PG ID;有了PG_ID,再根據CRUSH演演算法結合叢集執行圖和PG對應的OSD集合,最終把PG對應的主OSD確認,然後將對應osd的資訊返回給使用者端,然後使用者端拿著這些資訊主動聯絡osd所在主機進行資料的存取;這一過程中,沒有傳統的查表,查資料庫之類的操作;全程都是通過計算來確定對應資料儲存路徑;這也就規避了傳統查表或查資料庫的方式給叢集帶來的效能瓶頸的問題;