CRUSH 算法經過計算數據存儲位置來肯定如何存儲和檢索。 CRUSH 受權 Ceph 客戶端直接鏈接 OSD ,而非經過一箇中央服務器或代理。數據存儲、檢索算法的使用,使 Ceph 避免了單點故障、性能瓶頸、和伸縮的物理限制。node
CRUSH 須要一張集羣的 Map,且使用 CRUSH Map 把數據僞隨機地、儘可能平均地分佈到整個集羣的 OSD 裏。CRUSH Map 包含 OSD 列表、把設備匯聚爲物理位置的「桶」列表、和指示 CRUSH 如何複製存儲池裏的數據的規則列表。算法
徹底手動管理 CRUSH Map 也是可能的,在配置文件中設定:服務器
osd crush update on start = falseide
要編輯現有的 CRUSH Map:性能
獲取 CRUSH Map;spa
反編譯 CRUSH 圖;代理
至少編輯一個設備、桶、規則;orm
重編譯 CRUSH Map;server
注入 CRUSH Map。對象
要激活 CRUSH Map 裏某存儲池的規則,找到通用規則集編號,而後把它指定到那個規則集。
要獲取集羣的 CRUSH Map,執行命令:
ceph osd getcrushmap -o {compiled-crushmap-filename}
Ceph 將把 CRUSH 輸出( -o )到你指定的文件,因爲 CRUSH Map 是已編譯的,因此編輯前必須先反編譯。
要反編譯 CRUSH Map,執行命令:
crushtool -d {compiled-crushmap-filename} -o {decompiled-crushmap-filename}
Ceph 將反編譯( -d )二進制 CRUSH Map,且輸出( -o )到你指定的文件。
要編譯 CRUSH Map,執行命令:
crushtool -c {decompiled-crush-map-filename} -o {compiled-crush-map-filename}
Ceph 將把已編譯的 CRUSH Map 保存到你指定的文件。
要把 CRUSH Map 應用到集羣,執行命令:
ceph osd setcrushmap -i {compiled-crushmap-filename}
Ceph 將把你指定的已編譯 CRUSH Map 注入到集羣。
CRUSH Map 主要有 4 個段落。
設備: 由任意對象存儲設備組成,即對應一個 ceph-osd進程的存儲器。 Ceph 配置文件裏的每一個 OSD 都應該有一個設備。
桶類型: 定義了 CRUSH 分級結構裏要用的桶類型( types ),桶由逐級匯聚的存儲位置(如行、機櫃、機箱、主機等等)及其權重組成。
桶實例: 定義了桶類型後,還必須聲明主機的桶類型、以及規劃的其它故障域。
規則: 由選擇桶的方法組成。
爲把 PG 映射到 OSD , CRUSH Map 須要 OSD 列表(即配置文件所定義的 OSD 守護進程名稱),因此它們首先出如今 CRUSH Map 裏。要在 CRUSH Map 裏聲明一個設備,在設備列表後面新建一行,輸入 device 、以後是惟一的數字 ID 、以後是相應的 ceph-osd 守護進程實例名字。
# devices
device {num} {osd.name}
例如:
# devices
device 0 osd.0
device 1 osd.1
device 2 osd.2
device 3 osd.3
CRUSH Map 裏的第二個列表定義了 bucket (桶)類型,桶簡化了節點和葉子層次。節點(或非葉子)桶在分級結構裏通常表示物理位置,節點匯聚了其它節點或葉子,葉桶表示 ceph-osd 守護進程及其對應的存儲媒體。
要往 CRUSH Map 中增長一種 bucket 類型,在現有桶類型列表下方新增一行,輸入 type 、以後是唯一數字 ID 和一個桶名。按慣例,會有一個葉子桶爲 type 0 ,然而你能夠指定任何名字(如 osd 、 disk 、 drive 、 storage 等等):
# types
type {num} {bucket-name}
例如:
# types
type 0 osd
type 1 host
type 2 chassis
type 3 rack
type 4 row
type 5 pdu
type 6 pod
type 7 room
type 8 datacenter
type 9 region
type 10 root
CRUSH 算法根據各設備的權重、大體統一的機率把數據對象分佈到存儲設備中。 CRUSH 根據你定義的集羣運行圖分佈對象及其副本, CRUSH Map 表達了可用存儲設備以及包含它們的邏輯單元。
要把 PG 映射到跨故障域的 OSD ,一個 CRUSH Map 需定義一系列分級桶類型(即現有 CRUSH Map 的 # type 下)。建立桶分級結構的目的是按故障域隔離葉子節點,像主機、機箱、機櫃、電力分配單元、機羣、行、房間、和數據中心。除了表示葉子節點的 OSD ,其它分級結構都是任意的,你能夠按需定義。
聲明一個桶實例時,你必須指定其類型、唯一名稱(字符串)、唯一負整數 ID (可選)、指定和各條目總容量/能力相關的權重、指定桶算法(一般是 straw )、和哈希(一般爲 0 ,表示哈希算法 rjenkins1 )。一個桶能夠包含一到多個條目,這些條目能夠由節點桶或葉子組成,它們能夠有個權重用來反映條目的相對權重。
你能夠按下列語法聲明一個節點桶:
[bucket-type] [bucket-name] {
id [a unique negative numeric ID]
weight [the relative capacity/capability of the item(s)]
alg [the bucket type: uniform | list | tree | straw ]
hash [the hash type: 0 by default]
item [item-name] weight [weight]
}
例如,咱們能夠定義兩個主機桶和一個機櫃桶,機櫃桶包含兩個主機桶, OSD 被聲明爲主機桶內的條目:
host node1 {
id -1
alg straw
hash 0
item osd.0 weight 1.00
item osd.1 weight 1.00
}
host node2 {
id -2
alg straw
hash 0
item osd.2 weight 1.00
item osd.3 weight 1.00
}
rack rack1 {
id -3
alg straw
hash 0
item node1 weight 2.00
item node2 weight 2.00
}
Ceph 用雙精度類型數據表示桶權重。權重和設備容量不一樣,咱們建議用 1.00 做爲 1TB 存儲設備的相對權重,這樣 0.5 的權重大概表明 500GB 、 3.00 大概表明 3TB 。較高級桶的權重是全部葉子桶的權重之和。
一個桶的權重是一維的,你也能夠計算條目權重來反映存儲設備性能。例如,若是你有不少 1TB 的硬盤,其中一些數據傳輸速率相對低、其餘的數據傳輸率相對高,即便它們容量相同,也應該設置不一樣的權重(如給吞吐量較低的硬盤設置權重 0.8 ,較高的設置 1.20 )。
CRUSH Map 支持「 CRUSH 規則」的概念,用以肯定一個存儲池裏數據的分佈。CRUSH 規則定義了歸置和複製策略、或分佈策略,用它能夠規定 CRUSH 如何放置對象副本。對大型集羣來講,你可能建立不少存儲池,且每一個存儲池都有它本身的 CRUSH 規則集和規則。默認的 CRUSH Map 裏,每一個存儲池有一條規則、一個規則集被分配到每一個默認存儲池。
注意: 大多數狀況下,你都不須要修改默認規則。新建立存儲池的默認規則集是 0 。
規則格式以下:
rule <rulename> {
ruleset <ruleset>
type [ replicated | erasure ]
min_size <min-size>
max_size <max-size>
step take <bucket-type>
step [choose|chooseleaf] [firstn|indep] <N> <bucket-type>
step emit
}
參數說明:
ruleset:區分一條規則屬於某個規則集的手段。給存儲池設置規則集後激活。
type:規則類型,目前僅支持 replicated 和 erasure ,默認是 replicated 。
min_size:能夠選擇此規則的存儲池最小副本數。
max_size:能夠選擇此規則的存儲池最大副本數。
step take <bucket-name>:選取起始的桶名,並迭代到樹底。
step choose firstn {num} type {bucket-type}:選取指定類型桶的數量,這個數字一般是存儲池的副本數(即 pool size )。若是 {num} == 0 , 選擇 pool-num-replicas 個桶(全部可用的);若是 {num} > 0 && < pool-num-replicas ,就選擇那麼多的桶;若是 {num} < 0,它意味着選擇 pool-num-replicas - {num} 個桶。
step chooseleaf firstn {num} type {bucket-type}:選擇 {bucket-type} 類型的桶集合,並從各桶的子樹裏選擇一個葉子節點。桶集合的數量一般是存儲池的副本數(即 pool size )。若是 {num} == 0 ,選擇 pool-num-replicas 個桶(全部可用的);若是 {num} > 0 && < pool-num-replicas ,就選擇那麼多的桶;若是 {num} < 0 ,它意味着選擇 pool-num-replicas - {num} 個桶。
step emit:輸出當前值並清空堆棧。一般用於規則末尾,也適用於相同規則應用到不一樣樹的狀況。
某個 Ceph 客戶端讀寫數據時,老是鏈接 acting set 裏的主 OSD (如 [2, 3, 4] 中, osd.2 是主的)。有時候某個 OSD 與其它的相比並不適合作主 OSD (好比其硬盤慢、或控制器慢)。最大化硬件利用率時爲防止性能瓶頸(特別是讀操做),你能夠調整 OSD 的主親和性,這樣 CRUSH 就儘可能不把它用做 acting set 裏的主 OSD 了。
ceph osd primary-affinity <osd-id> <weight>
主親和性默認爲 1 (就是說此 OSD 可做爲主 OSD )。此值合法範圍爲 0-1 ,其中 0 意爲此 OSD 不能用做主的, 1 意爲 OSD 可用做主的。此權重 < 1 時, CRUSH 選擇主 OSD 時選中它的可能性就較低。
要增長或移動在線集羣裏 OSD 所對應的 CRUSH Map 條目,執行 ceph osd crush set 命令。
ceph osd crush set {id-or-name} {weight} {bucket-type}={bucket-name} [{bucket-type}={bucket-name} ...]
要調整在線集羣中某個 OSD 的 CRUSH 權重,執行命令:
ceph osd crush reweight {name} {weight}
要從在線集羣裏把某個 OSD 完全踢出 CRUSH Map,或僅踢出某個指定位置的 OSD,執行命令:
ceph osd crush remove {name} {<ancestor>}
要在運行集羣的 CRUSH Map 中新建一個桶,用 ceph osd crush add-bucket 命令:
ceph osd crush add-bucket {bucket-name} {bucket-type}
要把一個桶移動到 CRUSH Map 裏的不一樣位置,執行命令:
ceph osd crush move {bucket-name} {bucket-type}={bucket-name} [{bucket-type}={bucket-name} ...]
要把一個桶從 CRUSH Map 的分級結構中刪除,可用此命令:
ceph osd crush remove {bucket-name}
注意:從 CRUSH 分級結構裏刪除時必須是空桶。
11. 可調選項
從 v0.74 起,若是 CRUSH 可調選項不是最優值( v0.73 版裏的默認值) Ceph 就會發出健康告警,有兩種方法可消除這些告警:
一、調整現有集羣上的可調選項。注意,這可能會致使一些數據遷移(可能有 10% 之多)。這是推薦的辦法,可是在生產集羣上要注意此調整對性能帶來的影響。此命令可啓用較優可調選項:
ceph osd crush tunables optimal
若是切換得不太順利(如負載過高)且切換纔不久,或者有客戶端兼容問題(較老的 cephfs 內核驅動或 rbd 客戶端、或早於 bobtail 的 librados 客戶端),你能夠這樣切回:
ceph osd crush tunables legacy
二、不對 CRUSH 作任何更改也能消除報警,把下列配置加入 ceph.conf 的 [mon] 段下:
mon warn on legacy crush tunables = false
爲使變動生效需重啓全部監視器,或者執行下列命令:
ceph tell mon.\* injectargs --no-mon-warn-on-legacy-crush-tunables
12.CRUSH Map 實例
假設你想讓大多數存儲池映射到使用大容量硬盤的 OSD 上,可是其中一些存儲池映射到使用高速 SSD 的 OSD 上。在同一個 CRUSH Map 內有多個獨立的 CRUSH 層級結構是可能的,定義兩棵樹、分別有本身的根節點 —— 一個用於機械硬盤(如 root platter )、一個用於 SSD (如 root ssd ),具體的 CRUSH Map 內容以下:
# devices
device 0 osd.0
device 1 osd.1
device 2 osd.2
device 3 osd.3
device 4 osd.4
device 5 osd.5
device 6 osd.6
device 7 osd.7
# types
type 0 osd
type 1 host
type 2 root
# buckets
host ceph-osd-ssd-server-1 {
id -1
alg straw
hash 0
item osd.0 weight 1.00
item osd.1 weight 1.00
}
host ceph-osd-ssd-server-2 {
id -2
alg straw
hash 0
item osd.2 weight 1.00
item osd.3 weight 1.00
}
host ceph-osd-platter-server-1 {
id -3
alg straw
hash 0
item osd.4 weight 1.00
item osd.5 weight 1.00
}
host ceph-osd-platter-server-2 {
id -4
alg straw
hash 0
item osd.6 weight 1.00
item osd.7 weight 1.00
}
root platter {
id -5
alg straw
hash 0
item ceph-osd-platter-server-1 weight 2.00
item ceph-osd-platter-server-2 weight 2.00
}
root ssd {
id -6
alg straw
hash 0
item ceph-osd-ssd-server-1 weight 2.00
item ceph-osd-ssd-server-2 weight 2.00
}
# rules
rule replicated_ruleset {
ruleset 0
type replicated
min_size 1
max_size 10
step take default
step chooseleaf firstn 0 type host
step emit
}
rule platter {
ruleset 1
type replicated
min_size 0
max_size 10
step take platter
step chooseleaf firstn 0 type host
step emit
}
rule ssd {
ruleset 2
type replicated
min_size 0
max_size 4
step take ssd
step chooseleaf firstn 0 type host
step emit
}
rule ssd-primary {
ruleset 3
type replicated
min_size 5
max_size 10
step take ssd
step chooseleaf firstn 1 type host
step emit
step take platter
step chooseleaf firstn -1 type host
step emit
}
而後你能夠設置一個存儲池,讓它使用 SSD 規則:
ceph osd pool set <poolname> crush_ruleset 2
一樣,用 ssd-primary 規則將使存儲池內的各歸置組用 SSD 做主 OSD ,普通硬盤做副本。