ここは今からストレージです。

Ceph Pool の利用可能容量の計算法

f:id:ututaq:20200630133258p:plain

こんにちは、うつぼです。
今日は小ネタで Ceph の capacity usage について説明します。

Ceph はどれくらいの容量が使えるのか分かりづらいことで定評があります。
いや分かることは分かるんですが、「なんでそーなるの?」と欽ちゃんライクになる人もいるでしょう。今日はそこを紐解いていきましょう。

ceph df を見ていく

ceph df の出力

まず、Ceph の Pool の capacity usage は、ceph df コマンドで見ることができます。
0.5 TiB (512 GiB) の osd を1つだけ持つシンプルな 3 node の Ceph クラスターを用意します。直後に、

  • 3x replicated pool
  • 2x replicated pool
  • 2+1 erasure coded pool

の3つの Pool を作り、ceph df を実行してみます。

sh-4.4# ceph df   
RAW STORAGE:
    CLASS     SIZE        AVAIL       USED       RAW USED     %RAW USED 
    ssd       1.5 TiB     1.5 TiB     47 MiB      3.0 GiB          0.20 
    TOTAL     1.5 TiB     1.5 TiB     47 MiB      3.0 GiB          0.20 
 
POOLS:
    POOL           ID     STORED      OBJECTS     USED        %USED     MAX AVAIL 
    pool_3rep       1         0 B           0         0 B         0       433 GiB 
    pool_2rep       2         0 B           0         0 B         0       650 GiB 
    pool_2+1ec      3         0 B           0         0 B         0       867 GiB 

これはまだクライアントから全然使われてない状態です。

上の RAW STORAGE には全ての osd を合算した usage が書かれます。ここはここでまあいいとして、問題は下の POOLS です。

妙に少ない MAX AVAIL と full ratio

右端の MAX AVAIL が Pool ごとに違います。名前からしても、冗長度が違う Pool ごとに値が違うところからしても、Pool に保存できる最大のデータ容量のようですね。でも、ちょっと少ないですよね。
1 node あたり 512 GiB あるのだから、例えば 3x replicated pool の場合、仮に何やかんや無条件に内部処理で引かれる分 (個人的にこれを税金と呼んでいる) があるとしても、433 GiB は少なすぎる。

なぜこんなに少ないのか?どういう計算をしているのか?

これは、osd に設定されている full ratio が主な原因です。

sh-4.4# ceph osd dump | grep ^full_ratio
full_ratio 0.85

full ratio は、『osd の使用可能容量の何%まで使ったら osd は容量フルとして write できないようにする』というパラメータです。
つまり osd の容量に full ratio をかけた分が、Ceph で使える容量ということですね。

デフォルトは 0.95 なんですが、この環境では上の通り 0.85 でした。これを頭に入れて 3x replicated pool で計算をしてみると、

512 GiB x 0.85 = 435.2 GiB

大分近い数値になりました。

Pool の MAX AVAIL を計算する式

正確な計算式は、次のようになります。

 pool.MAX\ AVAIL = \\ \\ \ \ \ \ \{osd.AVAIL - ( osd.SIZE \times ( 1 - full\ ratio )\ )\ \} \times num\ of\ osd \div pool.size

osd.AVAIL は、その時点の osd の空き容量 であり、osd.SIZE は元々の osd の容量です。(ややこしい)
これらは ceph osd df で確認できます。

sh-4.4# ceph osd df
ID CLASS WEIGHT  REWEIGHT SIZE    RAW USE DATA   OMAP META  AVAIL   %USE VAR  PGS STATUS
 0   ssd 0.49899  1.00000 511 GiB 1.0 GiB 19 MiB  0 B 1 GiB 510 GiB 0.20 1.00 147     up
 1   ssd 0.49899  1.00000 511 GiB 1.0 GiB 19 MiB  0 B 1 GiB 510 GiB 0.20 1.00 146     up
 2   ssd 0.49899  1.00000 511 GiB 1.0 GiB 19 MiB  0 B 1 GiB 510 GiB 0.20 1.00 147     up
                    TOTAL 1.5 TiB 3.1 GiB 56 MiB  0 B 3 GiB 1.5 TiB 0.20

どの osdosd.AVAIL = 510 GiB、osd.SIZE = 511 GiB であることが分かります。これらと num of osd(osdの数) = 3 とpool.size=3(3x replicated pool) を式に代入すると、

{ 510 - ( 511 x ( 1 - 0.85 ) ) } x 3 / 3 = 433.35

となって MAX AVAIL が出てきます。
2x replicated pool では pool.size=2 として、2+1 ec pool では pool.size を (2+1)/2 とすることで、同様に計算できます。

  • 2x replicated pool --> pool.size = 2 : { 510 - ( 511 x ( 1 - 0.85 ) ) } x 3 / 2 = 650.025
  • 2+1 ec pool --> pool.size = (2+1)/2 : { 510 - ( 511 x ( 1 - 0.85 ) ) } x 3 / { (2+1) / 2 } = 866.7

osd の AVAIL がバラバラな場合

クラスター作成直後だとデータが入っていないので osd の AVAIL は同じでした。しかしデータが入ってくると osd の AVAIL はバラバラになります。Ceph は精密に全ての osd を均等に利用することはないからです。

先程のクラスターにデータを入れ始めて、途中で osd を 6つに増やしてまた入れた結果が次の例です。

sh-4.4# ceph df
RAW STORAGE:
    CLASS     SIZE        AVAIL       USED        RAW USED     %RAW USED 
    ssd       3.0 TiB     2.3 TiB     658 GiB      664 GiB         21.65 
    TOTAL     3.0 TiB     2.3 TiB     658 GiB      664 GiB         21.65 
 
POOLS:
    POOL           ID     STORED      OBJECTS     USED        %USED     MAX AVAIL 
    pool_3rep       1     112 GiB      28.23k     337 GiB     18.76       487 GiB 
    pool_2rep       2     113 GiB      26.95k     227 GiB     13.45       730 GiB 
    pool_2+1ec      3      62 GiB      15.49k      93 GiB      6.01       973 GiB 
sh-4.4#
sh-4.4# ceph osd df
ID CLASS WEIGHT  REWEIGHT SIZE    RAW USE DATA    OMAP    META     AVAIL   %USE  VAR  PGS STATUS 
 0   ssd 0.49899  1.00000 511 GiB 191 GiB 190 GiB  36 KiB 1024 MiB 320 GiB 37.31 1.72 110     up 
 3   ssd 0.49899  1.00000 511 GiB  43 GiB  42 GiB     0 B    1 GiB 468 GiB  8.33 0.38  70     up 
 1   ssd 0.49899  1.00000 511 GiB 184 GiB 183 GiB  35 KiB 1024 MiB 327 GiB 35.96 1.66 112     up 
 4   ssd 0.49899  1.00000 511 GiB  15 GiB  14 GiB     0 B    1 GiB 496 GiB  3.00 0.14  64     up 
 2   ssd 0.49899  1.00000 511 GiB 186 GiB 185 GiB  36 KiB 1024 MiB 325 GiB 36.39 1.68 101     up 
 5   ssd 0.49899  1.00000 511 GiB  45 GiB  44 GiB     0 B    1 GiB 466 GiB  8.89 0.41  79     up 
                    TOTAL 3.0 TiB 664 GiB 658 GiB 108 KiB  6.0 GiB 2.3 TiB 21.65                 
MIN/MAX VAR: 0.14/1.72  STDDEV: 15.03

osd の AVAIL がばらついています。この場合でも Pool の MAX AVAIL は表示されているのですが、これの場合はどう計算するのでしょうか。

正解は、『一番少ない osd.AVAIL を数値を先の計算式に入れて計算する』です。

上の 6 つの osd の内、最も osd.AVAIL が少ないのは、osd.0 の 320 GiB です。これと osd.SIZE = 511 と num of osd=6 を計算式に入れると、

  • 3x replicated pool: { 320 - ( 511 x ( 1 - 0.85 ) ) } x 6 / 3 = 486.7
  • 2x replicated pool: { 320 - ( 511 x ( 1 - 0.85 ) ) } x 6 / 2 = 730.05
  • 2+1 ec pool : { 320 - ( 511 x ( 1 - 0.85 ) ) } x 6 / { (2+1) / 2 } = 973.4

となり、MAX AVAIL と同じことが分かります。

改めて全ての osd.AVAIL の中で最小の値を採用することを反映した計算式と、この様子を図で表したものを載せます。

 pool.MAX\ AVAIL = \\ \\ \ \ \ \ \{\ min(osd.AVAIL) - ( osd.SIZE \times ( 1 - full\ ratio )\ )\ \} \times num\ of\ osd \div pool.size
f:id:ututaq:20200630225712p:plain
Ceph Pool の MAX AVAIL

まとめ

Ceph Pool の capacity usage について説明しました。
Ceph の設計をする時には、full ratio を加味して容量を見積もる必要があることは盲点になりやすいので、注意しましょう。full ratio line の上にある屋根裏部屋は (デフォルトで) 5% ですが、12 TB の玉を使えば 600GB ですから、なかなかバカになりません。
また MAX AVAIL の計算方法も紹介しましたが、はっきり言ってこれが分からないことで運用上すごく困ることにはあんまりならないと思います。あくまで小ネタです。

Pool で利用できる領域が減ってくると、そのうち osd nearfull の WARNING が出てきます。もうすぐ full になるから対処してねというやつです。nearful ratio はデフォルトで 0.85 です。
nearfull が来たらすぐに osd を追加しましょう。それが本道です。とは言え、どうしても新しい玉が来るまで持ちこたえられないという時の緊急対処として、full ratio を引き上げるとか、(pg のばらつきが大きくないと効果ゼロだが) osd を reweight して rebalance してみるとかして、既存 osd の AVAIL を押し広げて多少持ちこたえる小技があります。

とまあ最後まで小ネタでした。今日はここまで。

Cephクラスタのupgrade

f:id:ututaq:20191125022735p:plain

この記事はRookだらけの Advent Calendar 2019 8日目の記事です。

今日は超軽くRook-Cephで作ったCephクラスタのupgradeを説明します。

Cephのupgrade

Cephは基本的にコンポーネント冗長化されて動いているので、1つずつrolling upgradeすることでストレージ的にはNon-disruptiveなupgradeができます。
Rook-Cephではoperatorがその辺りを自動的にやってくれるので、人がやることは超少ないです。というわけでやってみましょう。

いつもどおり3master+3workerで各workerにそれぞれ2つOSDをぶら下げたCephクラスタを作ります。

