本系列文章會深刻研究 Ceph 以及 Ceph 和 OpenStack 的集成:php
(1)安裝和部署html
(2)Ceph RBD 接口和工具前端
(3)Ceph 物理和邏輯結構node
(4)Ceph 的基礎數據結構mysql
(5)Ceph 與 OpenStack 集成的實現linux
(6)QEMU-KVM 和 Ceph RBD 的 緩存機制總結ios
(7)Ceph 的基本操做和常見故障排除方法git
(8)關於Ceph PGsgithub
1. Ceph 集羣的物理結構
1.1 Ceph 內部集羣
從前一篇文章 咱們知道,從物理上來說,一個 Ceph 集羣內部其實有幾個子集羣存在:算法
(1)MON(Montior)集羣:MON 集羣有由少許的、數目爲奇數個的 Monitor 守護進程(Daemon)組成,它們負責經過維護 Ceph Cluster map 的一個主拷貝(master copy of Cluster map)來維護整 Ceph 集羣的全局狀態。理論上來說,一個 MON 就能夠完成這個任務,之因此須要一個多個守護進程組成的集羣的緣由是保證高可靠性。每一個 Ceph node 上最多隻能有一個 Monitor Daemon。
root@ceph1:~# ps -ef | grep ceph-mon root 964 1 0 Sep18 ? 00:36:33 /usr/bin/ceph-mon --cluster=ceph -i ceph1 -f
實際上,除了維護 Cluster map 之外,MON 還承擔一些別的任務,好比用戶校驗、日誌等。詳細的配置能夠參考 MON 配置。
(2)OSD (Object Storage Device)集羣:OSD 集羣由必定數目的(從幾十個到幾萬個) OSD Daemon 組成,負責數據存儲和複製,向 Ceph client 提供存儲資源。每一個 OSD 守護進程監視它本身的狀態,以及別的 OSD 的狀態,而且報告給 Monitor;並且,OSD 進程負責在數據盤上的文件讀寫操做;它還負責數據拷貝和恢復。在一個服務器上,一個數據盤有一個 OSD Daemon。
root@ceph1:~# ps -ef | grep ceph-osd root 1204 1 0 Sep18 ? 00:24:39 /usr/bin/ceph-osd --cluster=ceph -i 3 -f root 2254 1 0 Sep18 ? 00:20:52 /usr/bin/ceph-osd --cluster=ceph -i 6 -f
(3)若干個數據盤:一個Ceph 存儲節點上能夠有一個或者多個數據盤,每一個數據盤上部署有特定的文件系統,好比 xfs,ext4 或者 btrfs,由一個 OSD Daemon 負責照顧其狀態以及向其讀寫數據。
Disk /dev/vda: 21.5 GB, 21474836480 bytes /dev/vda1 1 41943039 20971519+ ee GPT Disk /dev/vdb: 32.2 GB, 32212254720 bytes /dev/vdb1 1 62914559 31457279+ ee GPT
(MON 和 OSD 能夠共同在一個節點上,也能夠分開)
關於Ceph 支持的數據盤上的 xfs、ext4 和 btrfs 文件系統,它們都是日誌文件系統(其特色是文件系統將沒提交的數據變化保存到日誌文件,以便在系統崩潰或者掉電時恢復數據),三者各有優點和劣勢:
- btrfs (B-tree 文件系統) 是個很新的文件系統(Oracel 在2014年8月發佈第一個穩定版),它將會支持許多很是高大上的功能,好比 透明壓縮( transparent compression)、可寫的COW 快照(writable copy-on-write snapshots)、去重(deduplication )和加密(encryption )。所以,Ceph 建議用戶在非關鍵應用上使用該文件系統。 更多的參考包括 (1)(2)(3)。
- xfs 和 btrfs 相比較ext3/4而言,在高伸縮性數據存儲方面具備優點。
- Ceph 的這篇文章 明確推薦在生產環境中使用 XFS,在開發、測試、非關鍵應用上使用 btrfs。
- 網上有不少的文章比較這幾種文件系統,包括:
(4)要使用 CephFS,還須要 MDS 集羣,用於保存 CephFS 的元數據
(5)要使用對象存儲接口,還須要 RADOS Gateway, 它對外提供REST接口,兼容S3和Swift的API。
1.2 Ceph 網絡結構
Ceph 使用以太網鏈接內部各存儲節點以及鏈接 client 和集羣。Ceph 推薦使用兩個網絡:
- 前端(北向)網絡( a public (front-side) network)鏈接客戶端和集羣
- 後端/東西向網絡 (a cluster (back-side) network)來鏈接 Ceph 各存儲節點
下圖(來源)顯示了這種網絡拓撲結構:
這麼作,主要是從性能(OSD 節點之間會有大量的數據拷貝操做)和安全性(兩網分離)考慮。你能夠在 Ceph 配置文件的 [global] 部分配置兩個網絡:
public network = {public-network/netmask}
cluster network = {cluster-network/netmask}
具體能夠參考:
- Deploying Ceph with High Performance Networks, Architectures and benchmarks for Block Storage Solutions
- Deploying Ceph with High Performance Networks
- CHAPTER 2. NETWORKING RECOMMENDATIONS
- Ceph with a cluster and public network on IPv6 談到了 IPV6 的支持。
1.3 RDB Cache (緩存)
1.3.1 常見的 Write Cache 種類
緩存種類 | 說明 | 優劣勢 | 適合場景 |
Write-through(直寫) | 這種緩存方式在寫 I/O 時把數據放入緩存,同時直接寫入底層的持久存儲,而後再向主機確認寫入操做完成。 | 安全地保存數據,從緩存讀,減小了讀操做的延遲,可是寫操做 的延遲沒獲得優化 | 適合於寫較少,可是頻繁度的應用 |
Write-back (回寫) | 數據直接寫入緩存,而後向主機返回寫入完成。 | 對頻繁寫應用減小了寫的延遲,可是有數據丟失風險 | 對讀寫混合型應用有優點,可是須要考慮數據保護 |
緩存的一般位置分類:
- 服務器(主機)上:RAID 卡或者 HBA 卡上作緩存。
- VMM 內:在 Hypervisor 上作緩存。
- 客戶機操做系統內:以 Windows 2012 爲例,它提供 write-back 緩存機制。
更多資料,能夠參考 Cache is vital for application deployment, but which one to choose。
1.3.2 Ceph RBD 緩存
默認狀況下,Ceph RBD 是不使用緩存的,讀和寫直接到 Ceph 集羣中的存儲,寫只有在全部 replica 上寫都完成後纔給客戶端返回寫完成。Ceph 在較新的版本上陸續添加了 RBD 緩存支持:
- 從 0.46 版本開始,Ceph 支持 write-back 緩存,你能夠在 ceph.conf 文件的 [client] 部分添加 rbd cache = true 來使得 write-back 緩存生效。這時候,寫幾乎是當即返回,可是數據只有在被 flushed 後才寫入到實際存儲。
- 從 0.47 版本開始,Ceph 支持 write-through 緩存機制。你只須要再添加配置項 rbd cache max dirty = 0 便可。
- 從 0.60 版本開始,Ceph 支持 rbd cache writethrough until flush 配置項。設置它爲 true 時,會使得 write-through 機制變得更加安全,由於老的客戶機操做系統(2.6.32 內核版本以前)可能不支持 flush 操做。所以,在設置了該配置項爲 true 時,即便用戶設置了使用 write-through 機制,Ceph 也會自動使用 write-back 機制,直到它收到第一個 flush 指令後才真正使用 write-through。
可見 RBD 緩存是在客戶端作的,見以下圖示:
更多信息,能夠參考個人另外一篇文章:QEMU-KVM 和 Ceph RBD 的 緩存機制總結
1.3.3 Cache tiering (緩存分層)
Ceph 還支持在集羣段作緩存分層。其原理是,在較快的磁盤好比 SSD 上創建一個 cache pool,在創建存儲池(storage pool)和它之間的 cache 關係,設置必定的緩存策略,實現相似於在客戶端緩存一樣的效果。
更多的信息及詳細配置,參見 CACHE TIERING 和 Intel 的文章。
1.3.4 RBD Cache 和 Cache Tiering 的區別
從上面的分析能夠看出來,二者的區別在於緩存的位置不一樣:
- Cache tiering 是 RADOS 層在 OSD 端進行數據緩存,也就是說不管是塊存儲、對象存儲仍是文件存儲均可以使用tier來提升讀寫速度
- RBD Cache是 rbd 層在客戶端的緩存,也就是隻支持塊存儲。
Rbd cache是 客戶端的緩存,當多個客戶端使用同個塊設備時,存在客戶端數據不一致的問題。舉個例子,用戶A向塊設備寫入數據後,數據停留在客戶本身的緩存中,沒有當即刷新到磁盤,因此其它用戶讀取不到A寫入的數據。可是tier不存在這個問題,由於全部用戶的數據都直接寫入到 ssd,用戶讀取數據也是在ssd中讀取的,因此不存在客戶端數據不一致問題。
通常地,Tier 使用 SSD 作緩存,而 Rbd cache 只能使用內存作緩存。SSD和內存有兩個方面的差異,一個是讀寫速度、另外一個是掉電保護。掉電後內存中的數據就丟失了,而ssd中的數據不會丟失。
2. Ceph 集羣的邏輯結構(以RBD爲例)
Ceph 集羣的邏輯結構由 Pool 和 PG (Placement Group)來定義。
2.1 Pool
一個 Pool 是 Ceph 中的一些對象的邏輯分組,它並不表示一個連續的分區,而只是一個邏輯概念,相似於將二進制數據打了tag同樣而後根據tag歸類同樣。它相似於 LVM 中的 Volume Group,相似於一個命名空間。RBD Image 相似於 LVM 中的 Logical Volume。RBD Image 必須且只能在一個 Pool 中。Pool 由若干個PG組成。其屬性包括:
- 全部性和訪問權限
- 對象副本數目
- PG 數目
- CRUSH 規則集合
Ceph Pool 有兩種類型:
- Replicated pool:拷貝型 pool,經過生成對象的多份拷貝來確保在部分 OSD 丟失的狀況下數據不丟失。這種類型的 pool 須要更多的裸存儲空間,可是它支持全部的 pool 操做。
- Erasure-coded pool:糾錯碼型 pool(相似於 Software RAID)。在這種 pool 中,每一個數據對象都被存放在 K+M 個數據塊中:對象被分紅 K 個數據塊和 M 個編碼塊;pool 的大小被定義成 K+M 塊,每一個塊存儲在一個 OSD 中;塊的順序號做爲 object 的屬性保存在對象中。可見,這種 pool 用更少的空間實現存儲,即節約空間;糾刪碼實現了高速的計算,但有2個缺點,一個是速度慢,一個是隻支持對象的部分操做(好比:不支持局部寫)。這篇文章 詳細介紹了其原理和細節。
Pool 提供以下的能力:
- Resilience(彈力):即在確保數據不丟失的狀況容許必定的 OSD 失敗,這個數目取決於對象的拷貝(copy/replica)份數。對拷貝型 pool 來講,Ceph 中默認的拷貝份數是2,這意味着除了對象自身外,它還有一個另外的備份。你能夠本身決定一個 Pool 中的對象的拷貝份數。
- Placement Groups(放置組):Ceph 使用 PG 來組織對象,這是由於對象可能成千上萬,所以一個一個對象來組織的成本是很是高的。PG 的值會影響 Ceph 集羣的行爲和數據的持久性。你能夠設置 pool 的 PG 數目。推薦的配置是,每一個 OSD 大概 100 個 PG。
- CRUSH Rules (CRUSH 規則):數據映射的策略。系統默認提供 「replicated_ruleset"。用戶能夠自定義策略來靈活地設置 object 存放的區域。好比能夠指定 pool1中全部objecst放置在機架1上,全部objects的第1個副本放置在機架1上的服務器A上,第2個副本分佈在機架1上的服務器B上。 pool2中全部的object分佈在機架二、三、4上,全部Object的第1個副本分佈在機架2的服務器上,第2個副本分佈在機架3的服 器上,第3個副本分佈在機架4的服務器上。詳細的信息能夠參考這些文檔 (1)(2)(3)(4)。
- Snapshots(快照):你能夠對 pool 作快照。
- Set Ownership:設置 pool 的 owner 的用戶 ID。
- Ceph 集羣建立後,默認建立了 data,metadata 和 rbd 三個存儲池。
2.2 (Placement Group)PG
2.2.1 概念
PG 概念很是複雜,主要有以下幾點:
- PG 也是對象的邏輯集合。同一個PG 中的全部對象在相同的 OSD 上被複制。
- PG 聚合一部分對象成爲一個組(group),這個組被放在某些OSD上(place),合起來就是 Placemeng Group (放置組)了。
- Epoch:PG map 的版本號,它是一個單調遞增的序列。
- Peering:見下文的狀態(8)描述。詳細過程請參閱 Ceph:pg peering過程分析。
- Acting set:支持一個 PG 的全部 OSD 的有序列表,其中第一個 OSD 是主OSD,其他爲次。acting set 是 CRUSH 算法分配的,可是不必定已經生效了。
- Up set:某一個 PG map 歷史版本的 acting set。在大多數狀況下,acting set 和 up set 是一致的,除非出現了 pg_temp。
- Current Interval or Past Interval:若干個連續的版本號,這些版本中 acting 和 up set 保持不變。
- PG temp:在Ceph 正在往主 OSD 回填數據時,這個主OSD是不能提供數據服務的,這時候,它會向 MON 申請一個臨時的 acting set,這就是 PG temp。舉個例子,如今 acting set 是[0,1,2],出現了一點事情後,它變爲 [3,1,2],此時 osd.3 仍是空的所以它沒法提供數據服務所以它還須要等待backfilling過程結束,所以,它會向 MON 申請一個臨時的 set 好比 [1,2,3],此時將由 osd.1 提供數據服務。回填過程結束後,該臨時 set 會被丟棄,從新由 osd.3 提供服務。
- 主 (primary) OSD:在 acting set 中的首個 OSD,負責接收客戶端寫入數據;默認狀況下,提供數據讀服務,可是該行爲能夠被修改。它還負責 peering 過程,以及在須要的時候申請 PG temp。
- 次 (replica)OSD:在 acting set 中的除了第一個之外的其他 OSD。
- 流浪 (stray) OSD:已經不是 acting set 中了,可是尚未被告知去刪除數據 的 OSD。
- PG 的 acting set 是由 CRUSH 算法根據 CRUSH Rules 動態地計算得出的。
2.2.2 特色
其主要特色以下:
- 基本特色
- PG 肯定了 pool 中的對象和 OSD 之間的映射關係。一個 object 只會存在於一個 PG 中,可是多個 object 能夠在同一個 PG 內。
- Pool 的 PG 數目是建立 pool 時候指定的,Ceph 官方有推薦的計算方法。其值與 OSD 的總數的關係密切。當Ceph 集羣擴展 OSD 增多時,根據須要,能夠增長 pool 的 PG 數目。
- 對象的副本數目,也就是被拷貝的次數,是在建立 Pool 時指定的。該分數決定了每一個 PG 會在幾個 OSD 上保存對象。若是一個拷貝型 Pool 的size(拷貝份數)爲 2,它會包含指定數目的 PG,每一個 PG 使用兩個 OSD,其中,第一個爲主 OSD (primary),其它的爲從 OSD (secondary)。不一樣的 PG 可能會共享一個 OSD。
- Ceph 引入 PG 的目的主要是爲了減小直接將對象映射到 OSD 的複雜度。
- PG 也是Ceph 集羣作清理(scrubbing)的基本單位,也就是說數據清理是一個一個PG來作的。
- PG 和 OSD 之間的映射關係由 CRUSH 決定,而它作決定的依據是 CRUSH 規則(rules)。CRUSH 將全部的存儲設備(OSD)組織成一個分層結構,該結構能區分故障域(failure domain),該結構中每一個節點都是一個 CRUSH bucket。詳細狀況請閱讀 CRUSH 相關的文檔。
- PG 和 OSD 的關係是動態的:
- 一開始在 PG 被建立的時候,MON 根據 CRUSH 算法計算出 PG 所在的 OSD。這是它們之間的初始關係。
- Ceph 集羣中 OSD 的狀態是不斷變化的,它會在以下狀態之間作切換:
- up:守護進程運行中,可以提供IO服務;
- down:守護進程不在運行,沒法提供IO服務;
- in:包含數據;
- out:不包含數據
- 部分 PG 和 OSD 的關係會隨着 OSD 狀態的變化而發生變化。
- 當新的 OSD 被加入集羣后,已有OSD上部分PG將可能被挪到新OSD上;此時PG 和 OSD 的關係會發生改變。
- 當已有的某 OSD down 了並變爲 out 後,其上的 PG 會被挪到其它已有的 OSD 上。
- 可是大部分的 PG 和 OSD 的關係將會保持不變,在狀態變化時,Ceph 儘量只挪動最少的數據。
- 客戶端根據 Cluster map 以及 CRUSH Ruleset 使用 CRUSH 算法查找出某個 PG 所在的 OSD 列表(實際上是 up set)。
-
PG-Object-OSD 的關係以下圖所示:
- PG 的建立過程(詳細過程請參考 PG 的建立過程):
- MON 節點上有PGMonitotor,它發現有 pool 被建立後,判斷該 pool 是否有 PG。若是有PG,則一一判斷這些 PG 是否已經存在,若是不存在,則開始下面的建立 PG 的過程。
- 建立過程的開始,設置PG 狀態爲 Creating,並將它加入待建立PG隊列 creating_pgs,等待被處理。
- 開始處理後,使用 CRUSH 算法根據當前的 OSD map 找出來 up/acting set,加入 PG map 中以這個 set 中 OSD 爲索引的隊列 creating_pgs_by_osd。(看起來只會加入到主OSD的隊列中)。
- 隊列處理函數將該 OSD 上須要建立的 PG 合併,生成消息MOSDPGCreate,經過消息通道發給 OSD。
- OSD 收到消息字爲 MSG_OSD_PG_CREATE 的消息,獲得消息中待建立的 PG 信息,判斷類型,並獲取該PG的其它OSD,加入隊列 creating_pgs (彷佛是由主 OSD 負責發起建立次 OSD 上的PG),再建立具體的 PG。
- PG 被建立出來之後,開始 Peering 過程。
- PG 值的肯定:建立 pool 時須要肯定其 PG 的數目,在 pool 被建立後也能夠調整該數字,該數目會影響到:
- 數據的持久性:考慮pool 的 size 爲 3,代表每一個 PG 會將數據存放在 3 個 OSD 上。當一個 OSD down 了後,必定間隔後將開始 recovery 過程,recovery結束前,有部分 PG 的數據將只有兩個副本。這時候和須要被恢復的數據的數量有關係,若是該 OSD 上的 PG 過多,則花的時間將越長,風險將越大。若是此時再有一個 OSD down 了,那麼將有一部分 PG 的數據只有一個副本,recovery 過程繼續。若是再出現第三個 OSD down 了,那麼可能會出現部分數據丟失。可見,每一個 OSD 上的PG數目不宜過大,不然,會下降數據的持久性。這也就要求在添加 OSD 後,PG 的數目在須要的時候也須要相應增長。
-
數據的均勻分佈性:CRUSH 算法會僞隨機地保證 PG 被選中來存放客戶端的數據,它還會盡量地保證全部的 PG 均勻分佈在全部的 OSD 上。比方說,有10個OSD,可是隻有一個 size 爲 3 的 pool,它只有一個 PG,那麼10個 OSD 中將只有三個 OSD 被用到。可是 CURSH 算法在計算的時候不會考慮到OSD上已有數據的大小。比方說,100萬個4K對象共4G均勻地分佈在10個OSD上的1000個PG內,那麼每一個 OSD 上大概有400M 數據。再加進來一個400M的對象(假設它不會被分割),那麼有三塊 OSD 上將有 400M + 400M = 800 M 的數據,而其它七塊 OSD 上只有 400M 數據。
- 資源消耗:PG 做爲一個邏輯實體,它須要消耗必定的資源,包括內存,CPU 和帶寬。太多 PG 的話,則佔用資源會過多。
- 清理時間:Ceph 的清理工做是以 PG 爲單位進行的。若是一個 PG 內的數據太多,則其清理時間會很長。
那如何肯定一個 Pool 中有多少 PG?Ceph 不會本身計算,而是給出了一些參考原則,讓 Ceph 用戶本身計算:
- 少於 5 個 OSD, 建議設爲 128
- 5 到 10 個 OSD,建議設爲 512
- 10 到 50 個 OSD,建議設爲 4096
- 50 個 OSD 以上,就須要有更多的權衡來肯定 PG 數目
- 你可使用 pgcalc 工具
- PG 的狀態也是不斷變化的,其主要狀態包括:
- Creating 建立中:PG 正在被建立。
- Peering 對等互聯:表示一個過程,該過程當中一個 PG 的全部 OSD 都須要互相通訊來就PG 的對象及其元數據的狀態達成一致。處於該狀態的PG不能響應IO請求。Peering的過程其實就是pg狀態從初始狀態而後到active+clean的變化過程。一個 OSD 啓動以後,上面的pg開始工做,狀態爲initial,這時進行比對全部osd上的pglog和pg_info,對pg的全部信息進行同步,選舉primary osd和replica osd,peering過程結束,而後把peering的結果交給recovering,由recovering過程進行數據的恢復工做。
- Active 活動的:Peering 過程完成後,PG 的狀態就是 active 的。此狀態下,在主次OSD 上的PG 數據都是可用的。
- Clean 潔淨的:此狀態下,主次 OSD 都已經被 peered 了,每一個副本都就緒了。
- Down:PG 掉線了,由於存放其某些關鍵數據(好比 pglog 和 pginfo,它們也是保存在OSD上)的副本 down 了。
- Degraded 降級的:某個 OSD 被發現中止服務 (down)了後,Ceph MON 將該 OSD 上的全部 PG 的狀態設置爲 degraded,此時該 OSD 的 peer OSD 會繼續提供數據服務。這時會有兩種結果:一是它會從新起來(好比重啓機器時),須要再通過 peering 過程再到clean 狀態,並且 Ceph 會發起 recovery (恢復)過程,使該 OSD 上過時的數據被恢復到最新狀態;二是 OSD 的 down 狀態持續 300 秒後其狀態被設置爲 out,Ceph 會選擇其它的 OSD 加入 acting set,並啓動回填(backfilling)數據到新 OSD 的過程,使 PG 副本數恢復到規定的數目。詳情能夠參考 PG 的數據恢復過程。
- Recovering 恢復中:一個 OSD down 後,其上面的 PG 的內容的版本會比其它OSD上的 PG 副本的版本落後。在它重啓以後(好比重啓機器時),Ceph 會啓動 recovery 過程來使其數據獲得更新。
- Backfilling 回填中:一個新 OSD 加入集羣后,Ceph 會嘗試級將部分其它 OSD 上的 PG 挪到該新 OSD 上,此過程被稱爲回填。與 recovery 相比,回填(backfill)是在零數據的狀況下作全量拷貝,而恢復(recovery)是在已有數據的基礎上作增量恢復。
- Remapped 重映射:每當 PG 的 acting set 改變後,就會發生從舊 acting set 到新 acting set 的數據遷移。此過程結束前,舊 acting set 中的主 OSD 將繼續提供服務。一旦該過程結束,Ceph 將使用新 acting set 中的主 OSD 來提供服務。
- Stale 過時的:OSD 每隔 0.5 秒向 MON 報告其狀態。若是由於任何緣由,主 OSD 報告狀態失敗了,或者其它OSD已經報告其主 OSD down 了,Ceph MON 將會將它們的 PG 標記爲 stale 狀態。
- PG 的全部的狀態是一個相似樹形的結構,每一個狀態可能存在子狀態,子狀態還可能存在子狀態,以下圖所示:
(來源)
更多的狀態請參考 http://docs.ceph.com/docs/master/rados/operations/pg-states/。實際上 PG 的狀態能夠是以上這些狀態的組合,好比:
[root@ceph-mon ~]# ceph -s
cluster c5476875-2a04-41b7-a4e8-421133c69ac8 health HEALTH_WARN 28 pgs backfill #回填,有新的 OSD 被加入了? 79 pgs degraded #降級,有 OSD down 了? 10 pgs recovering #恢復中 42 pgs recovery_wait #等待恢復 80 pgs stuck unclean #有 80個 PG 一直處於 unclean 狀態 27 pgs undersized #GP 的副本數小於pool size recovery 4814/27835 objects degraded (17.295%) recovery 2047/27835 objects misplaced (7.354%)
注意,只有當全部的 PG 都是 active + clean 狀態時,集羣的狀態纔是 HEALTH_OK 的。
- 清理 scrubbing:Ceph 以 PG 爲單位進行數據清理,以保證數據的完整性,它的做用相似於文件系統的 fsck 工具。
- 有兩種比較方式:(1)light scrubbing:比較對象的size和屬性,通常天天進行 (2)deep scrubbing:讀取對象的數據,比較檢驗碼,通常每週進行。
- Ceph 的 OSD 按期啓動 scrub 線程來掃描部分對象,經過與其餘副本比對來發現是否一致,若是存在不一致,拋出異常提示用戶手動解決。管理員也能夠手工發起。
- Scrub 以 PG 爲單位,對於每個PG,Ceph 分析該 PG 下全部的對象, 產生一個相似於元數據信息摘要的數據結構,如對象大小,屬性等,叫scrubmap, 比較主與副scrubmap,來保證是否是有object 丟失或者不匹配。
-
Scrub 方式分紅兩種, classic vs. chunky。Scrub 流程須要提取對象的校驗信息而後跟其餘副本的校驗信息對比,這期間被校驗對象的數據是不能被修改的,因此 write 請求會被 block. 因爲 PG 可能包含成千上萬 objects, chunk 每一次的比較只取其中一部分 objects 來比較,這樣只 block一小部分object的write請求。這是在ceph的Bobtail(v0.56 Jan 1 2013)引入的feature,稱爲chunky scrub。Classic scrub 沒有引入chunk, 會block全部的write請求。
- 該機制對保證數據的完整性很是重要,可是也會消耗大量的集羣資源,block 住一部分對象的寫入操做,下降集羣的性能,特別是當一個OSD服務器上多個OSD同時進行深度清理的時候。這篇文章 Ceph Deep-Scrubbing Impact Study 說當有三個深度清理線程發生時,性能有明顯的降低。
2.2.3 PG 設計帶來的一些運維問題
引用自原文 Ceph運維告訴你分佈式存儲的那些「坑」
(1)擴容粒度
Ceph在實踐中,擴容受「容錯域」制約,一次只能擴一個「容錯域」。容錯域就是:副本隔離級別,即同一個replica的數據,放在不一樣的磁盤/機器/Rack/機房。默認是機器,一般設爲機架。
Ceph擴容須要對PGs進行調整。正由於這個調整,致使Ceph受「容錯域」制約。
例如:有一個PG,是3副本,Ceph集羣有一個配置是PG要向外提供正常服務,至少有2個完整的副本。而當這個數據pool的容錯域是host時,同時擴容2臺機器,一些PG就有可能把3副本中的2個都映射到2臺新機器上去。而這2個副本都是新副本,都沒有完整的最新數據。剩下的一個副本,沒法知足老機器至少有完整的2副本的要求,也就不能提供正常讀寫服務了。這就會致使這個PG裏的全部對象,中止對外服務。
那在擴容時,一次只擴容一臺機器時,是否是就安全了呢?這樣就能保證全部PG都至少在老機器有2個完整的副本了。但是,即便是擴容一臺機器,也還要面臨擴容時老機器中有硬盤壞掉,致使PG的完整副本又降低爲1的極端狀況發生。
辦法是,在開始規劃Ceph集羣時,設定好更大層次的「容錯域」,好比Rack。 能夠是真實的Rack,即便沒有也能夠是邏輯的Rack。這樣擴容時,能夠擴一個邏輯「容錯域」,就能夠打破擴一臺機器的限制,擴一整個Rack,至少有好幾臺機器。
(2)擴容是 crushmap 變化帶領的系統抖動
Ceph是根據crushmap去放置PG的物理位置的,假若在擴容進行了一半時,又有硬盤壞掉了,那Ceph的crushmap就會改變,Ceph又會從新進行PG的re-hash,不少PG的位置又會從新計算。若是運氣比較差,極可能一臺機器的擴容進度被迫進行了好久纔回到穩定的狀態。
這個crushmap改變致使的Ceph重平衡,不僅僅在擴容時,幾乎在任什麼時候候,對一個大的存儲集羣都有些頭疼。在創建一個新集羣時,硬盤都比較新,所以故障率並不高。可是在運行了2-3年的大存儲集羣,壞盤真的是一個稀鬆日常的事情,1000臺規模的集羣一天壞個2-3塊盤很正常。crushmap常常變更,對Ceph內部不穩定,影響真的很大。隨之而來,多是總體IO的降低(磁盤IO被反覆的rebalance佔滿),甚至是某些數據暫時不可用。
(3)OSD 增長時候的PG數量調整
假設咱們如今有10臺機器,每臺一塊硬盤一共10塊盤,有1024個PG,PG都是單副本,那麼每一個盤會存100個PG。此時這個設置很是健康,但當咱們集羣擴容到1000臺機器,每臺硬盤就只放一個PG了,這會致使僞隨機形成的不平衡現象放大。所以,admin就要面臨調整PG數量,這就帶來了問題。調PG,基本也就意味着整個集羣會進入一種嚴重不正常的狀態。幾乎50%的對象,涉及到調整後的PG都須要從新放置物理位置,這會引發服務質量的嚴重降低。
(4)盤滿形成的系統不可訪問
在集羣總體使用率不高時,都沒有問題。而在使用率達到70%後,就須要管理員介入了。由於方差大的盤,頗有可能會觸及95%這條紅線。admin開始調低容量太高磁盤的reweight,但若是在這一批磁盤被調整reweight沒有結束時,又有一些磁盤被寫滿了,那管理員就必須被迫在Ceph沒有達到穩定狀態前,又一次reweight太高的磁盤。 這就致使了crushmap的再一次變動,從而致使Ceph離穩定狀態愈來愈遠。而此時擴容又不及時的話,更是雪上加霜。並且以前的crushmap的中間狀態,也會致使一些PG遷移了一半,這些「不完整的」PG並不會被立刻刪除,這給原本就緊張的磁盤空間又加劇了負擔。關於reweight 致使的 rebalance,可參考 https://ceph.com/geen-categorie/ceph-osd-reweight/。
一塊磁盤滿了,Ceph爲何就不可用了。Ceph還真的就是這樣設計的,由於Ceph無法保證新的對象是否落在空盤而不落在滿盤,因此Ceph選擇在有盤滿了時,就拒絕服務。基本上你們的Ceph集羣都是在達到50%使用率時,就要開始準備擴容了。
2.3 Ceph 結構和狀態地圖 Cluster map
Ceph 要求 ceph 客戶端和 OSD 守護進程須要知曉整個集羣的拓撲結構,它們能夠經過 Monitor 獲取 cluster map 來達到這一點。Cluster map 包括:
(1)Monitor Map:MON 集羣的狀態(包括 the cluster fsid, the position, name address and port of each monitor, 建立時間,最後的更新時間等)。
root@ceph1:/osd/data# ceph mon dump dumped monmap epoch 1 epoch 1 fsid 4387471a-ae2b-47c4-b67e-9004860d0fd0 last_changed 0.000000 created 0.000000 0: 9.115.251.194:6789/0 mon.ceph1 1: 9.115.251.195:6789/0 mon.ceph2 2: 9.115.251.218:6789/0 mon.ceph3
(2)OSD Map:當前全部 Pool 的狀態和全部 OSD 的狀態 (包括 the cluster fsid, map 建立和最後修改時間, pool 列表, replica sizes, PG numbers, a list of OSDs and their status (e.g., up, in) 等)。經過運行 ceph osd dump 獲取。
root@ceph1:~# ceph osd dump epoch 76 fsid 4387471a-ae2b-47c4-b67e-9004860d0fd0 created 2015-09-18 02:16:19.504735 modified 2015-09-21 07:58:55.305221 flags pool 0 'data' replicated size 3 min_size 2 crush_ruleset 0 object_hash rjenkins pg_num 64 pgp_num 64 last_change 1 flags hashpspool crash_replay_interval 45 stripe_width 0 osd.3 up in weight 1 up_from 26 up_thru 64 down_at 25 last_clean_interval [7,23) 9.115.251.194:6801/1204 9.115.251.194:6802/1204 9.115.251.194:6803/1204 9.115.251.194:6804/1204 exists,up d55567da-4e2a-40ca-b7c9-5a30240c895a
......
(3)PG Map:包含PG 版本(version)、時間戳、最新的 OSD map epoch, full ratios, and 每一個 PG 的詳細信息好比 PG ID, Up Set, Acting Set, 狀態 (e.g., active + clean), pool 的空間使用統計。可使用命令 ceph pg dump 來獲取 PG Map。
這裏 有段代碼能夠以表格形式顯示這些映射關係:
ceph pg dump | awk '
/^pg_stat/ { col=1; while($col!="up") {col++}; col++ }
/^[0-9a-f]+\.[0-9a-f]+/ { match($0,/^[0-9a-f]+/); pool=substr($0, RSTART, RLENGTH); poollist[pool]=0; up=$col; i=0; RSTART=0; RLENGTH=0; delete osds; while(match(up,/[0-9]+/)>0) { osds[++i]=substr(up,RSTART,RLENGTH); up = substr(up, RSTART+RLENGTH) } for(i in osds) {array[osds[i],pool]++; osdlist[osds[i]];} } END { printf("\n"); printf("pool :\t"); for (i in poollist) printf("%s\t",i); printf("| SUM \n"); for (i in poollist) printf("--------"); printf("----------------\n"); for (i in osdlist) { printf("osd.%i\t", i); sum=0; for (j in poollist) { printf("%i\t", array[i,j]); sum+=array[i,j]; poollist[j]+=array[i,j] }; printf("| %i\n",sum) } for (i in poollist) printf("--------"); printf("----------------\n"); printf("SUM :\t"); for (i in poollist) printf("%s\t",poollist[i]); printf("|\n"); }'
(4)CRUSH (Controlled Replication under Scalable Hashing)Map:包含當前磁盤、服務器、機架等層級結構 (Contains a list of storage devices, the failure domain hierarchy (e.g., device, host, rack, row, room, etc.), and rules for traversing the hierarchy when storing data)。 要查看該 map 的話,先運行 ceph osd getcrushmap -o {filename} 命令,而後運行 crushtool -d {comp-crushmap-filename} -o {decomp-crushmap-filename} 命令,在vi 或者 cat {decomp-crushmap-filename} 便可。
CRUSH map 使用分層結構來組織集羣中的全部存儲設備:
CRUSH rules 主要有三個做用:
- 指定從CRUSH Map 中的哪一個節點開始查找
- 指定使用那個節點做爲故障隔離域
- 指定定位副本的搜索模式(廣度優先 or 深度優先)
例子:
rule replicated_ruleset #規則集的命名,建立pool時能夠指定rule集 { ruleset 0 #rules集的編號,順序編便可 type replicated #定義pool類型爲replicated(還有esurecode模式) min_size 1 #pool中最小指定的副本數量不能小1 max_size 10 #pool中最大指定的副本數量不能大於10 step take default #定義PG查找副本的入口點 step chooseleaf firstn 0 type host #選葉子節點、深度優先、隔離host step emit #結束 }
PG 選擇 OSD 的過程(詳情可閱讀 PG選擇osd的過程(crush 算法)):
- 首先要知道在 rules 中指明從 CRUSH map 中哪一個節點開始查找,入口點默認爲 default 也就是 root 節點
- 而後隔離域爲 host 節點(也就是同一個host下面不能選擇兩個子節點)。由 default 到3個host的選擇過程,這裏由default根據節點的bucket類型選擇下一個子節點,由子節點再根據自己的類型繼續選擇,知道選擇到host,而後在host下選擇一個osd。
所以,Ceph Admin 能夠經過配置 CRUSH map 和 rules 來決定數據的存放方式。詳細信息,能夠參考 The RADOS object store and Ceph filesystem: Part 2。
(5)MDS Map:包含當前全部 MDS 的狀態 (the current MDS map epoch, when the map was created, and the last time it changed. It also contains the pool for storing metadata, a list of metadata servers, and which metadata servers are up and in)。經過執行 ceph mds dump 獲取。
root@ceph1:~# ceph mds dump dumped mdsmap epoch 13 epoch 13 flags 0 created 2015-09-18 02:16:19.504427 modified 2015-09-18 08:05:55.438558 4171: 9.115.251.194:6800/962 'ceph1' mds.0.2 up:active seq 7 5673: 9.115.251.195:6800/959 'ceph2' mds.-1.0 up:standby seq 1
MDS 只用於Ceph 文件系統該,與 RDB 和對象存儲無關。
3. CRUSH 算法(以RBD爲例)
3.1 Ceph client 是如何將數據塊放到 OSD 的
Ceph 架構中,Ceph 客戶端是直接讀或者寫存放在 OSD上的 RADOS 對象存儲中的對象(data object)的,所以,Ceph 須要走完 (Pool, Object) → (Pool, PG) → OSD set → OSD/Disk 完整的鏈路,才能讓 ceph client 知道目標數據 object的具體位置在哪裏:
(1)建立 Pool 和它的 PG。根據上述的計算過程,PG 在 Pool 被建立後就會被 MON 在根據 CRUSH 算法計算出來的 PG 應該所在若干的 OSD 上被建立出來了。也就是說,在客戶端寫入對象的時候,PG 已經被建立好了,PG 和 OSD 的映射關係已是肯定了的。
(2)Ceph 客戶端經過哈希算法計算出存放 object 的 PG 的 ID:
- 客戶端輸入 pool ID 和 object ID (好比 pool = 「liverpool」 and object-id = 「john」)
- ceph 對 object ID 作哈希
- ceph 對該 hash 值取 PG 總數的模,獲得 PG 編號 (好比 58)(第2和第3步基本保證了一個 pool 的全部 PG 將會被均勻地使用)
- ceph 對 pool ID 取 hash (好比 「liverpool」 = 4)
- ceph 將 pool ID 和 PG ID 組合在一塊兒(好比 4.58)獲得 PG 的完整ID。
也就是:PG-id = hash(pool-id). hash(objet-id) % PG-number
(3)客戶端經過 CRUSH 算法計算出(或者說查找出) object 應該會被保存到 PG 中哪一個 OSD 上。(注意:這裏是說」應該「,而不是」將會「,這是由於 PG 和 OSD 之間的關係是已經肯定了的,那客戶端須要作的就是須要知道它所選中的這個 PG 到底將會在哪些 OSD 上建立對象。)。這步驟也叫作 CRUSH 查找。
對 Ceph 客戶端來講,只要它得到了 Cluster map,就可使用 CRUSH 算法計算出某個 object 將要所在的 OSD 的 ID,而後直接與它通訊。
- Ceph client 從 MON 獲取最新的 cluster map。
- Ceph client 根據上面的第(2)步計算出該 object 將要在的 PG 的 ID。
- Ceph client 再根據 CRUSH 算法計算出 PG 中目標主和次 OSD 的 ID。
也就是:OSD-ids = CURSH(PG-id, cluster-map, cursh-rules)。
(4)客戶端寫入數據
在客戶端使用 rbd 時通常有兩種方法:
- 第一種 是 Kernel rbd。就是建立了rbd設備後,把rbd設備map到內核中,造成一個虛擬的塊設備,這時這個塊設備同其餘通用塊設備同樣,通常的設備文件爲/dev/rbd0,後續直接使用這個塊設備文件就能夠了,能夠把 /dev/rbd0 格式化後 mount 到某個目錄,也能夠直接做爲裸設備使用。這時對rbd設備的操做都經過kernel rbd操做方法進行的。
- 第二種是 librbd 方式。就是建立了rbd設備後,這時可使用librbd、librados庫進行訪問管理塊設備。這種方式不會map到內核,直接調用librbd提供的接口,能夠實現對rbd設備的訪問和管理,可是不會在客戶端產生塊設備文件。
應用寫入rbd塊設備的過程(詳細步驟請參考 rbd client 端的數據請求處理):
- 應用調用 librbd 接口或者對linux 內核虛擬塊設備寫入二進制塊。下面以 librbd 爲例。
- librbd 對二進制塊進行分塊,默認塊大小爲 4M,每一塊都有名字,成爲一個對象
- librbd 調用 librados 將對象寫入 Ceph 集羣
- librados 向主 OSD 寫入分好塊的二進制數據塊 (先創建TCP/IP鏈接,而後發送消息給 OSD,OSD 接收後寫入其磁盤)
- 主 OSD 負責同時向一個或者多個次 OSD 寫入副本。注意這裏是寫到日誌(Journal)就返回,所以,使用SSD做爲Journal的話,能夠提升響應速度,作到服務器端對客戶端的快速同步返回寫結果(ack)。
- 當主次OSD都寫入完成後,主 OSD 向客戶端返回寫入成功。
- 當一段時間(也許得幾秒鐘)後Journal 中的數據向磁盤寫入成功後,Ceph經過事件通知客戶端數據寫入磁盤成功(commit),此時,客戶端能夠將寫緩存中的數據完全清除掉了。
- 默認地,Ceph 客戶端會緩存寫入的數據直到收到集羣的commit通知。若是此階段內(在寫方法返回到收到commit通知之間)OSD 出故障致使數據寫入文件系統失敗,Ceph 將會容許客戶端重作還沒有提交的操做(replay)。所以,PG 有個狀態叫 replay:「The placement group is waiting for clients to replay operations after an OSD crashed.」。
也就是,文件系統負責文件處理,librdb 負責塊處理,librados 負責對象處理,OSD 負責將數據寫入在Journal和磁盤中。
關於 RDB 鏡像在存儲池中是如何被存放的,請閱讀 理解 OpenStack + Ceph (4):Ceph 的基礎數據結構 [Pool, Image, Snapshot, Clone]。
(5)以存放一個文件爲例,下圖(來源)說明了完整的計算過程:
(6)一些說明
幾個比例關係:
- 文件 :對象 = 1 : n (由客戶端實時計算)
- object :PG = n : 1 (有客戶端使用哈希算法計算)
- PG :OSD = m : n (由 MON 根據 CRUSH 算法計算)
CRUSH 算法是至關複雜,快速看看的話能夠參考 官方文章,或者直接讀代碼和做者的論文。幾個簡單的結論或原則:
- 一個 RBD image(好比虛機的一個鏡像文件)會分紅幾個 data objects 保存在 Ceph 對象存儲中。
- 一個 Ceph 集羣含有多個 pool (使用 ceph osd pool create 命令建立pool)
- 一個 Pool 包含若干個 PG (在建立 pool 時必須指定 pg_num,在須要的時候對已有的pool的 pg_num 也能夠進行修改)
- 一個 PG 能夠包含多個對象
- 一個 object 只在一個 PG 中
- 一個 PG 映射到一組 OSD,其中第一個 OSD 是主(primary),其他的是從(secondary)
- 許多 PG 能夠映射到某個 OSD,一般一個OSD上會有50到100個PG。
使用 這裏 的腳本,能夠看出在個人測試環境(pool的副本數爲1)中,一共有 6 個pool,7 個 OSD,每一個 pool 中有 192 個PG,每一個 OSD 大概在164個 (192 * 6*1/7)PG 中:
pool : 4 5 0 1 2 3 | SUM ---------------------------------------------------------------- osd.4 20 19 21 23 21 24 | 128 osd.5 38 28 30 28 34 44 | 202 osd.6 30 33 33 32 34 36 | 198 osd.7 23 19 22 21 22 21 | 128 osd.8 26 36 34 36 30 20 | 182 osd.9 21 26 21 20 21 19 | 128 osd.3 34 31 31 32 30 28 | 186 ---------------------------------------------------------------- SUM : 192 192 192 192 192 192 |
這張圖(來源)也有助於理清其中的關係:
總之,Ceph 採用的是經過計算找到對象應該被保存的 OSD 位置,這比經過常見的查詢算法獲取位置快得多。CRUSH 算法是的 ceph 客戶端本身計算對象要被保存在哪裏(哪一個 OSD),也使得客戶端能夠從主 OSD 上保存或者讀取數據。
3.2 RBD image 保存過程和形式
以下圖所示,Ceph 系統中不一樣層次的組件/用戶所看到的數據的形式是不同的:
- Ceph 客戶端所見的是一個完整的連續的二進制數據塊,其大小爲建立 RBD image 是設置的大小或者 resize 的大小,客戶端能夠從頭或者從某個位置開始寫入二進制數據。
- librados 負責在 RADOS 中建立對象(object),其大小爲 pool 的 order 決定,默認狀況下 order = 22 此時 object 大小爲 4MB;以及負責將客戶端傳入的二進制塊條帶化爲若干個條帶(stripe)。
- librados 控制哪一個條帶由哪一個 OSD 寫入(條帶 ---寫入哪一個----> object ----位於哪一個 ----> OSD)
- OSD 負責建立在文件系統中建立文件,並將 librados 傳入的數據寫入數據。
Ceph client 向一個 RBD image 寫入二進制數據(假設 pool 的拷貝份數爲 3):
(1)Ceph client 調用 librados 建立一個 RBD image,這時候不會作存儲空間分配,而是建立若干元數據對象來保存元數據信息。
(2)Ceph client 調用 librados 開始寫數據。librados 計算條帶、object 等,而後開始寫第一個 stripe 到特定的目標 object。
(3)librados 根據 CRUSH 算法,計算出 object 所對應的主 OSD ID,並將二進制數據發給它。
(4)主 OSD 負責調用文件系統接口將二進制數據寫入磁盤上的文件(每一個 object 對應一個 file,file 的內容是一個或者多個 stripe)。
(5)主 ODS 完成數據寫入後,它使用 CRUSH 算啊計算出第二個OSD(secondary OSD)和第三個OSD(tertiary OSD)的位置,而後向這兩個 OSD 拷貝對象。都完成後,它向 ceph client 反饋該 object 保存完畢。
(6)而後寫第二個條帶,直到所有寫入完成。所有完成後,librados 還應該會作元數據更新,好比寫入新的 size 等。
完整的過程(來源):
該過程具備強一致性的特色:
- Ceph 的讀寫操做採用 Primary-Replica 模型,Client 只向 Object 所對應 OSD set 的 Primary 發起讀寫請求,這保證了數據的強一致性。
- 因爲每一個 Object 都只有一個 Primary OSD,所以對 Object 的更新都是順序的,不存在同步問題。
- 當 Primary 收到 Object 的寫請求時,它負責把數據發送給其餘 Replicas,只要這個數據被保存在全部的OSD上時,Primary 才應答Object的寫請求,這保證了副本的一致性。這也帶來一些反作用。相比那些只實現了最終一致性的存儲系統好比 Swift,Ceph 只有三份拷貝都寫入完成後纔算寫入完成,這在出現磁盤損壞時會出現寫延遲增長。
- 在 OSD 上,在收到數據存放指令後,它會產生2~3個磁盤seek操做:
-
- 把寫操做記錄到 OSD 的 Journal 文件上(Journal是爲了保證寫操做的原子性)。
- 把寫操做更新到 Object 對應的文件上。
- 把寫操做記錄到 PG Log 文件上。
3.3 條帶化(striping)
在 RADOS 層,Ceph 自己沒有條帶的概念,由於一個object 是做爲一個 文件總體性保存的。可是,RBD 能夠控制向一個 object 的寫入方式,默認是將一個 object 寫滿再去寫下一個object;還能夠經過指定 stripe_unit 和 stripe_count,來將 object 分紅若干個條帶即 strip。
- order:RADOS Object 的大小爲 2^[order] bytes。默認的 oder 爲 22,這時候對象大小爲4MB。最小 4k,最大 32M,默認 4M.
- stripe_unit:條帶(stripe unit)的大小。每一個 [stripe_unit] 的連續字節會被連續地保存到同一個對象中,client 寫滿 stripe unit 大小的數據後,接着去下一個 object 中寫下一個 stripe unit 大小的數據。默認爲 1,此時一個 stripe 就是一個 object。
- stripe_count:在分別寫入了 [stripe_unit] 個字節到 [stripe_count] 個對象後,ceph 又從新從一個新的對象開始寫下一個條帶,直到該對象達到了它的最大大小。這時候,ceph 轉移到下 [stripe_unit] 字節。默認爲 object site。