玩轉 Ceph 的正確姿式
本文先介紹 Ceph, 而後會聊到一些正確使用 Ceph 的姿式;在集羣規模小的時候,Ceph 怎麼玩都沒問題;但集羣大了(到PB級別),這些準則但是保證集羣健康運行的不二法門;算法
Ceph 最初的目標是作一個分佈式文件系統,直到如今這個目標也不能算完美實現;目前官網上對它的文件系統仍是謹慎推薦的態度(不建議對線上核心業務部署);shell
業界使用 Ceph ,大可能是用它的對象存儲;後端
Ceph 支持三種存儲接口:對象存儲 RGW(rados gateway)、塊存儲 RBD(rados block device) 和文件存儲 CephFS;服務器
這三個接口只是在客戶端的封裝庫不一樣,到服務端了都是對象存儲;
分佈式
Ceph 對象存儲服務提供了 REST 風格的 API ,它有與 Amazon S3 和 OpenStack Swift 兼容的接口。也就是一般意義的鍵值存儲,其接口就是簡單的GET、PUT、DEL和其餘擴展;性能
RBD 是經過librbd庫對應用提供塊存儲,主要面向雲平臺的虛擬機提供虛擬磁盤;RBD相似傳統的SAN存儲,提供數據塊級別的訪問;操作系統
目前 RBD 提供了兩個接口,一種是直接在用戶態實現, 經過 QEMU Driver 供 KVM 虛擬機使用。 另外一種是在操做系統內核態實現了一個內核模塊。經過該模塊能夠把塊設備映射給物理主機,由物理主機直接訪問。設計
Ceph 文件系統服務提供了兼容 POSIX 的文件系統,能夠直接掛載爲用戶空間文件系統。它跟傳統的文件系統如Ext4是一個類型,區別在於分佈式存儲提供了並行化的能力;3d
除了以上3種存儲接口, 還能夠直接使用 librados 的原生接口,直接和RADOS通訊;code
原生接口的優勢是是它直接和和應用代碼集成,操做文件很方便;但它的問題是它不會主動爲上傳的數據分片;一個1G的大對象上傳,落到 Ceph 的存儲磁盤上就是1G的文件;
而以上三個接口是具備分片功能(即:條帶化 file-striping)
PS:兩個對象的區分
須要說明下,這裏提到兩個對象的概念:一個是 RGW中的對象存儲,一個是 Ceph 的後端存儲的對象;這兩個須要區分:
第一個對象面向用戶,是用戶接口能訪問到的對象;
第二個對象是ceph 服務端操做的對象;
eg:使用RGW接口,存放一個1G的文件,在用戶接口看到的就是存放了一個對象(1);而經過RGW 分片成多個對象(2)後最終存儲到磁盤上;
服務端 RADOS 集羣主要由兩種節點組成:一種是爲數衆多的、負責完成數據存儲和維護功能的OSD(Object Storage Device),另外一種則是若干個負責完成系統狀態檢測和維護的monitor。
Monitor 集羣提供了整個存儲系統的節點信息等全局的配置信息,經過 Paxos 算法保持數據的一致性。
Pool是存儲對象的邏輯分區,它規定了數據冗餘的類型和對應的副本分佈策略;支持兩種類型:副本(replicated)和 糾刪碼( Erasure Code);目前咱們公司內部使用的Pool都是副本類型(3副本);
PG( placement group)是一個放置策略組,它是對象的集合,該集合裏的全部對象都具備相同的放置策略;簡單點說就是相同PG內的對象都會放到相同的硬盤上; PG是 ceph的核心概念, 服務端數據均衡和恢復的最小粒度就是PG;
OSD是負責物理存儲的進程,通常配置成和磁盤一一對應,一塊磁盤啓動一個OSD進程;
下面這張圖形象的描繪了它們之間的關係:
講究的PG
一個Pool裏設置的PG數量是預先設置的,PG的數量不是隨意設置,須要根據OSD的個數及副本策略來肯定:
Total PGs = ((Total_number_of_OSD * 100) / max_replication_count) / pool_count
線上儘可能不要更改PG的數量,PG的數量的變動將致使整個集羣動起來(各個OSD之間copy數據),大量數據均衡期間讀寫性能降低嚴重;
良好的工程實踐建議(掉坑後的教訓):
預先規劃Pool的規模,設置PG數量;一旦設置以後就再也不變動;後續須要擴容就以 Pool 爲維度爲擴容,經過新增Pool來實現(Pool經過 crushmap實現故障域隔離);
查找對象在集羣中的存儲的位置,具體分爲兩步:
第一步,對象到PG的映射;將對象的id 經過hash映射,而後用PG總數對hash值取模獲得pg id:
pg_ id = hash( object_ id ) % pg_num
第二步,PG到osd列表映射; 經過crush算法計算PG 上的對象分佈到哪些OSD硬盤上;
CRUSH(PG_ID) =⇒ OSD
CRUSH算法是 ceph的精華所在;
先看看crush算法的但願達成的目標:
簡單說下crush算法的過程:
第一步輸入PG id、可供選擇的OSD id 列表,和一個常量,經過一個僞隨機算法,獲得一個隨機數,僞隨機算法保證了同一個key老是獲得相同的隨機數,從而保證每次計算的存儲位置不會改變;
CRUSH_HASH( PG_ID, OSD_ID, r ) = draw
第二步將上面獲得的隨機數和每一個OSD的權重相乘,而後挑出乘積最大的那個OSD;
( draw &0xffff ) * osd_weight = osd_straw
在樣本容量足夠大以後,這個隨機數對挑中的結果再也不有影響,起決定性影響的是OSD的權重,也就是說,OSD的權重越大,被挑中的機率越大。
到這裏了咱們再看看crush算法如何達成的目標:
經過隨機算法讓數據均衡分佈,乘以權重讓挑選的結果考慮了權重;而若是出現故障OSD,只須要恢復這個OSD上的數據,不在這個節點上的數據不需移動;
聊到這裏,crush算法的優缺點就明顯了:
優勢以下:
缺點呢:
看清楚了尋址的過程,就明白爲啥PG不能輕易變動了;PG是尋址第一步中的取模參數,變動PG會致使對象的PG id 都發生變化,從而致使整個集羣的數據遷移;
這裏只是作個引子,關於crush算法,這篇文章講的通俗直白,有興趣的移步:大話Ceph--CRUSH那點事兒
Ceph 是Sega本人的博士論文做品, 其博士論文被整理成三篇短論文,其中一篇就是 CRUSH,
CRUSH論文標題爲《CRUSH: Controlled, Scalable, Decentralized Placement of Replicated Data》,介紹了CRUSH的設計與實現細節。
(PS:另外兩篇是 RADOS和 CephFS, 分別講 Ceph 的服務器實現和 Ceph 文件系統的細節實現)
剛開始接觸 Ceph,一般會忽略 crushmap,由於即便對它不作任何設置,也不影響咱們的正常使用;
一旦集羣大了,沒有它集羣就處於一個危險的運行狀態中;
沒有故障域的劃分,整個集羣就處於一個未隔離的資源池中;
一個對象存過去,可能落在 500個OSD硬盤的任意三個上;
若是一塊硬盤壞了,可能帶來的是全局影響(副本copy,這個硬盤上丟失的PG副本可能分佈在全局各個硬盤上);
使用crushmap 將整個集羣的OSD 劃分爲一個個故障域,相似將一個集羣按業務劃分紅爲了多個小集羣;每一個Pool 只會用到特定的 OSD,這樣,一旦某個OSD 損壞,影響的只是某個業務的某個Pool,將故障的範圍控制在一個很小的範圍內。
良好的工程實踐建議:
使用crushmap 劃分故障域,將pool限制在特定的osd list上,osd的損壞只會引發這個pool內的數據均衡,不會形成全局影響;
對象是數據存儲的基本單元, 通常默認 4MB 大小(這裏指的是RADOS的底層存儲的對象,非RGW接口的對象)。
對象的組成分爲3部分:key 、value、元數據;
對於大文件的存儲,Ceph 提供的客戶端接口會對大文件分片(條帶化)後存儲到服務端;這個條帶化操做是在客戶端接口程序完成的,在 Ceph 存儲集羣內存儲的那些對象是沒條帶化的。客戶端經過 librados 直接寫入 Ceph 存儲的數據不會分片。
良好的工程實踐建議:
對於對象存儲,只使用 Ceph 提供的 RGW 接口, 不使用 librados原生接口;不只有分片功能,擴展也更容易(RGW是無狀態的,可水平擴展);大量大對象直接存放到 Ceph中會影響 Ceph 穩定性(存儲容量達到60%後);
上線 Ceph 前,先規劃將來一年的預期使用量,爲每一個 pool 一次性設置 PG以後再也不變動; 使用crushmap 設置故障域隔離,將磁盤故障後帶來的數據平衡控制在一個小的範圍以內。接口方面推薦只使用Ceph 提供的RGW 接口,不使用 librados原生接口。作好這些, 你的 Ceph 用起來會省心不少。