[utubo@tutsunom ceph]$ kubectl get node --sort-by=".metadata.creationTimestamp"
NAME                             STATUS   ROLES    AGE   VERSION
ip-172-20-110-218.ec2.internal   Ready    master   38h   v1.15.5
ip-172-20-58-245.ec2.internal    Ready    master   38h   v1.15.5
ip-172-20-68-55.ec2.internal     Ready    master   38h   v1.15.5
ip-172-20-125-18.ec2.internal    Ready    node     38h   v1.15.5
ip-172-20-60-20.ec2.internal     Ready    node     12h   v1.15.5
ip-172-20-81-195.ec2.internal    Ready    node     12h   v1.15.5
[utubo@tutsunom ceph]$ kubectl -n rook-ceph get pod
NAME                                                        READY   STATUS      RESTARTS   AGE
csi-cephfsplugin-provisioner-974b566d9-8svhf                4/4     Running     0          6h21m
csi-cephfsplugin-provisioner-974b566d9-hxfx2                4/4     Running     0          6h21m
csi-cephfsplugin-q7m66                                      3/3     Running     0          6h21m
csi-cephfsplugin-r58wx                                      3/3     Running     0          6h21m
csi-cephfsplugin-w4pfg                                      3/3     Running     0          6h21m
csi-rbdplugin-fhbqj                                         3/3     Running     0          6h21m
csi-rbdplugin-jf5c9                                         3/3     Running     0          6h21m
csi-rbdplugin-provisioner-579c546f5-9nh7z                   5/5     Running     0          6h21m
csi-rbdplugin-provisioner-579c546f5-pvf76                   5/5     Running     0          6h21m
csi-rbdplugin-v9p2k                                         3/3     Running     0          6h21m
rook-ceph-mgr-a-649dcd8f8-z4bf9                             1/1     Running     0          6h14m
rook-ceph-mon-a-fb9dc8bf7-7tpfh                             1/1     Running     0          6h14m
rook-ceph-mon-b-7cbdf8b8bf-q8m89                            1/1     Running     0          6h15m
rook-ceph-mon-c-764f6b7c48-crlhc                            1/1     Running     0          6h15m
rook-ceph-operator-fb8b96548-cjzsp                          1/1     Running     0          6h21m
rook-ceph-osd-0-54cc869884-s7lx7                            1/1     Running     0          6h13m
rook-ceph-osd-1-69bb658d69-2x72t                            1/1     Running     0          6h12m
rook-ceph-osd-2-6886955f66-xxmjk                            1/1     Running     0          6h13m
rook-ceph-osd-3-c9fd5bdb6-l7n6r                             1/1     Running     0          6h13m
rook-ceph-osd-4-6fdc7dbb45-vvmqb                            1/1     Running     0          6h12m
rook-ceph-osd-5-66cbbf745-f6dwx                             1/1     Running     0          6h13m
rook-ceph-osd-prepare-ip-172-20-125-18.ec2.internal-h2rxc   0/1     Completed   0          6h14m
rook-ceph-osd-prepare-ip-172-20-60-20.ec2.internal-fm8h5    0/1     Completed   0          6h14m
rook-ceph-osd-prepare-ip-172-20-81-195.ec2.internal-rd9zk   0/1     Completed   0          6h14m
rook-ceph-tools-5bc668d889-44wq2                            1/1     Running     0          6h18m
rook-discover-dmgtp                                         1/1     Running     0          6h21m
rook-discover-p94l5                                         1/1     Running     0          6h21m
rook-discover-x97hw                                         1/1     Running     0          6h21m
[utubo@tutsunom ceph]$ kubectl exec -it `kubectl get pod -l app=rook-ceph-tools -o 'jsonpath={.items[].metadata.name}'` ceph version
ceph version 14.2.2 (4f8fa0a0024755aae7d95567c63f11d6862d55be) nautilus (stable)

はいはい。

それではこのCephクラスタを14.2.2から14.2.4にupgradeします。やることは一つです。CephClusterリソースのimageのタグを書き換えるだけ。

[utubo@tutsunom ceph]$ kubectl -n rook-ceph patch CephCluster rook-ceph --type=merge -p "{\"spec\": {\"cephVersion\": {\"image\": \"ceph/ceph:v14.2.4-20191204\"}}}"

これだけで後はRook-Ceph Operatorが全部やってくれます。その様子を追っかけてみましょう。
upgrade中、別窓で↓のようにDeploymentの"READY/UP-TO-DATE/AVAILABLE"とceph-versionをwatchで見ておくとどうなるか。 ※出力が長くなるので注目する部分だけ書きます。

[utubo@tutsunom ceph]$ watch --exec kubectl -n rook-ceph get deploy -l rook_cluster=rook-ceph -o jsonpath='{range .items[*]}{.metadata.name}{"  \treq/upd/avl: "}{.spec.replicas}{"/"}{.status.updatedReplicas}{"/"}{.status.readyReplicas}{"  \tceph-version="}{.metadata.labels.ceph-version}{"\n"}{end}'
rook-ceph-mgr-a         req/upd/avl: 1/1/1  ceph-version=14.2.2
rook-ceph-mon-a         req/upd/avl: 1/1/1  ceph-version=14.2.2
rook-ceph-mon-b         req/upd/avl: 1/1/1  ceph-version=14.2.2
rook-ceph-mon-c         req/upd/avl: 1/1/1  ceph-version=14.2.2
rook-ceph-osd-0         req/upd/avl: 1/1/1  ceph-version=14.2.2
rook-ceph-osd-1         req/upd/avl: 1/1/1  ceph-version=14.2.2
rook-ceph-osd-2         req/upd/avl: 1/1/1  ceph-version=14.2.2
rook-ceph-osd-3         req/upd/avl: 1/1/1  ceph-version=14.2.2
rook-ceph-osd-4         req/upd/avl: 1/1/1  ceph-version=14.2.2
rook-ceph-osd-5         req/upd/avl: 1/1/1  ceph-version=14.2.2

↓

rook-ceph-mon-a         req/upd/avl: 1/1/1  ceph-version=14.2.2
rook-ceph-mon-b         req/upd/avl: 1/1/1  ceph-version=14.2.2
rook-ceph-mon-c         req/upd/avl: 1//    ceph-version=14.2.4

↓

rook-ceph-mon-a         req/upd/avl: 1/1/   ceph-version=14.2.4
rook-ceph-mon-b         req/upd/avl: 1/1/1  ceph-version=14.2.2
rook-ceph-mon-c         req/upd/avl: 1/1/1  ceph-version=14.2.4

↓

rook-ceph-mon-a         req/upd/avl: 1/1/1  ceph-version=14.2.4
rook-ceph-mon-b         req/upd/avl: 1/1/   ceph-version=14.2.4
rook-ceph-mon-c         req/upd/avl: 1/1/1  ceph-version=14.2.4

まず初めにMONがupgradeされ。今回はmon-c -> mon-a -> mon-bの順序でしたが、別の機会はまた違った順序だったので特に順番は決まって居ないようです。1つずつupgradeするのは間違いないです。

続き、

↓

rook-ceph-mgr-a         req/upd/avl: 1//    ceph-version=14.2.4

↓

rook-ceph-mgr-a         req/upd/avl: 1/1/1  ceph-version=14.2.4

次にMGRがupgrade。続いて、

rook-ceph-osd-0         req/upd/avl: 1/1/1  ceph-version=14.2.2
rook-ceph-osd-1         req/upd/avl: 1/1/1  ceph-version=14.2.2
rook-ceph-osd-2         req/upd/avl: 1/1/1  ceph-version=14.2.2
rook-ceph-osd-3         req/upd/avl: 1/1/1  ceph-version=14.2.2
rook-ceph-osd-4         req/upd/avl: 1/1/1  ceph-version=14.2.2
rook-ceph-osd-5         req/upd/avl: 1/1/   ceph-version=14.2.4

↓

rook-ceph-osd-0         req/upd/avl: 1/1/1  ceph-version=14.2.2
rook-ceph-osd-1         req/upd/avl: 1/1/1  ceph-version=14.2.2
rook-ceph-osd-2         req/upd/avl: 1//    ceph-version=14.2.4
rook-ceph-osd-3         req/upd/avl: 1/1/1  ceph-version=14.2.2
rook-ceph-osd-4         req/upd/avl: 1/1/1  ceph-version=14.2.2
rook-ceph-osd-5         req/upd/avl: 1/1/1  ceph-version=14.2.4

↓

rook-ceph-osd-0         req/upd/avl: 1/1/   ceph-version=14.2.4
rook-ceph-osd-1         req/upd/avl: 1/1/1  ceph-version=14.2.2
rook-ceph-osd-2         req/upd/avl: 1/1/1  ceph-version=14.2.4
rook-ceph-osd-3         req/upd/avl: 1/1/1  ceph-version=14.2.2
rook-ceph-osd-4         req/upd/avl: 1/1/1  ceph-version=14.2.2
rook-ceph-osd-5         req/upd/avl: 1/1/1  ceph-version=14.2.4

↓

rook-ceph-osd-0         req/upd/avl: 1/1/1  ceph-version=14.2.4
rook-ceph-osd-1         req/upd/avl: 1/1/1  ceph-version=14.2.2
rook-ceph-osd-2         req/upd/avl: 1/1/1  ceph-version=14.2.4
rook-ceph-osd-3         req/upd/avl: 1//    ceph-version=14.2.4
rook-ceph-osd-4         req/upd/avl: 1/1/1  ceph-version=14.2.2
rook-ceph-osd-5         req/upd/avl: 1/1/1  ceph-version=14.2.4

↓

rook-ceph-osd-0         req/upd/avl: 1/1/1  ceph-version=14.2.4
rook-ceph-osd-1         req/upd/avl: 1/1/   ceph-version=14.2.4
rook-ceph-osd-2         req/upd/avl: 1/1/1  ceph-version=14.2.4
rook-ceph-osd-3         req/upd/avl: 1/1/1  ceph-version=14.2.4
rook-ceph-osd-4         req/upd/avl: 1/1/1  ceph-version=14.2.2
rook-ceph-osd-5         req/upd/avl: 1/1/1  ceph-version=14.2.4

↓

rook-ceph-osd-0         req/upd/avl: 1/1/1  ceph-version=14.2.4
rook-ceph-osd-1         req/upd/avl: 1/1/1  ceph-version=14.2.4
rook-ceph-osd-2         req/upd/avl: 1/1/1  ceph-version=14.2.4
rook-ceph-osd-3         req/upd/avl: 1/1/1  ceph-version=14.2.4
rook-ceph-osd-4         req/upd/avl: 1/1/1  ceph-version=14.2.4
rook-ceph-osd-5         req/upd/avl: 1/1/1  ceph-version=14.2.4

という感じで最後にOSDがupgradeされます。

ここではosdが5,2,0,3,1,4という順序でupgradeされました。Cephのosd treeを見てみるとworker nodeごとにupgradeをかけていることがわかります。

[utubo@tutsunom ceph]$ kubectl exec -it `kubectl get pod -l app=rook-ceph-tools -o 'jsonpath={.items[].metadata.name}'` ceph osd tree
ID  CLASS WEIGHT  TYPE NAME                                      STATUS REWEIGHT PRI-AFF 
 -1       0.16974 root default                                                           
 -5       0.16974     region us-east-1                                                   
 -4       0.05658         zone us-east-1a                                                
 -3       0.05658             host ip-172-20-60-20-ec2-internal                          
  0   ssd 0.02829                 osd.0                              up  1.00000 1.00000 
  3   ssd 0.02829                 osd.3                              up  1.00000 1.00000 
-10       0.05658         zone us-east-1b                                                
 -9       0.05658             host ip-172-20-81-195-ec2-internal                         
  1   ssd 0.02829                 osd.1                              up  1.00000 1.00000 
  4   ssd 0.02829                 osd.4                              up  1.00000 1.00000 
