Japan Rook Meetup #4 でお話した内容です。
Rook/Ceph on ZFSJapan Rook Meetup #4@takutaka1220Takuya Takahashi
View Slide
アジェンダ- 自己紹介- 環境構築- 計測- 考察
自己紹介します
自己紹介- 高橋 拓也- GMO ペパボという会社でインフラエンジニアをしています- ストレージは趣味です- https://github.com/takutakahashi- https://twitter.com/takutaka1220- https://www.takutakahashi.dev
突然ですが
ZFS はいいぞ
ZFS (Zettabyte File System) とは?- CoW な多機能ファイルシステム- Block Device をまとめて Storage Pool を構成- RAID (シングルパリティの RAIDZ, ダブルパリティの RAIDZ2 など)- SnapShot, Clone- Export- 圧縮- 重複排除- Storage Pool から filesystem や Block Device (zvol) の切り出しができる- Solaris が源流、FreeBSD が主流、Linux にもあり (ZFS on Linux)- 最近 Ubuntu でデフォルトで利用できるようになった- macOS や Windows でも使えるらしい (OpenZFS)
とある自鯖ユーザーの悩み
おうち k8s を作ったぞ!Master * 3 + Node * 3 で冗長構成だ!
Node をまたがったPersistent Volume が欲しいぞ
Rook/Cephというものがあるらしい
たくさんの Block Device をひとつにまとめるらしいぞ
ZFS なら無限に Block Device を生やせるぞ
ZFS に乗せてみよう!
検証環境スペック- Node 1- Intel(R) Core(TM) i5-10210U CPU @ 1.60GHz- 32GB Memory (single channel)- SanDisk SSD Ultra 3D 1TB * 2- Node 2- Intel(R) Core(TM) i5-4460 CPU @ 3.20GHz- 16GB Memory- SanDisk SSD Ultra 3D 1TB * 2- Node 3- 今回は使わない
検証環境スペック- Cluster- k3s Master * 3 (rpi) + Node * 3- version: v1.18.6+k3s1- Rook- version: v1.3.7- Ceph- version: v15.2.4
環境構築
SSD1zvolZFS でできたなにかOSD OSDOSDCeph Cluster/dev/rbd0
環境構築- OSD のストレージプロビジョニング- Block Device? Directory?- Block Device を割り当てる方式が推奨らしい- zvol を使おう!
SSD1zvolOSDzvolOSDzvolOSDCeph Cluster/dev/rbd0zfs pool
環境構築- Block Storage を利用する- zvol をどうやってプロビジョニングする?- → pvc による dynamic provisioning を設定する- local provisioning してくれる provisioner が必要- openebs/zfs-localpv が便利
環境構築- openebs/zfs-localpv が便利- https://github.com/openebs/zfs-localpv- zfs pool から fs, zvol を dynamic provisioning してくれるやつ- local provision なので、ノードを跨いでマウントできる pvc は作成できない- openEBS- https://openebs.io/- rook 競合な Cloud Native SDS
環境構築zfs-localpv のデプロイは一発kubectl apply -f https://raw.githubusercontent.com/openebs/zfs-localpv/master/deploy/zfs-operator.yaml
SSD1zvolpvcOSDzvolpvcOSDzvolpvcOSDCeph Cluster/dev/rbd0zfs poolこれを目指す
SSD1zfs pool今はまだ、これ
localpv StorageClass をつくる
StorageClass
‘zfs’ だと filesystem‘ext(2-4)|xfs|btrfs’ だと block device(zvol) になるblock device でしか OSD on pvc は動作しないallowedTopologies を指定してpool のない node にスケジュールされないようにする
環境構築- Pool の空き容量は考慮してくれない- とにかく zvol 作りに行くし、作れなかったとしても zvol が作れたと嘘を付く- ハマった- デフォルトでは snapshot を持つ vol は削除できない- snapshot を自動生成するようにしていたら削除できずに容量使い果たした
SSD1zvolpvczvolpvczvolpvczfs poolpvc ができた
環境構築- cephcluster.yaml を書く- node が少ないので、以下の設定が必要となる- mon が同一ノードに起動することを許可する- osd を同一ノードに建てるために topologySpreadConstraints.maxSkew を増やす
node が足りないので複数 node に立つことを許可する
maxSkew: いくつまで障害ドメインの差を許容するか1node なのでたくさん設定するOSD を pvc に載せる設定
SSD1zvolpvcOSDzvolpvcOSDzvolpvcOSDCeph Clusterzfs poolクラスタができた
クラスタができた
計測だ!
計測方法● 以下の条件で fio を 10回測定し、中央値を計測○ rw=read/write○ size=1g○ numjobs=1● bandwidth, iops を抽出し記録● pool の replicated size は 2とする
計測方法● 以下の条件で fio を 10回測定し、中央値を計測○ rw=read/write○ size=1g○ numjobs=1● bandwidth, iops を抽出し記録● pool の replicated size は 2とする「妥当な計測」がなんなのか手探りで計測したのでこれは違うぞ!という内容があったら twitter でメンションください
まずは raw device と ZFS の実力
まずは raw device と ZFS の実力1. raw device を ext4 でフォーマットした vol2. zfs プールから切り出した zvola. single deviceb. 2 devices (striped)
basic benchmarkread (bw) read (iops) write (bw) write (iops)raw device 415MiB/s 106k 2498MiB/s 639kzfs (single device) 1058MiB/s 271k 2498MiB/s 639kzfs (2 devices) 1100MiB/s 282k 2485MiB/s 636k
basic benchmarkread (bw) read (iops) write (bw) write (iops)raw device 415MiB/s 106k 2498MiB/s 639kzfs (single device) 1058MiB/s 271k 2498MiB/s 639kzfs (2 devices) 1100MiB/s 282k 2485MiB/s 636kzfs にするだけで性能アップ
ZFS はキャッシュが命● ARC というインメモリキャッシュを持つ● ARC のキャッシュヒット率が高くなると速い● ZFS に載せるだけで速くなったのはそのため● fio の read benchmark はめちゃめちゃキャッシュに乗る...
ベンチ中の iostat の様子を見てみる
1 device readiostat の結果
zd0 … zvolraw … raw device (sda とか)
x軸は時系列
raw は zd0 より書き込み開始が遅い(fio は tmpfile を書いてから計測する )
zd0 で read が走るが、raw は IO が発生しない(すべてキャッシュから読んでいる )
キャッシュヒット率を見たい?
arcstat- ZFS のキャッシュの状況を確認できるスゴイヤツ- キャッシュミスの割合や、キャッシュサイズなどを確認できる- zfsonlinux に同梱されている- https://packages.ubuntu.com/ja/xenial-updates/amd64/zfsutils-linux/filelist
1 device readarcstat の結果
秒間 arc アクセス
read のうちミスしたアクセス
キャッシュミス率
arc の容量
arc target sizeキャッシュに載せようとしているサイズ
read IO が発生したのはここ
キャッシュミスしたアクセスはない
striped pool のread iostat
zvol, raw に書いてキャッシュから読む挙動は同じ
raw1, 2 の IO は全く同じ量出ている(グラフが重なっている )
シングルノード性能いよいよ Ceph
シングルノード性能- OSD を全て node 1 に寄せて計測- 1 device pool, 2 devices pool でそれぞれ計測- 3台の OSD を起動させた
read (bw) read (iops) write (bw) write (iops)raw device 415MiB/s 106k 2498MiB/s 639kzfs (single device) 1058MiB/s 271k 2498MiB/s 639kzfs (2 devices) 1100MiB/s 282k 2485MiB/s 636k1node-1disk 1504MiB/s 385k 2221MiB/s 569k1node-2disk 1517MiB/s 388k 2265MiB/s 580kシングルノード性能
read の iostat(1 device)
zvol に直接書くより書き込みが遅い(約半分)
osd へのアクセスはひとつだけ少ない
キャッシュの状態をarcstat で見る
ここが read のスパイク
全てキャッシュから読んでいる(miss がゼロ)
write の iostat(1 device)
rbd0 よりもraw device のほうが転送量が多い
zvol へのアクセスはひとつだけ突出しているread とは逆
シングルノード性能- OSD を3 → 6 に増やすとどうなる?
read (bw) read (iops) write (bw) write (iops)raw device 415MiB/s 106k 2498MiB/s 639kzfs (single device) 1058MiB/s 271k 2498MiB/s 639kzfs (2 devices) 1100MiB/s 282k 2485MiB/s 636k1node-1disk (3osd) 1504MiB/s 385k 2221MiB/s 569k1node-1disk (6osd) 1333MiB/s 341k 2124MiB/s 544k1node-2disk (3osd) 1517MiB/s 388k 2265MiB/s 580k1node-2disk (6osd) 1499MiB/s 384k 2111MiB/s 541kシングルノード性能
read (bw) read (iops) write (bw) write (iops)raw device 415MiB/s 106k 2498MiB/s 639kzfs (single device) 1058MiB/s 271k 2498MiB/s 639kzfs (2 devices) 1100MiB/s 282k 2485MiB/s 636k1node-1disk (3osd) 1504MiB/s 385k 2221MiB/s 569k1node-1disk (6osd) 1333MiB/s 341k 2124MiB/s 544k1node-2disk (3osd) 1517MiB/s 388k 2265MiB/s 580k1node-2disk (6osd) 1499MiB/s 384k 2111MiB/s 541kシングルノード性能1disk, 2disk どちらにおいても若干の性能劣化が見られる
osd の分散が大きくなった
write にコブができているIO 待ちしている?
マルチノード性能
マルチノード性能- 2disk striped なノードを2つ利用して計測- ノード間は 1GbE で接続
read (bw) read (iops) write (bw) write (iops)raw device 415MiB/s 106k 2498MiB/s 639kzfs (single device) 1058MiB/s 271k 2498MiB/s 639kzfs (2 devices) 1100MiB/s 282k 2485MiB/s 636k1node-1disk (3osd) 1504MiB/s 385k 2221MiB/s 569k1node-1disk (6osd) 1333MiB/s 341k 2124MiB/s 544k1node-2disk (3osd) 1517MiB/s 388k 2265MiB/s 580k1node-2disk (6osd) 1499MiB/s 384k 2111MiB/s 541k2node-2disk (3osd)計測ノードに 1osd 180MiB/s 46.1k 2188MiB/s 560k2node-2disk (5osd)計測ノードに 2osd 155MiB/s 39.6k 2231MiB/s 571kマルチノード性能
read (bw) read (iops) write (bw) write (iops)raw device 415MiB/s 106k 2498MiB/s 639kzfs (single device) 1058MiB/s 271k 2498MiB/s 639kzfs (2 devices) 1100MiB/s 282k 2485MiB/s 636k1node-1disk (3osd) 1504MiB/s 385k 2221MiB/s 569k1node-1disk (6osd) 1333MiB/s 341k 2124MiB/s 544k1node-2disk (3osd) 1517MiB/s 388k 2265MiB/s 580k1node-2disk (6osd) 1499MiB/s 384k 2111MiB/s 541k2node-2disk (3osd)計測ノードに 2osd 180MiB/s 46.1k 2188MiB/s 560k2node-2disk (5osd)計測ノードに 2osd 155MiB/s 39.6k 2231MiB/s 571kマルチノード性能OSD が増えると性能が劣化する
rbd0 よりもzd0, zd1 のほうがIO が出ている
zd0 + zd1 < rbd0 なのでキャッシュはある程度効いている
1node の read とスケールを合わせてみた
write は誤差の範囲
read は倍くらい違う
OSD が増えると性能が劣化している
OSD を極端に増やしてみる
OSD を極端に増やしてみる- 1node 2disk な cluster に大量の OSD を設置する- なるべく object が乗る osd が分散するように tmpfile のサイズを 10g にした- 30, 50 osd で計測
一部の OSD がCrashLoopBackOff
とりあえず増やしたら動いた
read (bw) read (iops) write (bw) write (iops)raw device 415MiB/s 106k 2498MiB/s 639kzfs (single device) 1058MiB/s 271k 2498MiB/s 639kzfs (2 devices) 1100MiB/s 282k 2485MiB/s 636k1node-1disk (3osd) 1504MiB/s 385k 2221MiB/s 569k1node-1disk (6osd) 1333MiB/s 341k 2124MiB/s 544k1node-2disk (3osd) 1517MiB/s 388k 2265MiB/s 580k1node-2disk (6osd) 1499MiB/s 384k 2111MiB/s 541k1node-2disk (30osd) 181MiB/s 46.4k 207MiB/s 52.9k1node-2disk (50osd) 158MiB/s 40.4k 144MiB/s 36.9kOSD を極端に増やしてみる
osd 30 read の iostatosd を抜粋しました
書き込みと IO 待ちを繰り返している
OSD ごとの性能差が大きくなった
rbd0 に常に write io が発生している
トップラインが伸びた
arcstat の結果
read io はこのへんひとけた増えている
ほぼキャッシュヒット(miss がほぼない)
read (bw) read (iops) write (bw) write (iops)raw device 415MiB/s 106k 2498MiB/s 639kzfs (single device) 1058MiB/s 271k 2498MiB/s 639kzfs (2 devices) 1100MiB/s 282k 2485MiB/s 636k1node-1disk (3osd) 1504MiB/s 385k 2221MiB/s 569k1node-1disk (6osd) 1333MiB/s 341k 2124MiB/s 544k1node-2disk (3osd) 1517MiB/s 388k 2265MiB/s 580k1node-2disk (6osd) 1499MiB/s 384k 2111MiB/s 541k1node-2disk (30osd) 181MiB/s 46.4k 207MiB/s 52.9k1node-2disk (50osd) 158MiB/s 40.4k 144MiB/s 36.9kベンチマークの結果は悪い...
このトップラインはすべてキャッシュ30 OSD と50 OSD の read グラフを重ねてみる
このトップラインはすべてキャッシュwrite io の帯域はだいたい同じ
このトップラインはすべてキャッシュIO 待ちが長くなっている
このトップラインはすべてキャッシュトップラインが非常に低速
50 OSD の arcstat
書き込み時はarc に乗っている
read 開始前にキャッシュが破棄されている
キャッシュミス率が40% を超えている
なぜ?
考察
考察● Read IO は伸びる○ 特にキャッシュを効かせると非常に伸びる○ zfs の特性を Rook/Ceph でも活かすことができる■ 一度ファイルを置いたら read しかしないワークロード■ メモリが余っていればだけど ...● Write IO は変化なし○ オーバーヘッド分少なくなる○ 1 Pool あたりの OSD の数が増えるとオーバーヘッドが大きくなる
考察● Rook/Ceph on ZFS 構築の難易度○ めちゃめちゃ簡単○ Multiple Devices 界で一番簡単なのでは?○ ご家庭に余った k8s クラスタとディスクがあればぜひお試しを
考察● 障害ドメイン○ Storage Pool が障害ドメイン■ OSD 分割しても Pool が死んだら全て死ぬ■ ceph on zfs による障害リスクの上昇はない● (うまく OSD 分散できれば)● Pool の件があるからドメイン分割には少し気を使う必要がある
考察- zfs はいいぞ○ ド安定○ わかりやすい CLI○ 機能が豊富■ ストレージプールの大きさを動的に増やしたり■ ライトホール問題のない RAID 組めたり■ pool ごとバックアップ取ったり● (OSD のバックアップいるか? )