このページでは ceph の CRUSH マップの記述方法を説明する。 Ceph の設定手順の中では「Ceph を使ってみる」のSTEP7で必要になる。
以下は関連ページ。
更新履歴
(2012.02.11) 作成。
(2012.02.14) 「CRUSHマップの取得・設定」と「テストツールを使った配置テスト」を追記。
(2012.04.02) 稼働中の CRUSH マップの確認方法を追加。
(2012.05.03) CRUSH マップの記述ルールを追加。
目次
CRUSHマップの取得・設定
稼働中の ceph システムから CRUSH マップを取得し、再設定するには以下の手順が必要である。
まず CRUSH マップを /tmp/crush というファイルに書き出す。
# ceph osd getcrushmap -o /tmp/crush
/tmp/crush はバイナリファイルのため、これを人間が見て分かるテキストファイルにデコードする。 変換後のファイルが /tmp/crush.txt になる。
crushtool -d /tmp/crush -o /tmp/crush.txt
逆に /tmp/crush.txt から元のバイナリファイルにエンコードするには以下のようにする。 結果は /tmp/crush.new に書き出される。
# crushtool -c /tmp/crush.txt -o /tmp/crush.new
バイナリ形式の CRUSH マップを稼働中の ceph システムに適用するには以下のようにする。
# ceph osd setcrushmap -i /tmp/crush.new
ただし CRUSH マップを適用しただけでは、すぐにオブジェクトが CRUSH マップに従った配置にならない。 これを正すには reweight を行う。
# ceph -w
構造
まず CRUSH マップの例を示す。
これは ceph 0.39 で mkcephfs を実行したときにできた CRUSH マップである。 ceph.conf の中で定義された OSD を同一のホストに属するもの同士で一つにまとめるため実質2段の階層となっている。
ただし以前のバージョンの ceph は、ceph.conf に定義された全ての OSD を一まとめにした実質1段の階層だった。同一構成であっても ceph のバージョンによって異なる CRUSH マップができる可能性がある。
# 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 device 8 osd.8 device 9 osd.9 device 10 osd.10 device 11 osd.11 # types type 0 osd type 1 host type 2 rack type 3 pool # buckets host host9 { id -2 # do not change unnecessarily # weight 4.000 alg straw hash 0 # rjenkins1 item osd.0 weight 1.000 item osd.1 weight 1.000 item osd.2 weight 1.000 item osd.3 weight 1.000 } host host12 { id -4 # do not change unnecessarily # weight 4.000 alg straw hash 0 # rjenkins1 item osd.10 weight 1.000 item osd.11 weight 1.000 item osd.8 weight 1.000 item osd.9 weight 1.000 } host host11 { id -5 # do not change unnecessarily # weight 4.000 alg straw hash 0 # rjenkins1 item osd.4 weight 1.000 item osd.5 weight 1.000 item osd.6 weight 1.000 item osd.7 weight 1.000 } rack unknownrack { id -3 # do not change unnecessarily # weight 12.000 alg straw hash 0 # rjenkins1 item host9 weight 4.000 item host12 weight 4.000 item host11 weight 4.000 } pool default { id -1 # do not change unnecessarily # weight 12.000 alg straw hash 0 # rjenkins1 item unknownrack weight 12.000 } # rules rule data { ruleset 0 type replicated min_size 1 max_size 10 step take default step chooseleaf firstn 0 type host step emit } rule metadata { ruleset 1 type replicated min_size 1 max_size 10 step take default step chooseleaf firstn 0 type host step emit } rule rbd { ruleset 2 type replicated min_size 1 max_size 10 step take default step chooseleaf firstn 0 type host step emit }
CRUSH マップはセクションに分かれてはいないが、ある程度意味的なまとまりがある。 順番に説明する。
Devices
まず先頭にある device は OSD のことである。 ceph.conf で記述している OSD を一つづつ device として定義してゆく。
# devices device 0 osd.0 device 1 osd.1 device 2 osd.2 ...
Types
types は CRUSH マップの bucket に与える型の名前である。 type の名前(青字)には意味はなく適当につけてよい。 ただし慣習として pool と osd を定義しておいた方がよいと思われる。 type の番号は bucket の階層構造とは無関係である。 type 名毎に重ならないように順番に番号を振る。
# types type 0 osd type 1 host type 2 rack type 3 pool
Buckets
次に bucket の定義を記述する。 青字の部分は type で定義した bucket 型の名前を指定し、次に bucket の名前を付ける。 この下の例では rack 型の bucket unknownrack ということになる。 名前は自由につけてよい。
ここで定義するのは bucket 内にある item と個数だけであり、この段でレプリケーションをどう配分するかはルール(rule)の方で記述する。
# buckets rack unknownrack { id -3 # do not change unnecessarily # weight 12.000 alg straw hash 0 # rjenkins1 item host9 weight 4.000 item host12 weight 4.000 item host11 weight 4.000 }
- id の緑字の -3 は bucket の ID である。他と重ならない負の数を番号付けすること。
- alg に続くピンク色の文字には "uniform"、"list"、"tree"、"straw"のいずれかを記述する。これは bucket の種類を示すものでこちらを参考にして欲しい。基本的に "straw" を指定する。
- hash はハッシュ関数にどのようなアルゴリズムを使うかを指定する。"0" か "rjenkins1" が指定できる。両者は同一である。これ以外の値には対応していない。
- item は bucket または device を指定する。一つの bucket の中にある item は同一の type である必要はない。混ざっていても OK。
- 紫色の weight を変更することで item の重み付けをすることができる。weight が大きい item の方がオブジェクトが割り付けられやすい。weight の大きさによってオブジェクトがどの OSD にどの程度偏るかはこちらを参照のこと。
- id → alg → hash → item の順に記述すること。ただし hash と item は省略することができる。
Rules
ルールは CRUSH の選択ルールを記述する。 RADOS の pool の数だけルールを記述する必要がある。 また pool 毎に別のルールを記述できるので、pool によって使われる OSDを変えたりすることもできる。
rule data { ruleset 0 type replicated min_size 1 max_size 10 step take default # L1 step choose firstn 0 type host # L2 step choose firstn 1 type device # L3 step emit }
まずルールのうち形式的な部分から説明する。
- 赤文字で示したルール名は適当に付けられる。空のままでもよい。
- 青文字で示した ruleset の 0 は RADOS のプール番号を指定する。プール番号はこちらを参考にして欲しい。
- ピンク色文字で示した type は通常 "replicated" を指定するが、"raid4" も指定できる。ただし RAID4 まだ実装されていない(ceph 0.56.4 の時点)。
- min_size と max_size はレプリケーション数の上限の幅を指定している。とりあえずこのまま記述すればよい。
- ruleset → type → min_size → max_size → step の順に記述すること。
次にステップの部分の説明をする。
- step は並び順通りに処理される。step take で開始し、step emit で終わる必要がある。
- step take は最初に選択する bucket の名前を指定する。例では緑文字の部分に default とあり、default という名前の bucket が最初に選択されることが分かる。
- step choose
- 青文字の部分には type 名が入る。これは上流からはじめて item として type と一致するものを探すまで検索するという意味である。
- 上流というのは L1 で最初の bucket として default が指定されているため、L2 は default が上流になる。L3 は L2 によって選択された host 型の bucket が上流になる。
- firstn N は type で指定された item を N 個探すという意味である。ただし firstn 0 は特別な意味を持ち、レプリケーション数の指定からくる最大まで item を探せという意味になる。
- firstn N type M 以外に indep N type M という指定も可能だが、どのような効果があるか分からない。
- よって L2 はレプリケーション数指定されている最大数まで host 型の bucket を探す。 L3 は L2 で選択された 3 つの host 型の bucket おのおのからはじめて device を 1 つだけ選択するという意味になる。
- step emit で終了する。
いうまでもなくルールの最後は device を選択して終了する必要がある。 そのため L3 のルールはある意味不必要である。 L2 の選択後は device を見つけるところまで選択せよという意味で choose のかわりに chooseleaf が用意されている。 以下の1行は L2 と L3 を合わせた意味を持つ。
step chooseleaf firstn 0 type host
なお chooseleaf にも firstn の替わりに indep を指定する方法がある。 これもどのような効果があるか分からない。
step chooseleaf indep 0 type host
注意事項
いくつか注意事項を記述する。
- レプリケーションの多重度は CRUSH マップでは与えられない。 「Ceph を使ってみる」の5節のようにコマンドで動的に切り替えるものである。
正式なドキュメントが存在しないためルールなのかどうかはっきりしないが、以下のようなマナーを守ったほうがよい。
- step take からはじまる bucket の階層の中にループができるとまずいだろう。
- 書き順がある。最初に devices と types を書いてから、その後に buckets と rules を記述すること。
テストツールを使った配置テスト
crushtool コマンドを使うことで CRUSH マップを実際の ceph システムに適用する前に、OSD 間にオブジェクトがどのように割り付けられるかを模擬することができる。
Ceph-0.43 で確認すると、この機能は失われて、crushtool --test の結果は別の表示結果を返すようになっている。
8つの OSD が straw 型の bucket によって均等に重み付けされているとする。
host ceph1 { id -2 # do not change unnecessarily # weight 3.000 alg straw hash 0 # rjenkins1 item osd.0 weight 1.000 item osd.1 weight 1.000 item osd.2 weight 1.000 item osd.3 weight 1.000 item osd.4 weight 1.000 item osd.5 weight 1.000 item osd.6 weight 1.000 item osd.7 weight 1.000 }(これは CRUSH マップの一部)
これを実行するためにはテキスト形式の CRUSH マップをバイナリ形式に変換する。
# crushtool -c (テキスト形式のクラッシュマップ) -o /tmp/crush.bin.new
crushtool は --test
を使うとオブジェクトの配置実験ができる。
デフォルトでは 10,000 個のオブジェクトを割り付けた時に各 OSD にどのようにマップされるかが示される。
--rule
を指定すると特定のルールのみをテスト、--num_rep
を指定するとレプリケーションの多重度を設定できる。
# crushtool -i /tmp/crush.bin.new --rule 0 --num_rep 2 --test devices weights (hex): [10000,10000,10000,10000,10000,10000,10000,10000] rule 0 (data), x = 0..9999 device 0: 2525 device 1: 2513 device 2: 2529 device 3: 2536 device 4: 2453 device 5: 2507 device 6: 2489 device 7: 2448 result size 2x: 10000
ほぼ均等になっているのが分かる。
次に 8 個の OSD の weight を傾けてみる。
host ceph1 { id -2 # do not change unnecessarily # weight 3.000 alg straw hash 0 # rjenkins1 item osd.0 weight 0.000 item osd.1 weight 0.062 item osd.2 weight 0.125 item osd.3 weight 0.250 item osd.4 weight 0.500 item osd.5 weight 1.000 item osd.6 weight 2.000 item osd.7 weight 4.000 }(これは CRUSH マップの一部)
この結果は以下のようになる。 weight の数値は倍々にしたがオブジェクトの割り付け量は倍々になっていない。 また weight に 0.000 を指定しても多少オブジェクトが割り付けられる。
# crushtool -i /tmp/crush.bin.new --rule 0 --num_rep 2 --test devices weights (hex): [10000,10000,10000,10000,10000,10000,10000,10000] rule 0 (data), x = 0..9999 device 0: 6 device 1: 355 device 2: 574 device 3: 1088 device 4: 1915 device 5: 3267 device 6: 5398 device 7: 7397 result size 2x: 10000
稼働中の CRUSH マップの確認
Ceph が稼働中動作した後に CRUSH マップを確認するには ceph osd tree
を使うとよい。
CRUSH マップの階層と重み付けが表示される。
# ceph osd tree 2012-03-09 02:00:53.036484 mon <- [osd,tree] 2012-03-09 02:00:53.036745 mon.0 -> 'dumped osdmap tree epoch 5' (0) # id weight type name up/down reweight -1 3 pool default -3 3 rack unknownrack -2 1 host host1 0 1 osd.0 up 1 -4 1 host host2 1 1 osd.1 up 1 -5 1 host host3 2 1 osd.2 up 1
例
いくつか例をあげてみる。
例1
上位階層には制約を設けず、レプリケーション数分の device を選択する。
step take default step choose firstn 0 type device step emit
この場合、同一 host の中の device が選択されることもある。
例2
まず rack の中から1つ選択肢、その rack の中からレプリケーション数分の host を選択する。 各 host からは 1 個づつ device を選択する。
step take default step choose firstn 1 type rack # L1 step choose firstn 0 type host step choose firstn 1 type device step emit
最初の step choose がない場合、host は異なる rack から選択されることもある。