-14       0.05658         zone us-east-1c                                                
-13       0.05658             host ip-172-20-125-18-ec2-internal                         
  2   ssd 0.02829                 osd.2                              up  1.00000 1.00000 
  5   ssd 0.02829                 osd.5                              up  1.00000 1.00000 

最後にCephクラスタでバージョンを確認すると、

[utubo@tutsunom ceph]$ kubectl exec -it `kubectl get pod -l app=rook-ceph-tools -o 'jsonpath={.items[].metadata.name}'` ceph version
ceph version 14.2.4 (75f4de193b3ea58512f204623e6c5a16e6c1e1ba) nautilus (stable)

はいおわり。

コマンド一個打つだけでMON -> MGR -> OSDとそれぞれ1つずつupgradeしてくれるのだからRook-Ceph operatorは偉大です。

まとめ

今日は軽くCephクラスタのupgradeについて説明しました。
今回は同一メジャーバージョン間のupgradeでしたが、メジャーバージョンをまたぐとOSDメタデータの持ち方とかが変わってデバイスをattachできないとか(特にBlueStoreの場合)があるかもしれないので注意が必要かもです(適当) ※13->14のupgradeにことごとく失敗して、原因がまだ調査できていない(汗)
あと、本当はRook-Ceph operator自身のupgradeも説明すべきですが、時間がなくて検証できていないので続きは赤帽エンジニアブログで書きます。

Rookは対応するSDSやDBのoperatorで、ストレージの構築から運用まで強力に手助けしてくれる革命的なオーケストレーションツールです。
みなさんぜひ試してみて下さいね。

それでは今日でRookだらけのAdvent Calendar 2019の私のパートは終わりです。8日連続でお付き合いいただきありがとうございました。
引き続きtzkobaさんとmakotowさんという豪華顔ぶれの説明をお楽しみください!

Rook-Ceph OSD on PVC(後半)

f:id:ututaq:20191125022735p:plain

この記事はRookだらけの Advent Calendar 2019 7日目の記事です。

昨日OSD on PVCのCephクラスタをデプロイできたのはいいものの、OSDがうまく散らばってくれないという問題に直面しました。
しかも織り込み済みの問題だという。今回はこの問題を回避する方法の一例を紹介します。

そういうわけで、今の時点ではマニュアルで3nodeの散らばるように書く必要がありそうです。

3nodeに散らばるようにする方法

Kubernetesにまかせてカッコよくosdを散らすのは難しそうなので、マニュアルで散らせます。
色々失敗した検討した結果、こんな感じでNode Affinityを使ったら3osdずつ散らばりました。

[utubo@tutsunom ceph]$ cat my-cluster-on-pvc.yaml 
apiVersion: ceph.rook.io/v1
kind: CephCluster
metadata:
  name: rook-ceph
  namespace: rook-ceph
spec:
  dataDirHostPath: /var/lib/rook
  mon:
    count: 3
    allowMultiplePerNode: false
    volumeClaimTemplate:
      spec:
        storageClassName: my-gp2
        resources:
          requests:
            storage: 10Gi
  cephVersion:
    image: ceph/ceph:v14.2.4-20190917
    allowUnsupported: false
  dashboard:
    enabled: true
    ssl: true
  network:
    hostNetwork: false
  storage:
    topologyAware: true
    storageClassDeviceSets:
    - name: set-1a
      count: 3
      portable: true
      placement:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: failure-domain.beta.kubernetes.io/zone
                operator: In
                values:
                  - us-east-1a
      resources:
      volumeClaimTemplates:
      - metadata:
          name: data
        spec:
          resources:
            requests:
              storage: 10Gi
          storageClassName: my-gp2
          volumeMode: Block
          accessModes:
            - ReadWriteOnce
    - name: set-1b
      count: 3
      portable: true
      placement:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: failure-domain.beta.kubernetes.io/zone
                operator: In
                values:
                  - us-east-1b
      resources:
      volumeClaimTemplates:
      - metadata:
          name: data
        spec:
          resources:
            requests:
              storage: 10Gi
          storageClassName: my-gp2
          volumeMode: Block
          accessModes:
            - ReadWriteOnce
    - name: set-1c
      count: 3
      portable: true
      placement:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: failure-domain.beta.kubernetes.io/zone
                operator: In
                values:
                  - us-east-1c
      resources:
      volumeClaimTemplates:
      - metadata:
          name: data
        spec:
          resources:
            requests:
              storage: 10Gi
          storageClassName: my-gp2
          volumeMode: Block
          accessModes:
            - ReadWriteOnce
  disruptionManagement:
    managePodBudgets: false
    osdMaintenanceTimeout: 30
    manageMachineDisruptionBudgets: false
    machineDisruptionBudgetNamespace: openshift-machine-api

どうですか。ダサい、ダサすぎる。と風が語りかけてくるようです。

恥ずかしい思いをグッと我慢して解説すると、storageClassDeviceSets:OSDのセットを指定します。
サンプルのyamlだと、9個のOSDを1つのセットにするからPod AntiAffinity ruleでうまく分けろって言っているのです。
しかし、9個の唐揚げを1枚の皿に乗せて3人の子供達に仲良く分けて食えと言ってもうまくいかないように、(今のルールだと)意図するように分けるのはなかなか難しい。
そこで3個のOSDのセットを3つ用意してそれぞれのzoneにあるworker nodeに分けさせるruleにしてるのが、上のyamlです。
3枚の皿に3個ずつ唐揚げを乗せて1皿ずつ取れって形にしたら話早いやん、という感じです。

めっちゃhard codedなんで結局これかい感満々ですが、これでもう一回クラスタをデプロイしてみましょう。

[utubo@tutsunom ceph]$ kubectl create -f my-cluster-on-pvc.yaml
[utubo@tutsunom ceph]$ kubectl -n rook-ceph get pod
NAME                                              READY   STATUS      RESTARTS   AGE
csi-cephfsplugin-5cp9g                            3/3     Running     0          5m44s
csi-cephfsplugin-fhr5b                            3/3     Running     0          5m44s
csi-cephfsplugin-l6556                            3/3     Running     0          5m44s
csi-cephfsplugin-provisioner-974b566d9-frwz6      4/4     Running     0          5m44s
csi-cephfsplugin-provisioner-974b566d9-tqswk      4/4     Running     0          5m44s
csi-rbdplugin-4qxgj                               3/3     Running     0          5m44s
csi-rbdplugin-crc8t                               3/3     Running     0          5m44s
csi-rbdplugin-h5dkw                               3/3     Running     0          5m44s
csi-rbdplugin-provisioner-579c546f5-sfx72         5/5     Running     0          5m44s
csi-rbdplugin-provisioner-579c546f5-z7rmk         5/5     Running     0          5m44s
rook-ceph-mgr-a-7cf896f748-m7czk                  1/1     Running     0          3m37s
rook-ceph-mon-a-568dc96b5f-xl9x4                  1/1     Running     0          4m49s
rook-ceph-mon-b-5cbc9dcd9d-gxnjp                  1/1     Running     0          4m32s
rook-ceph-mon-c-647db6d4b5-wmkzg                  1/1     Running     0          4m1s
rook-ceph-operator-fb8b96548-n9jv6                1/1     Running     0          5m52s
rook-ceph-osd-0-8cbc88874-k6dlj                   1/1     Running     0          2m2s
rook-ceph-osd-1-5b746b478f-fzs8z                  1/1     Running     0          2m3s
rook-ceph-osd-2-64c746c558-mwzn2                  1/1     Running     0          2m7s
rook-ceph-osd-3-86f8984495-b7b7n                  1/1     Running     0          2m5s
rook-ceph-osd-4-67889676b9-wjswz                  1/1     Running     0          2m2s
rook-ceph-osd-5-687847c9f-6z7ps                   1/1     Running     0          2m3s
rook-ceph-osd-6-f8997d5b7-bmblb                   1/1     Running     0          114s
rook-ceph-osd-7-65dc45747c-fckw7                  1/1     Running     0          111s
rook-ceph-osd-8-76bf67bcf8-s9vmm                  1/1     Running     0          114s
rook-ceph-osd-prepare-set-1a-0-data-kx7k4-n4wrn   0/1     Completed   0          3m10s
rook-ceph-osd-prepare-set-1a-1-data-kdwjp-n4fm9   0/1     Completed   0          3m9s
rook-ceph-osd-prepare-set-1a-2-data-j5fm6-7vjwg   0/1     Completed   0          3m8s
rook-ceph-osd-prepare-set-1b-0-data-tsv5x-mcx9q   0/1     Completed   0          3m8s
rook-ceph-osd-prepare-set-1b-1-data-gdpqg-9knxk   0/1     Completed   0          3m7s
rook-ceph-osd-prepare-set-1b-2-data-jfwcn-d67gp   0/1     Completed   0          3m7s
rook-ceph-osd-prepare-set-1c-0-data-9tcl4-87946   0/1     Completed   0          3m6s
rook-ceph-osd-prepare-set-1c-1-data-h4wk7-c9d76   0/1     Completed   0          3m5s
rook-ceph-osd-prepare-set-1c-2-data-svq64-9sdzb   0/1     Completed   0          3m5s
rook-discover-fmprp                               1/1     Running     0          5m51s
rook-discover-m895f                               1/1     Running     0          5m51s
rook-discover-wnpj7                               1/1     Running     0          5m51s

ここまではええねん。MONとOSDは?

[utubo@tutsunom ceph]$ kubectl -n rook-ceph get pod -l app=rook-ceph-mon -o wide
NAME                               READY   STATUS    RESTARTS   AGE     IP             NODE                            NOMINATED NODE   READINESS GATES
rook-ceph-mon-a-568dc96b5f-xl9x4   1/1     Running   0          5m19s   100.96.6.111   ip-172-20-93-28.ec2.internal    <none>           <none>
rook-ceph-mon-b-5cbc9dcd9d-gxnjp   1/1     Running   0          5m2s    100.96.7.178   ip-172-20-42-193.ec2.internal   <none>           <none>
rook-ceph-mon-c-647db6d4b5-wmkzg   1/1     Running   0          4m31s   100.96.8.99    ip-172-20-102-19.ec2.internal   <none>           <none>
[utubo@tutsunom ceph]$ kubectl -n rook-ceph get pod -l app=rook-ceph-osd -o wide
NAME                               READY   STATUS    RESTARTS   AGE     IP             NODE                            NOMINATED NODE   READINESS GATES
rook-ceph-osd-0-8cbc88874-k6dlj    1/1     Running   0          2m37s   100.96.6.118   ip-172-20-93-28.ec2.internal    <none>           <none>
rook-ceph-osd-1-5b746b478f-fzs8z   1/1     Running   0          2m38s   100.96.8.104   ip-172-20-102-19.ec2.internal   <none>           <none>
rook-ceph-osd-2-64c746c558-mwzn2   1/1     Running   0          2m42s   100.96.8.103   ip-172-20-102-19.ec2.internal   <none>           <none>
rook-ceph-osd-3-86f8984495-b7b7n   1/1     Running   0          2m40s   100.96.6.116   ip-172-20-93-28.ec2.internal    <none>           <none>
rook-ceph-osd-4-67889676b9-wjswz   1/1     Running   0          2m37s   100.96.6.117   ip-172-20-93-28.ec2.internal    <none>           <none>
rook-ceph-osd-5-687847c9f-6z7ps    1/1     Running   0          2m38s   100.96.7.182   ip-172-20-42-193.ec2.internal   <none>           <none>
rook-ceph-osd-6-f8997d5b7-bmblb    1/1     Running   0          2m29s   100.96.8.105   ip-172-20-102-19.ec2.internal   <none>           <none>
rook-ceph-osd-7-65dc45747c-fckw7   1/1     Running   0          2m26s   100.96.7.184   ip-172-20-42-193.ec2.internal   <none>           <none>
rook-ceph-osd-8-76bf67bcf8-s9vmm   1/1     Running   0          2m29s   100.96.7.183   ip-172-20-42-193.ec2.internal   <none>           <none>

お、3個ずつ散らばってますね。(そらそうやろ

Cephの観点で見た注意点

ここでちょっとCephクラスタを見てみましょう。

[utubo@tutsunom ceph]$ kubectl create -f toolbox.yaml 
[utubo@tutsunom ceph]$  kubectl -n rook-ceph exec -it `kubectl get pod -l app=rook-ceph-tools -o 'jsonpath={.items[].metadata.name}'` ceph osd tree
ID  CLASS WEIGHT  TYPE NAME                            STATUS REWEIGHT PRI-AFF 
 -1       0.07910 root default                                                 
 -5       0.07910     region us-east-1                                         
-14       0.02637         zone us-east-1a                                      
-13       0.00879             host set-1a-0-data-kx7k4                         
  5   ssd 0.00879                 osd.5                    up  1.00000 1.00000 
-27       0.00879             host set-1a-1-data-kdwjp                         
  7   ssd 0.00879                 osd.7                    up  1.00000 1.00000 
-23       0.00879             host set-1a-2-data-j5fm6                         
  8   ssd 0.00879                 osd.8                    up  1.00000 1.00000 
-10       0.02637         zone us-east-1b                                      
 -9       0.00879             host set-1b-0-data-tsv5x                         
  3   ssd 0.00879                 osd.3                    up  1.00000 1.00000 
-17       0.00879             host set-1b-1-data-gdpqg                         
  0   ssd 0.00879                 osd.0                    up  1.00000 1.00000 
-19       0.00879             host set-1b-2-data-jfwcn                         
  4   ssd 0.00879                 osd.4                    up  1.00000 1.00000 
 -4       0.02637         zone us-east-1c                                      
 -3       0.00879             host set-1c-0-data-9tcl4                         
  2   ssd 0.00879                 osd.2                    up  1.00000 1.00000 
-25       0.00879             host set-1c-1-data-h4wk7                         
  6   ssd 0.00879                 osd.6                    up  1.00000 1.00000 
-21       0.00879             host set-1c-2-data-svq64                         
  1   ssd 0.00879                 osd.1                    up  1.00000 1.00000 

なんか3つずつに分かれて良さそうに見えますね。しかしこのトポロジーではCephのデフォルトのCRUSH ruleを使う場合に落とし穴があります。
各zoneの中に3つのhostがありますね。この3つのhostは実際は同一のworker nodeなんですが、Cephとしては異なるhostが3つあるトポロジーとして捉えます。そして各hostが1つosdを持ちます。

デフォルトのreplicated_ruleは「regionとかzoneとか無視して、とにかく1つhostを選んでその中のosdを1つ選ぶ」というルールになっています。

[utubo@tutsunom ceph]$ kubectl -n rook-ceph exec -it `kubectl get pod -l app=rook-ceph-tools -o 'jsonpath={.items[].metadata.name}'` sh
sh-4.2# ceph osd getcrushmap -o crushmap.bin
sh-4.2# crushtool -d crushmap.bin -o crushmap.txt 
sh-4.2# grep -A 10 rules crushmap.txt 
# rules
rule replicated_rule {
    id 0
    type replicated
    min_size 1
    max_size 10
    step take default
    step chooseleaf firstn 0 type host
    step emit
}

つまり、上記のようなトポロジーのCephクラスタでreplicated_ruleを使って3x Replicated Poolを作ると、同一のzoneから3つのOSDを選んだPGが作られる可能性があります。
実際やって見てみましょう。面倒なんでCephで直接コマンド打ってPoolを作ります。すんません。

[utubo@tutsunom ceph]$ kubectl -n rook-ceph exec -it `kubectl get pod -l app=rook-ceph-tools -o 'jsonpath={.items[].metadata.name}'` ceph osd pool create test 256 256
pool 'test' created
[utubo@tutsunom ceph]$ kubectl -n rook-ceph exec -it `kubectl get pod -l app=rook-ceph-tools -o 'jsonpath={.items[].metadata.name}'` ceph pg ls-by-pool test
PG   OBJECTS DEGRADED MISPLACED UNFOUND BYTES OMAP_BYTES* OMAP_KEYS* LOG STATE        SINCE VERSION REPORTED UP        ACTING    SCRUB_STAMP                DEEP_SCRUB_STAMP           
1.0        0        0         0       0     0           0          0   0 active+clean   26s     0'0    26:10 [0,1,2]p0 [0,1,2]p0 2019-12-05 03:57:01.395376 2019-12-05 03:57:01.395376 
1.1        0        0         0       0     0           0          0   0 active+clean   26s     0'0    26:10 [5,2,0]p5 [5,2,0]p5 2019-12-05 03:57:01.395376 2019-12-05 03:57:01.395376 
1.2        0        0         0       0     0           0          0   0 active+clean   26s     0'0    26:10 [4,1,2]p4 [4,1,2]p4 2019-12-05 03:57:01.395376 2019-12-05 03:57:01.395376 
1.3        0        0         0       0     0           0          0   0 active+clean   26s     0'0    26:10 [7,2,4]p7 [7,2,4]p7 2019-12-05 03:57:01.395376 2019-12-05 03:57:01.395376 
1.4        0        0         0       0     0           0          0   0 active+clean   26s     0'0    26:10 [7,4,2]p7 [7,4,2]p7 2019-12-05 03:57:01.395376 2019-12-05 03:57:01.395376 
1.5        0        0         0       0     0           0          0   0 active+clean   26s     0'0    26:10 [8,1,4]p8 [8,1,4]p8 2019-12-05 03:57:01.395376 2019-12-05 03:57:01.395376
...
1.34       0        0         0       0     0           0          0   0 active+clean   26s     0'0    26:10 [6,2,1]p6 [6,2,1]p6 2019-12-05 03:57:01.395376 2019-12-05 03:57:01.395376 
...
1.97       0        0         0       0     0           0          0   0 active+clean   26s     0'0    26:10 [5,7,8]p5 [5,7,8]p5 2019-12-05 03:57:01.395376 2019-12-05 03:57:01.395376 
...
1.c2       0        0         0       0     0           0          0   0 active+clean   26s     0'0    26:10 [0,3,4]p0 [0,3,4]p0 2019-12-05 03:57:01.395376 2019-12-05 03:57:01.395376 
...

ceph osd pool createコマンドでruleを指定せずにpoolを作るとデフォルトのreplicated_ruleが使われるのですが、どうでしょう。
1.34に割り当てられたOSD[6,2,1]は、ceph osd treeを見ると3つともzone us-east-1cから取られています。が、実際は1つのworker nodeにattachされてるOSDです。1.971.c2も同様です。
つまりworker nodeに障害が起きるとこれらのPGは完全にアウトとなり、データロスとなります。全然冗長化されてないpoolなので、これはアカン。

だから何かしら対応しなくてはいけません。
トポロジーを変えるのはRook-Ceph側の扱いになって難しそうなので、適切なCRUSH ruleを作って指定する方がよいでしょう。例えばこんな感じのruleを作ったらいけるはずです。

# rules
rule spread_across_all_zones {
        ruleset 1
        type replicated
        min_size 1
        max_size 10
        step take default
        step choose firstn 0 type zone
        step chooseleaf firstn 1 type host
        step emit
}

まとめ

前後半にわたって、OSD on PVCについて説明しました。worker nodeにブロックデバイスをぶら下げる作業がしなくていいのでKubernetes側でデバイス管理でき、Cloud Native Infra感出ますよね。本当に便利だと思います。
でも個人的にOSD on PVCで一番うれしいのは、Cephクラスタを作り直すたびにworker nodeに入ってMONが使うCephの構成ファイル(/var/lib/rook以下)をいちいち消さなくてよかったり、OSDになるデバイスをきれいに消してLVMのエントリーを消したりしなくていいところだったりします。クラスタ消したら全部自動的にPVCも消えてきれいさっぱり無くなるからです。本番のKubernetesクラスタでは全然関係ないけれど、テストする上ではスムーズにクラスタのdelete/createが繰り返しができるので、ホントーーーーにありがたい。

OSDをきれいに散らす方法は、Kubernetes一年生のうつぼではNode Affinityを使ったダサい方法しか思いつかなかったけど、やっぱりPod (Anti)Affinityをうまく使ったやりかたがあるんでしょうね。それかPod Topology Spread Constraintsを待つんでしょう。
こちらのissueを見ると、Rook 1.2にはPod Topology Spread Constraintsに対応するとのことです。
Pod Topology Spread Constraintsについてはsatさんが追究されています。やはり今日本で一番のRook-Cepherだと思います。最敬礼。

blog.cybozu.io

というわけで、今回はおしまい。

BGMは境界の彼方でした。

Rook-Ceph OSD on PVC(前半)

f:id:ututaq:20191125022735p:plain

この記事はRookだらけの Advent Calendar 2019 6日目の記事です。

Rook 1.1でできるようになった OSD on PVC の説明をします。

OSD on PVC ???

OSD on PVCというのは、ceph-osdをPVC から作ろうということです。

CephはブロックデバイスからOSDを作成するため、言うまでもなくceph-osdにはブロックデバイスが必要です。
osdのブロックデバイスはrook-ceph-osd Podが稼働するworker nodeから取ってくるので、事前に何らかの方法でworker nodeにブロックデバイスをattachしておかなくてはなりません。 しかしこのworker nodeにブロックデバイスをattachする作業は、Kubernetesが乗るプラットフォーム側の操作です。Kubernetesで何かしらやりようはあると思いますが、基本的にはKubernetes外の管理操作だと思います。
せっかくRookでKubernetes内で完結できるストレージシステムが作れるというのに、根本にある保存媒体はKubernetes外でmanageされる。それってなんかイケてないですよね。

そういうわけで、 「既存のPV/PVCの仕組みでceph-osdのPodにブロックデバイスを直でattachしようや、そしたら何もないところから自動でOSDが作れるやろ」 を実現するのがOSD on PVCです。
この考えの元にあるのがKubernetes 1.11からでてきたRaw Block Volumeのfeatureです。(1.14でbetaながらデフォルトでenableになっているfeature) この技術を使ってPVをブロックデバイスとして見せることができるようになりました。

https://kubernetes.io/docs/concepts/storage/volumes/#csi-raw-block-volume-support

これってまるでCloud(Container) Native Storageのためにあるようなfeature。ええやん、ステキやん。

実際にやってみる

OSDはベースのPVC-baseのブロックデバイスにするとし、MONの構成ファイルをどうするかという選択肢もあります。
これまでMONの構成ファイルはworker nodeの/var/lib/rookなどyamlで記載されたディレクトリに置かれていたのですが、これもPVC-baseのファイルPVに置くというパターンが選べます。せっかくだからこっちでやってみましょう。

前回までと同じように3master+3workerのクラスタです。

[utubo@tutsunom ceph]$ kubectl get node --sort-by=".metadata.creationTimestamp" --show-labels
NAME                            STATUS   ROLES    AGE   VERSION   LABELS
ip-172-20-91-64.ec2.internal    Ready    master   25h   v1.15.5   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/instance-type=t2.medium,beta.kubernetes.io/os=linux,failure-domain.beta.kubernetes.io/region=us-east-1,failure-domain.beta.kubernetes.io/zone=us-east-1b,kops.k8s.io/instancegroup=master-us-east-1b,kubernetes.io/arch=amd64,kubernetes.io/hostname=ip-172-20-74-29.ec2.internal,kubernetes.io/os=linux,kubernetes.io/role=master,node-role.kubernetes.io/master=
ip-172-20-53-29.ec2.internal    Ready    master   25h   v1.15.5   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/instance-type=t2.medium,beta.kubernetes.io/os=linux,failure-domain.beta.kubernetes.io/region=us-east-1,failure-domain.beta.kubernetes.io/zone=us-east-1a,kops.k8s.io/instancegroup=master-us-east-1a,kubernetes.io/arch=amd64,kubernetes.io/hostname=ip-172-20-63-169.ec2.internal,kubernetes.io/os=linux,kubernetes.io/role=master,node-role.kubernetes.io/master=
ip-172-20-113-40.ec2.internal   Ready    master   25h   v1.15.5   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/instance-type=t2.medium,beta.kubernetes.io/os=linux,failure-domain.beta.kubernetes.io/region=us-east-1,failure-domain.beta.kubernetes.io/zone=us-east-1c,kops.k8s.io/instancegroup=master-us-east-1c,kubernetes.io/arch=amd64,kubernetes.io/hostname=ip-172-20-121-240.ec2.internal,kubernetes.io/os=linux,kubernetes.io/role=master,node-role.kubernetes.io/master=
ip-172-20-93-28.ec2.internal    Ready    node     25h   v1.15.5   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/instance-type=t2.large,beta.kubernetes.io/os=linux,failure-domain.beta.kubernetes.io/region=us-east-1,failure-domain.beta.kubernetes.io/zone=us-east-1a,kops.k8s.io/instancegroup=nodes,kubernetes.io/arch=amd64,kubernetes.io/hostname=ip-172-20-52-182.ec2.internal,kubernetes.io/os=linux,kubernetes.io/role=node,node-role.kubernetes.io/node=
ip-172-20-42-193.ec2.internal   Ready    node     25h   v1.15.5   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/instance-type=t2.large,beta.kubernetes.io/os=linux,failure-domain.beta.kubernetes.io/region=us-east-1,failure-domain.beta.kubernetes.io/zone=us-east-1c,kops.k8s.io/instancegroup=nodes,kubernetes.io/arch=amd64,kubernetes.io/hostname=ip-172-20-118-92.ec2.internal,kubernetes.io/os=linux,kubernetes.io/role=node,node-role.kubernetes.io/node=
ip-172-20-102-19.ec2.internal   Ready    node     25h   v1.15.5   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/instance-type=t2.large,beta.kubernetes.io/os=linux,failure-domain.beta.kubernetes.io/region=us-east-1,failure-domain.beta.kubernetes.io/zone=us-east-1b,kops.k8s.io/instancegroup=nodes,kubernetes.io/arch=amd64,kubernetes.io/hostname=ip-172-20-74-57.ec2.internal,kubernetes.io/os=linux,kubernetes.io/role=node,node-role.kubernetes.io/node=

まず、ベースの元になるPV/PVCのStorageClassをチェックしましょう。
ベースのStorageClassがvolumeBindingMode: WaitForFirstConsumerで定義されていることが大事です。volumeBindingMode: Immediateだと動きません。ダメです。ここちょっとハマりました。

[utubo@tutsunom ceph]$ cat my-sc-gp2.yaml 
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: my-gp2
  annotations:
    storageclass.kubernetes.io/is-default-class: "true"
provisioner: kubernetes.io/aws-ebs
volumeBindingMode: WaitForFirstConsumer
[utubo@tutsunom ceph]$ kubectl create -f my-sc-gp2.yaml 

それではgithubにあるサンプルのcluster-on-pvc.yamlから、StorageClassと(gp2 --> my-gp2)とosdの数(count:3 --> 9)だけ変更して、Cephクラスタを作ってみます。(長いのでコメント行は省略)
yamlの中のmon:storage:(=osd)にあるvolumeClaimTemplateがPVC-baseの証。

[utubo@tutsunom ceph]$ cat my-cluster-on-pvc.yaml 
apiVersion: ceph.rook.io/v1
kind: CephCluster
metadata:
  name: rook-ceph
  namespace: rook-ceph
spec:
  dataDirHostPath: /var/lib/rook
  mon:
    count: 3
    allowMultiplePerNode: false
    volumeClaimTemplate:
      spec:
        storageClassName: my-gp2
        resources:
          requests:
            storage: 10Gi
  cephVersion:
    image: ceph/ceph:v14.2.4-20190917
    allowUnsupported: false
  dashboard:
    enabled: true
    ssl: true
  network:
    hostNetwork: false
  storage:
    topologyAware: true
    storageClassDeviceSets:
    - name: set1
      count: 9
      portable: true
      placement:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: app
                  operator: In
                  values:
                  - rook-ceph-osd
                - key: app
                  operator: In
                  values:
                  - rook-ceph-osd-prepare
              topologyKey: kubernetes.io/hostname
      resources:
      volumeClaimTemplates:
      - metadata:
          name: data
        spec:
          resources:
            requests:
              storage: 10Gi
          storageClassName: my-gp2
          volumeMode: Block
          accessModes:
            - ReadWriteOnce
  disruptionManagement:
    managePodBudgets: false
    osdMaintenanceTimeout: 30
    manageMachineDisruptionBudgets: false
    machineDisruptionBudgetNamespace: openshift-machine-api
[utubo@tutsunom ceph]$
[utubo@tutsunom ceph]$ kubectl create -f common.yaml
[utubo@tutsunom ceph]$ kubectl create -f operator.yaml
[utubo@tutsunom ceph]$ kubectl create -f my-cluster-on-pvc.yaml
[utubo@tutsunom ceph]$
[utubo@tutsunom ceph]$ kubectl -n rook-ceph get pod
NAME                                            READY   STATUS      RESTARTS   AGE
csi-cephfsplugin-jbs7g                          3/3     Running     0          4m53s
csi-cephfsplugin-lvtg9                          3/3     Running     0          4m53s
csi-cephfsplugin-provisioner-974b566d9-b24zs    4/4     Running     0          4m53s
csi-cephfsplugin-provisioner-974b566d9-wgx4l    4/4     Running     0          4m53s
csi-cephfsplugin-rpg6v                          3/3     Running     0          4m53s
csi-rbdplugin-4pvjd                             3/3     Running     0          4m53s
csi-rbdplugin-hw59q                             3/3     Running     0          4m53s
csi-rbdplugin-mrq2g                             3/3     Running     0          4m53s
csi-rbdplugin-provisioner-579c546f5-k4x78       5/5     Running     0          4m53s
csi-rbdplugin-provisioner-579c546f5-tlb8s       5/5     Running     0          4m53s
rook-ceph-mgr-a-8656964c64-d6rxd                1/1     Running     0          2m50s
rook-ceph-mon-a-5964fd74f7-fpqhl                1/1     Running     0          4m3s
rook-ceph-mon-b-858958c794-mwfzz                1/1     Running     0          3m36s
rook-ceph-mon-c-576b4d7687-94ts9                1/1     Running     0          3m14s
rook-ceph-operator-fb8b96548-f5dl5              1/1     Running     0          5m1s
rook-ceph-osd-0-66db6b5bbc-6v82x                1/1     Running     0          59s
rook-ceph-osd-1-7b76f55b96-bqknl                1/1     Running     0          49s
rook-ceph-osd-2-57f696464b-jlzjr                1/1     Running     0          74s
rook-ceph-osd-3-7b975f57c7-6snsr                1/1     Running     0          78s
rook-ceph-osd-4-5d485c8997-8fq7r                1/1     Running     0          75s
rook-ceph-osd-5-6f4f87db77-b52z6                1/1     Running     0          43s
rook-ceph-osd-6-6999896cdf-7d4mv                1/1     Running     0          26s
rook-ceph-osd-7-c66d49fcb-5lrwr                 1/1     Running     0          29s
rook-ceph-osd-8-6bdf79c57c-prnzq                1/1     Running     0          28s
rook-ceph-osd-prepare-set1-0-data-bhmrj-rjbnk   0/1     Completed   0          2m22s
rook-ceph-osd-prepare-set1-1-data-dcr5r-gcdl8   0/1     Completed   0          2m21s
rook-ceph-osd-prepare-set1-2-data-klbr2-bnlqv   0/1     Completed   0          2m21s
rook-ceph-osd-prepare-set1-3-data-7b2pk-7n6wl   0/1     Completed   0          2m21s
rook-ceph-osd-prepare-set1-4-data-249gz-b2z79   0/1     Completed   0          2m20s
rook-ceph-osd-prepare-set1-5-data-znf49-l7xbz   0/1     Completed   0          2m19s
rook-ceph-osd-prepare-set1-6-data-57bft-qtnwt   0/1     Completed   0          2m19s
rook-ceph-osd-prepare-set1-7-data-sm244-v546c   0/1     Completed   0          2m18s
rook-ceph-osd-prepare-set1-8-data-cd5hb-zt2gk   0/1     Completed   0          2m18s
rook-discover-5jzcr                             1/1     Running     0          5m
rook-discover-glvgr                             1/1     Running     0          5m
rook-discover-jh9pr                             1/1     Running     0          5m

おっ、イケるやん。worker nodeにあらかじめブロックデバイスをつけることなくCephクラスタが作れました。

[utubo@tutsunom ceph]$ kubectl -n rook-ceph get pod -l app=rook-ceph-mon -o wide
NAME                               READY   STATUS    RESTARTS   AGE     IP             NODE                            NOMINATED NODE   READINESS GATES
rook-ceph-mon-a-5964fd74f7-fpqhl   1/1     Running   0          4m56s   100.96.6.128   ip-172-20-93-28.ec2.internal    <none>           <none>
rook-ceph-mon-b-858958c794-mwfzz   1/1     Running   0          4m29s   100.96.7.198   ip-172-20-42-193.ec2.internal   <none>           <none>
rook-ceph-mon-c-576b4d7687-94ts9   1/1     Running   0          4m7s    100.96.8.117   ip-172-20-102-19.ec2.internal   <none>           <none>

MONはしっかり3ノードに分かれています。OSDは、

[utubo@tutsunom ceph]$ kubectl -n rook-ceph get pod -l app=rook-ceph-osd -o wide
NAME                               READY   STATUS    RESTARTS   AGE     IP             NODE                            NOMINATED NODE   READINESS GATES
rook-ceph-osd-0-66db6b5bbc-6v82x   1/1     Running   0          119s    100.96.6.136   ip-172-20-93-28.ec2.internal    <none>           <none>
rook-ceph-osd-1-7b76f55b96-bqknl   1/1     Running   0          109s    100.96.6.137   ip-172-20-93-28.ec2.internal    <none>           <none>
rook-ceph-osd-2-57f696464b-jlzjr   1/1     Running   0          2m14s   100.96.7.204   ip-172-20-42-193.ec2.internal   <none>           <none>
rook-ceph-osd-3-7b975f57c7-6snsr   1/1     Running   0          2m18s   100.96.7.202   ip-172-20-42-193.ec2.internal   <none>           <none>
rook-ceph-osd-4-5d485c8997-8fq7r   1/1     Running   0          2m15s   100.96.7.203   ip-172-20-42-193.ec2.internal   <none>           <none>
rook-ceph-osd-5-6f4f87db77-b52z6   1/1     Running   0          103s    100.96.6.138   ip-172-20-93-28.ec2.internal    <none>           <none>
rook-ceph-osd-6-6999896cdf-7d4mv   1/1     Running   0          86s     100.96.6.141   ip-172-20-93-28.ec2.internal    <none>           <none>
rook-ceph-osd-7-c66d49fcb-5lrwr    1/1     Running   0          89s     100.96.6.139   ip-172-20-93-28.ec2.internal    <none>           <none>
rook-ceph-osd-8-6bdf79c57c-prnzq   1/1     Running   0          88s     100.96.6.140   ip-172-20-93-28.ec2.internal    <none>           <none>

ええー!osd全然3ノードにバラけてないやん!!一応Cephで見てみると…

[utubo@tutsunom ceph]$ kubectl create -f toolbox.yaml 
[utubo@tutsunom ceph]$  kubectl -n rook-ceph exec -it `kubectl get pod -l app=rook-ceph-tools -o 'jsonpath={.items[].metadata.name}'` ceph osd tree
ID  CLASS WEIGHT  TYPE NAME                          STATUS REWEIGHT PRI-AFF 
 -1       0.07910 root default                                               
 -5       0.07910     region us-east-1                                       
 -4       0.02637         zone us-east-1a                                    
 -9       0.00879             host set1-0-data-bhmrj                         
  4   ssd 0.00879                 osd.4                  up  1.00000 1.00000 
-11       0.00879             host set1-2-data-klbr2                         
  2   ssd 0.00879                 osd.2                  up  1.00000 1.00000 
 -3       0.00879             host set1-6-data-57bft                         
  3   ssd 0.00879                 osd.3                  up  1.00000 1.00000 
-14       0.05273         zone us-east-1b                                    
-13       0.00879             host set1-1-data-dcr5r                         
  0   ssd 0.00879                 osd.0                  up  1.00000 1.00000 
-19       0.00879             host set1-3-data-7b2pk                         
  5   ssd 0.00879                 osd.5                  up  1.00000 1.00000 
-17       0.00879             host set1-4-data-249gz                         
  1   ssd 0.00879                 osd.1                  up  1.00000 1.00000 
-23       0.00879             host set1-5-data-znf49                         
  6   ssd 0.00879                 osd.6                  up  1.00000 1.00000 
-21       0.00879             host set1-7-data-sm244                         
  8   ssd 0.00879                 osd.8                  up  1.00000 1.00000 
-25       0.00879             host set1-8-data-cd5hb                         
  7   ssd 0.00879                 osd.7                  up  1.00000 1.00000 

(´・ω・`)
yamlosd Affinity ruleを見ると、

      placement:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: app
                  operator: In
                  values:
                  - rook-ceph-osd
                - key: app
                  operator: In
                  values:
                  - rook-ceph-osd-prepare
              topologyKey: kubernetes.io/hostname

うーん…確かに3osdより多くなるとうまく散らばらなそうやけど3nodeにすら散らばらんのはどうなっとるんや…

実はコレ想定内の問題なんです。サンプルのcluster-on-pvc.yamlで使われているpodAntiAffinity ruleではうまく散らばらないのは織り込み済みです。yamlのコメントをよく読んでみると次のことが書かれています。

      # Since the OSDs could end up on any node, an effort needs to be made to spread the OSDs
      # across nodes as much as possible. Unfortunately the pod anti-affinity breaks down
      # as soon as you have more than one OSD per node. If you have more OSDs than nodes, K8s may
      # choose to schedule many of them on the same node. What we need is the Pod Topology
      # Spread Constraints, which is alpha in K8s 1.16. This means that a feature gate must be
      # enabled for this feature, and Rook also still needs to add support for this feature.
      # Another approach for a small number of OSDs is to create a separate device set for each
      # zone (or other set of nodes with a common label) so that the OSDs will end up on different

なるほど。要約すると、

  • いまのPod Anti-Affinityだと上手くosdをバラけさせられない。まだ努力が必要やわ。
  • Pod Topology Spread Constraints(k8s 1.16 alpha)を使えばピチッとバラけさせられるけど、Rookがまだ対応してないねん。これからの子やねん。
  • Node Affinityも使った組み合わせたらイケるかも。それでどないや?

ということです。そうか、この分野ではRookはまだまだこれからの子なんか。それならしゃーない。

そういうわけで、今の時点ではマニュアルで3nodeの散らばるように書く必要がありそうですね。

まとめ

今回はOSD on PVCのコンセプトの紹介をし、実際にやってみて問題に直面しました。
この悲しき現実に立ち向かう方法たるやいかに。次回バジリスク甲賀忍法帖…というノリはやめといて、次回はこの問題を回避する方法を紹介します。

BGMは甲賀忍法帖でした。(やっぱり)

Rook-CephでSSDとHDDを使い分ける

f:id:ututaq:20191125022735p:plain

この記事はRookだらけの Advent Calendar 2019 3日目の記事です。

Rook-CephでSSDとHDDの使い分けの例を説明します。

SSDOSDメタデータを分けて置く

Cephをご存知の方だと、『OSDのユーザーデータとメタデータ領域は分けたほうがいい』というプラクティスを知っていると思います。
メタデータはsmall I/Oがバンバン来るのでFlashバイスの方がいいということです。そのとおりで、特にsmall I/Oに強くないHDDにとっては性能的に望ましくないです。

CephではユーザーデータはHDDへ、メタデータだけSSDやNVMeへと分けることができるので、Rook-Cephでやってみます。

3台のworkerにそれぞれ5つのデバイスをぶら下げます。

[utubo@tutsunom ceph]$ ssh xxxxxx.compute-1.amazonaws.com lsblk
NAME    MAJ:MIN RM    SIZE RO TYPE MOUNTPOINT
xvda    202:0    0    128G  0 disk 
├─xvda1 202:1    0 1007.5K  0 part 
└─xvda2 202:2    0    128G  0 part /
xvdf    202:80   0      5G  0 disk  ← ssd(metadata用) 
xvdg    202:96   0     10G  0 disk  ← ssd(osd用) 
xvdh    202:112  0     10G  0 disk  ← ssd(osd用) 
xvdi    202:128  0     20G  0 disk  ← hdd(osd用) 
xvdj    202:144  0     20G  0 disk  ← hdd(osd用) 

何も考えずにkubectl create -f cluster.yamlをやってしまうと、

[utubo@tutsunom ceph]$ kubectl exec -it `kubectl get pod -l app=rook-ceph-tools -o 'jsonpath={.items[].metadata.name}'` ceph osd df tree
ID  CLASS WEIGHT  REWEIGHT SIZE    RAW USE DATA    OMAP META   AVAIL   %USE  VAR  PGS STATUS TYPE NAME                                      
 -1       0.19061        - 195 GiB  15 GiB  15 MiB  0 B 15 GiB 180 GiB  7.70 1.00   -        root default                                   
 -5       0.19061        - 195 GiB  15 GiB  15 MiB  0 B 15 GiB 180 GiB  7.70 1.00   -            region us-east-1                           
 -4       0.06354        -  65 GiB 5.0 GiB 4.8 MiB  0 B  5 GiB  60 GiB  7.70 1.00   -                zone us-east-1a                        
 -3       0.06354        -  65 GiB 5.0 GiB 4.8 MiB  0 B  5 GiB  60 GiB  7.70 1.00   -                    host ip-172-20-42-193-ec2-internal 
  0   ssd 0.01859  1.00000  19 GiB 1.0 GiB 992 KiB  0 B  1 GiB  18 GiB  5.27 0.68   0     up                 osd.0                          
  3   ssd 0.00879  1.00000   9 GiB 1.0 GiB 992 KiB  0 B  1 GiB 8.0 GiB 11.12 1.44   0     up                 osd.3                          
  6   ssd 0.00879  1.00000   9 GiB 1.0 GiB 992 KiB  0 B  1 GiB 8.0 GiB 11.12 1.44   0     up                 osd.6                          
  9   ssd 0.01859  1.00000  19 GiB 1.0 GiB 992 KiB  0 B  1 GiB  18 GiB  5.27 0.68   0     up                 osd.9                          
 12   ssd 0.00879  1.00000   9 GiB 1.0 GiB 992 KiB  0 B  1 GiB 8.0 GiB 11.12 1.44   0     up                 osd.12                         
-14       0.06354        -  65 GiB 5.0 GiB 4.8 MiB  0 B  5 GiB  60 GiB  7.70 1.00   -                zone us-east-1b                        
-13       0.06354        -  65 GiB 5.0 GiB 4.8 MiB  0 B  5 GiB  60 GiB  7.70 1.00   -                    host ip-172-20-93-28-ec2-internal  
  1   ssd 0.00879  1.00000   9 GiB 1.0 GiB 992 KiB  0 B  1 GiB 8.0 GiB 11.12 1.44   0     up                 osd.1                          
  4   ssd 0.01859  1.00000  19 GiB 1.0 GiB 992 KiB  0 B  1 GiB  18 GiB  5.27 0.68   0     up                 osd.4                          
  7   ssd 0.00879  1.00000   9 GiB 1.0 GiB 992 KiB  0 B  1 GiB 8.0 GiB 11.12 1.44   0     up                 osd.7                          
 11   ssd 0.00879  1.00000   9 GiB 1.0 GiB 992 KiB  0 B  1 GiB 8.0 GiB 11.12 1.44   0     up                 osd.11                         
 14   ssd 0.01859  1.00000  19 GiB 1.0 GiB 992 KiB  0 B  1 GiB  18 GiB  5.27 0.68   0     up                 osd.14                         
-10       0.06354        -  65 GiB 5.0 GiB 4.8 MiB  0 B  5 GiB  60 GiB  7.70 1.00   -                zone us-east-1c                        
 -9       0.06354        -  65 GiB 5.0 GiB 4.8 MiB  0 B  5 GiB  60 GiB  7.70 1.00   -                    host ip-172-20-102-19-ec2-internal 
  2   ssd 0.01859  1.00000  19 GiB 1.0 GiB 992 KiB  0 B  1 GiB  18 GiB  5.27 0.68   0     up                 osd.2                          
  5   ssd 0.00879  1.00000   9 GiB 1.0 GiB 992 KiB  0 B  1 GiB 8.0 GiB 11.12 1.44   0     up                 osd.5                          
  8   ssd 0.01859  1.00000  19 GiB 1.0 GiB 992 KiB  0 B  1 GiB  18 GiB  5.27 0.68   0     up                 osd.8                          
 10   ssd 0.00879  1.00000   9 GiB 1.0 GiB 992 KiB  0 B  1 GiB 8.0 GiB 11.12 1.44   0     up                 osd.10                         
 13   ssd 0.00879  1.00000   9 GiB 1.0 GiB 992 KiB  0 B  1 GiB 8.0 GiB 11.12 1.44   0     up                 osd.13                         

という風に全部のデバイスosdとなってしまいました。あー。ノードではこんな感じでLVが作られる。

[utubo@tutsunom ceph]$ ssh xxxxxx.compute-1.amazonaws.com lsblk
NAME                                                                                                 MAJ:MIN RM    SIZE RO TYPE MOUNTPOINT
xvda                                                                                                 202:0    0    128G  0 disk 
├─xvda1                                                                                              202:1    0 1007.5K  0 part 
└─xvda2                                                                                              202:2    0    128G  0 part /
xvdf                                                                                                 202:80   0     10G  0 disk 
└─ceph--8e673497--53a0--457f--9b7a--b48ea1f6e6d8-osd--data--e7021f4d--29f2--4c49--ba75--d242b0288181 254:2    0      9G  0 lvm  
xvdg                                                                                                 202:96   0     10G  0 disk 
└─ceph--0698a316--b2c8--4301--b076--0ed7c854649a-osd--data--a4f8a81f--1b17--483f--8ed6--7c3c299cc394 254:4    0      9G  0 lvm  
xvdh                                                                                                 202:112  0     10G  0 disk 
└─ceph--77603f3c--1bf5--4a22--8e98--4e90a06863af-osd--data--91c9407d--4b7f--4975--b1ae--2a2fffd8330e 254:1    0      9G  0 lvm  
xvdi                                                                                                 202:128  0     20G  0 disk 
└─ceph--0d73aa24--76db--4993--a455--07de990a7032-osd--data--67b5ef67--40d4--486e--a7f4--a92a974c19a4 254:3    0     19G  0 lvm  
xvdj                                                                                                 202:144  0     20G  0 disk 
└─ceph--e33ac0e4--633c--4af7--8fbf--f65a125bb1a3-osd--data--e952dada--1dea--4f24--8efd--2a4884914ea1 254:0    0     19G  0 lvm  

各デバイスのLVでユーザー領域とメタデータ領域が同居することになってしまってます。 メタデータ専用のデバイスを指定できればできればいいですね。cluster.yamlファイルのstorageのセクションで、

  storage: # cluster level storage configuration and selection
    useAllNodes: true
    useAllDevices: true
    topologyAware: true
    deviceFilter:
    location:
    config:
      storeType: bluestore
      metadataDevice: "xvdf"
      databaseSizeMB: "1024" # uncomment if the disks are smaller than 100 GB

みたいな感じにconfig:以下を指定してあげましょう。

[utubo@tutsunom ceph]$ kubectl exec -it `kubectl get pod -l app=rook-ceph-tools -o 'jsonpath={.items[].metadata.name}'` ceph osd df tree
ID  CLASS WEIGHT  REWEIGHT SIZE    RAW USE DATA    OMAP META   AVAIL   %USE  VAR  PGS STATUS TYPE NAME                                      
 -1       0.17569        - 180 GiB  24 GiB  10 MiB  0 B 12 GiB 156 GiB 13.34 1.00   -        root default                                   
 -5       0.17569        - 180 GiB  24 GiB  10 MiB  0 B 12 GiB 156 GiB 13.34 1.00   -            region us-east-1                           
 -4       0.05856        -  60 GiB 8.0 GiB 3.5 MiB  0 B  4 GiB  52 GiB 13.34 1.00   -                zone us-east-1a                        
 -3       0.05856        -  60 GiB 8.0 GiB 3.5 MiB  0 B  4 GiB  52 GiB 13.34 1.00   -                    host ip-172-20-42-193-ec2-internal 
  0   ssd 0.01949  1.00000  20 GiB 2.0 GiB 896 KiB  0 B  1 GiB  18 GiB 10.00 0.75   0     up                 osd.0                          
  3   ssd 0.00980  1.00000  10 GiB 2.0 GiB 896 KiB  0 B  1 GiB 8.0 GiB 20.01 1.50   0     up                 osd.3                          
  6   ssd 0.01949  1.00000  20 GiB 2.0 GiB 896 KiB  0 B  1 GiB  18 GiB 10.00 0.75   0     up                 osd.6                          
  9   ssd 0.00980  1.00000  10 GiB 2.0 GiB 896 KiB  0 B  1 GiB 8.0 GiB 20.01 1.50   0     up                 osd.9                          
-14       0.05856        -  60 GiB 8.0 GiB 3.5 MiB  0 B  4 GiB  52 GiB 13.34 1.00   -                zone us-east-1b                        
-13       0.05856        -  60 GiB 8.0 GiB 3.5 MiB  0 B  4 GiB  52 GiB 13.34 1.00   -                    host ip-172-20-93-28-ec2-internal  
  2   ssd 0.01949  1.00000  20 GiB 2.0 GiB 896 KiB  0 B  1 GiB  18 GiB 10.00 0.75   0     up                 osd.2                          
  5   ssd 0.00980  1.00000  10 GiB 2.0 GiB 896 KiB  0 B  1 GiB 8.0 GiB 20.01 1.50   0     up                 osd.5                          
  8   ssd 0.01949  1.00000  20 GiB 2.0 GiB 896 KiB  0 B  1 GiB  18 GiB 10.00 0.75   0     up                 osd.8                          
 11   ssd 0.00980  1.00000  10 GiB 2.0 GiB 896 KiB  0 B  1 GiB 8.0 GiB 20.01 1.50   0     up                 osd.11                         
-10       0.05856        -  60 GiB 8.0 GiB 3.5 MiB  0 B  4 GiB  52 GiB 13.34 1.00   -                zone us-east-1c                        
 -9       0.05856        -  60 GiB 8.0 GiB 3.5 MiB  0 B  4 GiB  52 GiB 13.34 1.00   -                    host ip-172-20-102-19-ec2-internal 
  1   ssd 0.01949  1.00000  20 GiB 2.0 GiB 896 KiB  0 B  1 GiB  18 GiB 10.00 0.75   0     up                 osd.1                          
  4   ssd 0.00980  1.00000  10 GiB 2.0 GiB 896 KiB  0 B  1 GiB 8.0 GiB 20.01 1.50   0     up                 osd.4                          
  7   ssd 0.01949  1.00000  20 GiB 2.0 GiB 896 KiB  0 B  1 GiB  18 GiB 10.00 0.75   0     up                 osd.7                          
 10   ssd 0.00980  1.00000  10 GiB 2.0 GiB 896 KiB  0 B  1 GiB 8.0 GiB 20.01 1.50   0     up                 osd.10                         
                     TOTAL 180 GiB  24 GiB  10 MiB  0 B 12 GiB 156 GiB 13.34                                                                
MIN/MAX VAR: 0.75/1.50  STDDEV: 5.27

ノードではこうなってます。希望通り/dev/xvdfメタデータ(dbs; rocksDB)領域のLVがまとまっています。

[utubo@tutsunom ceph]$ ssh xxxxxx.compute-1.amazonaws.com lsblk
NAME                                                                                                                  MAJ:MIN RM    SIZE RO TYPE MOUNTPOINT
xvda                                                                                                                  202:0    0    128G  0 disk 
├─xvda1                                                                                                               202:1    0 1007.5K  0 part 
└─xvda2                                                                                                               202:2    0    128G  0 part /
xvdf                                                                                                                  202:80   0     10G  0 disk 
├─ceph--block--dbs--3d572391--5d90--4499--9777--1882fdb2d032-osd--block--db--0a9417fd--1f1e--4847--901b--abc1bc7a6f76 254:1    0      1G  0 lvm  
├─ceph--block--dbs--3d572391--5d90--4499--9777--1882fdb2d032-osd--block--db--209b10e9--9712--462a--a354--e1e74321252c 254:3    0      1G  0 lvm  
├─ceph--block--dbs--3d572391--5d90--4499--9777--1882fdb2d032-osd--block--db--5743ece9--2e47--4a80--bd65--85a5e8ded997 254:5    0      1G  0 lvm  
└─ceph--block--dbs--3d572391--5d90--4499--9777--1882fdb2d032-osd--block--db--69a7ef92--0c0f--4921--9276--754b1eb0aade 254:7    0      1G  0 lvm  
xvdg                                                                                                                  202:96   0     10G  0 disk 
└─ceph--block--690c01e5--5df9--4f76--a0b7--5cda604029f3-osd--block--438cc4b8--acc4--4bc2--844f--4169f7d15eb4          254:6    0      9G  0 lvm  
xvdh                                                                                                                  202:112  0     10G  0 disk 
└─ceph--block--63789610--ea4f--4c4d--b7d3--3a9cc61aab7b-osd--block--f00ca520--0ce4--4458--b2ca--8836ec16719f          254:2    0      9G  0 lvm  
xvdi                                                                                                                  202:128  0     20G  0 disk 
└─ceph--block--2df2e114--9da7--44e6--ab0b--971511841130-osd--block--fad745f3--d7f6--4734--a0ab--081456857402          254:4    0     19G  0 lvm  
xvdj                                                                                                                  202:144  0     20G  0 disk 
└─ceph--block--071efb65--60b2--4a97--94b4--da0a60fc2022-osd--block--fc1da0af--0c9a--4f12--b31b--6ac9438d9e1c          254:0    0     19G  0 lvm  

Device Classを指定してPoolを作る

この状態で素直にPoolを作ってしまうと、SSDとHDDがごちゃまぜになったPoolになってしまって、SSDの性能がHDDに引っ張られてしまってもったいないことになります。だからSSDとHDDは分けて使いましょう。
それはそうと、SSDとHDDが混在する環境なのにCLASSのカラムが全部ssdってのはいただけません。クラウドではありがちな感じでしょうね。github見てると近いうちにデバイス毎にdeviceClassが指定できるようになるでしょう。いまのところは手で変更しときます。CRUSH mapをいじくってもいいです。

[utubo@tutsunom ceph]$ kubectl exec -it `kubectl get pod -l app=rook-ceph-tools -o 'jsonpath={.items[].metadata.name}'` ceph osd crush rm-device-class 0 1 2 6 7 8
done removing class of osd(s): 0,1,2,6,7,8
[utubo@tutsunom ceph]$ kubectl exec -it `kubectl get pod -l app=rook-ceph-tools -o 'jsonpath={.items[].metadata.name}'` ceph osd crush set-device-class hdd 0 1 2 6 7 8
set osd(s) 0,1,2,6,7,8 to class 'hdd'
[utubo@tutsunom ceph]$ kubectl exec -it `kubectl get pod -l app=rook-ceph-tools -o 'jsonpath={.items[].metadata.name}'` ceph osd df tree
ID  CLASS WEIGHT  REWEIGHT SIZE    RAW USE DATA    OMAP META   AVAIL   %USE  VAR  PGS STATUS TYPE NAME                                      
 -1       0.17569        - 180 GiB  24 GiB  11 MiB  0 B 12 GiB 156 GiB 13.34 1.00   -        root default                                   
 -5       0.17569        - 180 GiB  24 GiB  11 MiB  0 B 12 GiB 156 GiB 13.34 1.00   -            region us-east-1                           
 -4       0.05856        -  60 GiB 8.0 GiB 3.8 MiB  0 B  4 GiB  52 GiB 13.34 1.00   -                zone us-east-1a                        
 -3       0.05856        -  60 GiB 8.0 GiB 3.8 MiB  0 B  4 GiB  52 GiB 13.34 1.00   -                    host ip-172-20-42-193-ec2-internal 
  0   hdd 0.01949  1.00000  20 GiB 2.0 GiB 960 KiB  0 B  1 GiB  18 GiB 10.00 0.75   0     up                 osd.0                          
  6   hdd 0.01949  1.00000  20 GiB 2.0 GiB 960 KiB  0 B  1 GiB  18 GiB 10.00 0.75   0     up                 osd.6                          
  3   ssd 0.00980  1.00000  10 GiB 2.0 GiB 960 KiB  0 B  1 GiB 8.0 GiB 20.01 1.50   0     up                 osd.3                          
  9   ssd 0.00980  1.00000  10 GiB 2.0 GiB 960 KiB  0 B  1 GiB 8.0 GiB 20.01 1.50   0     up                 osd.9                          
-14       0.05856        -  60 GiB 8.0 GiB 3.8 MiB  0 B  4 GiB  52 GiB 13.34 1.00   -                zone us-east-1b                        
-13       0.05856        -  60 GiB 8.0 GiB 3.8 MiB  0 B  4 GiB  52 GiB 13.34 1.00   -                    host ip-172-20-93-28-ec2-internal  
  2   hdd 0.01949  1.00000  20 GiB 2.0 GiB 960 KiB  0 B  1 GiB  18 GiB 10.00 0.75   0     up                 osd.2                          
  8   hdd 0.01949  1.00000  20 GiB 2.0 GiB 960 KiB  0 B  1 GiB  18 GiB 10.00 0.75   0     up                 osd.8                          
  5   ssd 0.00980  1.00000  10 GiB 2.0 GiB 960 KiB  0 B  1 GiB 8.0 GiB 20.01 1.50   0     up                 osd.5                          
 11   ssd 0.00980  1.00000  10 GiB 2.0 GiB 960 KiB  0 B  1 GiB 8.0 GiB 20.01 1.50   0     up                 osd.11                         
-10       0.05856        -  60 GiB 8.0 GiB 3.8 MiB  0 B  4 GiB  52 GiB 13.34 1.00   -                zone us-east-1c                        
 -9       0.05856        -  60 GiB 8.0 GiB 3.8 MiB  0 B  4 GiB  52 GiB 13.34 1.00   -                    host ip-172-20-102-19-ec2-internal 
  1   hdd 0.01949  1.00000  20 GiB 2.0 GiB 960 KiB  0 B  1 GiB  18 GiB 10.00 0.75   0     up                 osd.1                          
  7   hdd 0.01949  1.00000  20 GiB 2.0 GiB 960 KiB  0 B  1 GiB  18 GiB 10.00 0.75   0     up                 osd.7                          
  4   ssd 0.00980  1.00000  10 GiB 2.0 GiB 960 KiB  0 B  1 GiB 8.0 GiB 20.01 1.50   0     up                 osd.4                          
 10   ssd 0.00980  1.00000  10 GiB 2.0 GiB 960 KiB  0 B  1 GiB 8.0 GiB 20.01 1.50   0     up                 osd.10                         
                     TOTAL 180 GiB  24 GiB  11 MiB  0 B 12 GiB 156 GiB 13.34                                                                
MIN/MAX VAR: 0.75/1.50  STDDEV: 5.27

それではDeviceClassを指定してPoolを作りましょう。

[utubo@tutsunom ceph]$ cat pool-ssd-hdd.yaml
apiVersion: ceph.rook.io/v1
kind: CephBlockPool
metadata:
  name: ssd-pool
  namespace: rook-ceph
spec:
  failureDomain: host
  replicated:
    size: 3
  deviceClass: ssd
---
apiVersion: ceph.rook.io/v1
kind: CephBlockPool
metadata:
  name: hdd-pool
  namespace: rook-ceph
spec:
  failureDomain: host
  replicated:
    size: 3
  deviceClass: hdd
[utubo@tutsunom ceph]$ kubectl create -f pool-ssd-hdd.yaml 
cephblockpool.ceph.rook.io/ssd-pool created
cephblockpool.ceph.rook.io/hdd-pool created
[utubo@tutsunom ceph]$ kubectl exec -it `kubectl get pod -l app=rook-ceph-tools -o 'jsonpath={.items[].metadata.name}'` ceph df
RAW STORAGE:
    CLASS     SIZE        AVAIL       USED        RAW USED     %RAW USED 
    hdd       120 GiB     108 GiB     6.0 GiB       12 GiB         10.01 
    ssd        60 GiB      48 GiB     6.0 GiB       12 GiB         20.01 
    TOTAL     180 GiB     156 GiB      12 GiB       24 GiB         13.34 
 
POOLS:
    POOL         ID     STORED     OBJECTS     USED     %USED     MAX AVAIL 
    ssd-pool      1        0 B           0      0 B         0        15 GiB 
    hdd-pool      2        0 B           0      0 B         0        34 GiB 
[utubo@tutsunom ceph]$ kubectl exec -it `kubectl get pod -l app=rook-ceph-tools -o 'jsonpath={.items[].metadata.name}'` ceph pg ls-by-pool ssd-pool
PG  OBJECTS DEGRADED MISPLACED UNFOUND BYTES OMAP_BYTES* OMAP_KEYS* LOG STATE        SINCE VERSION REPORTED UP           ACTING       SCRUB_STAMP                DEEP_SCRUB_STAMP           
1.0       0        0         0       0     0           0          0   0 active+clean   26m     0'0    36:14 [11,9,10]p11 [11,9,10]p11 2019-12-02 03:55:47.245350 2019-12-02 03:55:47.245350 
1.1       0        0         0       0     0           0          0   0 active+clean   26m     0'0    36:14   [3,5,10]p3   [3,5,10]p3 2019-12-02 03:55:47.245350 2019-12-02 03:55:47.245350 
1.2       0        0         0       0     0           0          0   0 active+clean   26m     0'0    36:14   [9,4,11]p9   [9,4,11]p9 2019-12-02 03:55:47.245350 2019-12-02 03:55:47.245350 
1.3       0        0         0       0     0           0          0   0 active+clean   26m     0'0    36:14 [11,10,3]p11 [11,10,3]p11 2019-12-02 03:55:47.245350 2019-12-02 03:55:47.245350 
1.4       0        0         0       0     0           0          0   0 active+clean   26m     0'0    36:14   [3,5,10]p3   [3,5,10]p3 2019-12-02 03:55:47.245350 2019-12-02 03:55:47.245350 
1.5       0        0         0       0     0           0          0   0 active+clean   26m     0'0    36:14   [3,11,4]p3   [3,11,4]p3 2019-12-02 03:55:47.245350 2019-12-02 03:55:47.245350 
1.6       0        0         0       0     0           0          0   0 active+clean   26m     0'0    36:14  [3,10,11]p3  [3,10,11]p3 2019-12-02 03:55:47.245350 2019-12-02 03:55:47.245350 
1.7       0        0         0       0     0           0          0   0 active+clean   26m     0'0    36:14    [5,4,9]p5    [5,4,9]p5 2019-12-02 03:55:47.245350 2019-12-02 03:55:47.245350 

* NOTE: Omap statistics are gathered during deep scrub and may be inaccurate soon afterwards depending on utilisation. See http://docs.ceph.com/docs/master/dev/placement-group/#omap-statistics for further details.
[utubo@tutsunom ceph]$ kubectl exec -it `kubectl get pod -l app=rook-ceph-tools -o 'jsonpath={.items[].metadata.name}'` ceph pg ls-by-pool hdd-pool
PG  OBJECTS DEGRADED MISPLACED UNFOUND BYTES OMAP_BYTES* OMAP_KEYS* LOG STATE        SINCE VERSION REPORTED UP        ACTING    SCRUB_STAMP                DEEP_SCRUB_STAMP           
2.0       0        0         0       0     0           0          0   0 active+clean   26m     0'0    36:10 [7,8,0]p7 [7,8,0]p7 2019-12-02 03:55:51.450649 2019-12-02 03:55:51.450649 
2.1       0        0         0       0     0           0          0   0 active+clean   26m     0'0    36:10 [7,8,0]p7 [7,8,0]p7 2019-12-02 03:55:51.450649 2019-12-02 03:55:51.450649 
2.2       0        0         0       0     0           0          0   0 active+clean   26m     0'0    36:10 [8,1,0]p8 [8,1,0]p8 2019-12-02 03:55:51.450649 2019-12-02 03:55:51.450649 
2.3       0        0         0       0     0           0          0   0 active+clean   26m     0'0    36:10 [8,0,1]p8 [8,0,1]p8 2019-12-02 03:55:51.450649 2019-12-02 03:55:51.450649 
2.4       0        0         0       0     0           0          0   0 active+clean   26m     0'0    36:10 [6,2,1]p6 [6,2,1]p6 2019-12-02 03:55:51.450649 2019-12-02 03:55:51.450649 
2.5       0        0         0       0     0           0          0   0 active+clean   26m     0'0    36:10 [7,0,8]p7 [7,0,8]p7 2019-12-02 03:55:51.450649 2019-12-02 03:55:51.450649 
2.6       0        0         0       0     0           0          0   0 active+clean   26m     0'0    36:10 [6,8,1]p6 [6,8,1]p6 2019-12-02 03:55:51.450649 2019-12-02 03:55:51.450649 
2.7       0        0         0       0     0           0          0   0 active+clean   26m     0'0    36:10 [6,7,8]p6 [6,7,8]p6 2019-12-02 03:55:51.450649 2019-12-02 03:55:51.450649 

* NOTE: Omap statistics are gathered during deep scrub and may be inaccurate soon afterwards depending on utilisation. See http://docs.ceph.com/docs/master/dev/placement-group/#omap-statistics for further details.

ssd-poolのPG割り当てられているのはDevice ClassがssdOSDで、hdd-poolのそれはDevice Classがhddのものと確認できますね。
あとはStorageClassを別々に作って使い分けていくことができます。

まとめ

Cephはいろんなデバイスクラスタにごった煮に入れて使えるので、有効な使い方ができるように多少の設計は必要ですが、最初のうちはそんなに細かく考えなくていいと思います。

まずはこれだけ留意しておきましょう。