【ストレージ初心者向け】分散ストレージ、Cephの速度を計る（2）

こんにちは。「Rookと仲間たち、クラウドネイティブなストレージの Advent Calendar 2020」13日目は、[前回|【ストレージ初心者向け】分散ストレージ、Cephの速度を計る（１）](https://qiita.com/tj8000rpm/items/d01510282448ef105366)に引き続きましてCephの性能測定ネタを提供していきたいと思います。

思った以上に[前回](https://qiita.com/tj8000rpm/items/d01510282448ef105366)の記事でご反響がいただけたようで、大変嬉しい限りです。

# はじめに

さて、先週の記事ではストレージの性能指標やCephのアーキテクチャ上の特性を記載しました。
今回は実際にストレージ性能の測定ツールを動かしていこうと思います。

## 目次

第一回は、知識ベースの話を記載しました。
第二回では実際に動かし、ベンチマークする上でのテクニックに関して記載します。

* [第一回（６日目）](https://qiita.com/tj8000rpm/items/d01510282448ef105366)
    * ディスクIOの仕組みについてある程度理解する
    * Rook/Cephのアーキテクチャについて留意しておく

* 第二回（１３日目）
    * なにを測定したいのかを明確にすること
        * アプリケーションから使用する場合の性能が知りたいのか？
        * ストレージシステム全体のボトルネック・性能限界を知りたいのか？
        * 理論的な限界値が知りたいのか、実践的な値がしりたいのか？
        * ワーストケースが知りたいのか？平均値が知りたいのか？
    * 測定ツールのパラメータをある程度理解する
        * 簡単な試験ならDDコマンドでも十分
        * FIOというツールにはさらいいろんなパラメータを用いることが可能
    * 最後に実際のCephの測定をしてみる
        * アプリケーションを疑似した計測例1、２
        * 結果のまとめとCephの性能の特性

---

# なにを測定したいのかを明確にすること

まずはじめにストレージの性能を計るうえでの注意点やポイントを整理したいと思います。

## あなたが知りたい情報は何ですか？

これはストレージに限った話ではありませんが、「なに」を計るかは非常に重要なことです。
当たり前の話ですが計る対象や知りたいことによりテスト手法や準備するものも大きく異なります。

とはいえ、本当に初心者な人にとってはそもそも「自分がなにが知りたいか」を把握していないことはよくあることです。
私自身もはじめてCephというものを評価する仕事を行ったとき、いったい自分自身何を評価しなければいけないのかということに非常に迷いました。

こでは代表的な観点をいくつかピックアップし紹介したいと思います。

## 観点1: アドミニストレータとユーザの観点

大きな観点として、あなたが興味ある項目は、ストレージを利用するユーザ（サーバやアプリケーション）からみた観点でしょうか、それとも、ストレージを管理するアドミニストレータ（ストレージシステム全体）の観点でしょうか。

前者のイメージは、あなたがSDカードや外付けHDD/SSDを購入してきた際や、パソコンを新調した際に、ストレージの速度を計るイメージです[^1]。

[^1]: Windows PCでのベンチマークでは[CrystalDiskMark](https://crystalmark.info/ja/software/crystaldiskmark/)などが有名ですね。

対して、後者のイメージはCephなどのストレージシステムを提供する管理者としての立場です。おそらくRook/Cephに興味のある方は、Rook/Cephを kubernetes の Persistent Volume としてや、 OpenStack の Cinder や Manila のバックエンド、あるいは Swift や S3 などのオブジェクトストレージの代替手段として利用されようとしているかと思います。
また、Rook/Cephの優れた点はこれらの異なる利用方法を単一のクラスタで実現できてしまう点です。

こういったMixされたワークロードや、複数のクライアントシステムからストレージシステムを利用された場合の最大速度やボトルネックについて知りたいといったことも考えられます。

単一のクライアントが、そのストレージシステムのすべての性能を利用することができるストレージシステム[^2]や、小規模な環境において、これらの値はもしかしたら同一かもしれません。

また、もちろん両方の観点を評価することも（特にストレージシステムのアドミニストレータとしては）必要かもしれません。

[^2]: Cephはそうではありません。また、そんなストレージシステムがこの世にあるのかを私は知りません・・・

## 観点2: 理論的な限界値か、実践的な値か

理論的にどれぐらい出るのかという値を知りたいというケースと、実践的なアプリケーション等を考えたときにどれぐらい出るのかといった点で評価したいというケースの二パターンがあるかと思います。

これは、「n K IOPS達成」という評価結果の前提が何なのかということになります。

例えば、これは後述しますが、Cephはアプリケーションがシングルスレッドで動いておりかつ、同期IOで動くようなアプリケーションであればCephの性能はおそらく低い値を示します。しかしながら、非同期IOを用いる場合や、マルチスレッドで動作するようなアプリケーションであれば良いパフォーマンスを示すかもしれません。

しかしながら、後者のような非同期IOやマルチスレッドでの動作というのは、アプリケーションによっては利用できないことが多くなります。またこれは、Cephに対しては有効かもしれませんが、物理ディスクやその他のストレージシステムでは同様に有効かどうかはわかりません。

また通常のアプリケーションではページキャッシュを用いたBuffered IOを用いる事がほとんどです。その場合はバッファーを用いない短いバイトサイズでの試験結果はあまり意味のないものかもしれません。

このように理論的な限界値を評価の基軸にすると、性能を見誤ったり、実際のワークロードに合わない可能性もあります。実際のワークロードが決まっているのならばそれを軸にするのが一番間違いがないでしょう。

もちろん、こちらもどちらのパターンも評価をしておくことは言うまでもなく重要となります。

## 観点3: 最良値か平均値か、最悪値か。

これも何もストレージシステムに限った話ではありませんし前述の理論的 or 実践的に近いものがあります。

例えば、最良値を知りたい場合には環境の作成や、細かなチューニングが必要になるかもしれません。
また、最悪値を計りたい場合には考えうるワーストなケースを意識したり、ノイジーネイバーを意識したりする必要があります。
しかしながら、これらの最悪値も最良値ももしかしたら、あまりにも極端な値であり、現実的なワークロードを考えた場合には意味がないものかもしれません[^3]。

[^3]: バースト的にそういった最悪値のトラフィックが発生することがあってもそれが定常的になるのであればそもそもその設計容量等に問題があるのは明白です。

もちろん、こういった指標もすべてデータとして持っていることは設計から、トラブルシューティング時まで様々な面で助けになることに間違いがありませんので、多角的観点で評価することも重要ではあります。

しかしながら、時間は有限であり、リソースも限られるため何を重視するのかについては優先順位をつける必要あると思います。

## 観点のまとめ

![storage-bench-vewpoints.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207353/f8a789ad-1470-4c1b-8dee-e2e8d1d45562.png)


これらの観点の中から目的がどれに近いのかを意識すれば無駄な項目をベンチマークをとったり、意味のないパラメータで評価したりする必要もなくなります[^4]。

また、以降は上記の観点からも手法や結果を評価したいと思います。

[^4]: 私は本当にここに書いてあることをはじめに理解していなかったため、かなり遠回りな評価をやってしまいました。

# 測定ツールのパラメータをある程度理解する


## アクセスパターン、シーケンシャルとランダム

これは第一回のなかで紹介すべき話題だったかもしれませんが、漏れていましたのでここで記載します。

よくストレージのベンチマークにおいては、「シーケンシャルライトではxK IOPSですが、ランダムライトではyK IOPSでした」といった形で、「シーケンシャルxx」と「ランダムxx」が比較されることがあります。

これは一つのファイルはある程度の塊で連続して配置されていることが多くあるために生じる考慮事項です。

例えば、動画ファイルを例にとれば簡単に理解できると思います。
メモリが4GBしか積んでいないパソコンで巨大な動画ファイル（たとえばDVD 4.7GB分の映画）をすべてメモリにロードしてしまうとメモリ不足となり映画を見ることができません。

ですので映画をたとえば30秒単位（この数字は全くのでたらめです[^tipsvoice]）で断片的に読み込んでいけば、60秒分のデータをメモリにロードしておくだけで済みます。このようにデータには連続性があり、また次にアクセスするデータも同じ領域に近いという特性が「一般的」にはあります（とはいえ、コンピュータからみた一塊のデータが実際に物理ディスクの隣接した物理領域に常に存在しているわけではありません[^5]）。

[^tipsvoice]: 全く関係ないプチ情報：電話は20 msecごとに音声データを分割してパケットを運んでいることが多いです！（私の正体は電話屋）

こういった連続したデータへのアクセスのことをシーケンシャルアクセスといいます。
シーケンシャルアクセスの場合、次に呼び出せれるデータや次に書き込むデータなどをある程度予測できるため、データへのアクセスをハードウェア的、ソフトウェア的な最適化により効率化しているケースが多々あります（ページキャッシュなどもその例です）。

![storage-bench-seq.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207353/dd7f6c42-55bd-2508-a3a5-6e8359e48ada.png)


対して、ワークロードによってはシーケンシャルにアクセスしないようなケースも存在します。
たとえば、小さなファイルが大量にあるようなシステムでかつ、不規則にそのファイルにアクセスする必要があればランダムなアクセスになるでしょうし、最適化されていないようなデータベースなどでも同じように散らばったデータを探す必要が出てくるかもしれません。

![storage-bench-rand.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207353/4fd0d3ba-04ac-1285-4ff6-81db0e375f3a.png)


シーケンシャルアクセスに比べてランダムアクセスは次のアクセスに対する予測が難しいため、最適化による効率化が難しい側面があります。また、回転ディスクを用いるHDDでは物理的にあちこちにヘッドを移動させる必要があるため、読書きの時間が非常に低速になります[^6]。

[^5]: いまどきの若い人には通じないネタかもしれませんが、Windowsを高速させるための「デフラグ」とはこの一つのファイルを連続した領域に集めなおす作業でした。
[^6]: ヘッドのシーク時間等の計算を基本情報技術者試験や応用情報技術者試験で勉強された方も多いと思います

これらのストレージの特性から、一般的に、ランダムアクセスのほうがストレージ自身の持つオーバヘッドを顕著に表す傾向が強く、また各種の最適化の恩恵を受けない、ワーストケースを表すことが一般的にあります。
そのため、ストレージの評価に当たっては、シーケンシャルの場合と、ランダムの場合で比較して評価がなされたり、ベンチマークツールの中でそれらを指定できるようになっています。

## 簡単な試験ならDDコマンドでも十分

さて、では実際にストレージの試験をするうえではどういったツールを用いればよいでしょうか？

シングルスレッドでかつ、シーケンシャルアクセスを評価するのであれば、ファイルのコピーやディスクのバックアップ・リストアや削除などにも用いられる行うことができる`dd`コマンドというプログラムを用いることができます。
この`dd`はどのOSディストリビューションにも入っているはずですので、どのような環境でも簡単に計測ができます。

---

### 注意
今回の例では `/dev/vdb` に直接書出していますが、
これは計測試験用に新たに作成した、仮想ストレージデバイスです。

また、利用している環境も試験のためだけに作成した仮想マシンを利用しています。
この内容が理解できない場合は安易なコピペだけには十分気をつけてください・・・！[^caut]

[^caut]: 間違ってもルートディスクになっている`/dev/vda`や`/dev/sda`に書き込まないでくださいね・・

---

### シーケンシャルライト

では実際にシーケンシャルライトの評価を行ってみます。

結論的には以下のコマンドを実行した結果を見てみます。

```shell-session
$ sudo dd oflag=direct if=/dev/zero of=/dev/vdb bs=4K count=256000
256000+0 records in
256000+0 records out
1048576000 bytes (1.0 GB, 1000 MiB) copied, 262.252 s, 4.0 MB/s
```

このコマンドの引数の意味は以下のとおりです。
1. `oflag=direct`を指定することによりページキャッシュを経由しないで書き込みを行っている
2. `if=/dev/zero`を指定することにより、読出しにディスクを使用しない
3. `of=/dev/vdb`で書込みディスクを指定、今回`/dev/vdb`が測定対象となる cephのRBDで作られたデバイス
4. `bs=4K`を指定することによりブロックサイズを`4 KB`に指定している
5. `count=256000`を指定することにより、`4KB * 256,000 = 1,000,000KB = 1GB`分のデータ書き出しを実施

実行結果からは次のことが言えます。
1. 1.0GB のファイルの書込みに 262.252秒かかった
2. 1.0GB(1,000MiB = 1048MB) のファイルは 4MB/sの速度で書き込まれた(`1048MB / 262.252 = 3.996MB/s`)
3. 256,000回のIOに262.252秒かかった、およそ1,000 IOPS（`256,000 / 262.252 = 976 IOPS`）

### シーケンシャルリード

同様に次はシーケンシャルリードについて確認します。

```shell-session
$ sudo dd oflag=direct if=/dev/vdb of=/dev/null bs=4K count=256000
sudo: unable to resolve host tb6-az-a2-ceph-test: Name or service not known
dd: failed to open '/dev/null': Invalid argument
ubuntu@tb6-az-a2-ceph-test:~$ sudo dd iflag=direct if=/dev/vdb of=/dev/null bs=4K count=256000
sudo: unable to resolve host tb6-az-a2-ceph-test: Name or service not known
256000+0 records in
256000+0 records out
1048576000 bytes (1.0 GB, 1000 MiB) copied, 127.897 s, 8.2 MB/s
```

このコマンドの引数の意味は以下のとおりです。
1. `iflag=direct`を指定することによりページキャッシュを経由しないで読み込みを行っている
2. `if=/dev/vdb`で読込みディスクを指定、今回`/dev/vdb`が測定対象となる ceph のディスク
3. `of=/dev/null`を指定することにより、書出しにディスクを使用しない
4. `bs=4K`を指定することによりブロックサイズを`4 KB`に指定している
5. `count=256000`を指定することにより、`4KB * 256,000 = 1,000,000KB = 1GB`分のデータ書き出しを実施

また実行結果からは次のことが言えます。
1. 1.0GB のファイルの読込みに 127.897秒かかった
2. 1.0GB(1,000MiB = 1048MB) のファイルは 8.2 MB/sの速度で読込まれた(`1048MB / 127.897 = 8.19 MB/s`)
3. 256,000回のIOに127.897秒かかった、およそ2,000 IOPS（`256,000 / 127.897 = 2002 IOPS`）

### ブロックサイズを変えてみる

では更にブロックサイズを変えてみて確認します。

```shell-session:Writeの結果
$ sudo dd oflag=direct if=/dev/zero of=/dev/vdb bs=4M count=250
sudo: unable to resolve host tb6-az-a2-ceph-test: Name or service not known
250+0 records in
250+0 records out
1048576000 bytes (1.0 GB, 1000 MiB) copied, 5.70676 s, 184 MB/s
```

```shell-session:Readの結果
$ sudo dd iflag=direct if=/dev/vdb of=/dev/null bs=4M count=250
sudo: unable to resolve host tb6-az-a2-ceph-test: Name or service not known
250+0 records in
250+0 records out
1048576000 bytes (1.0 GB, 1000 MiB) copied, 3.98156 s, 263 MB/s

```

それぞれ、引数の差分は次のとおりです。

4. `bs=4M`を指定することによりブロックサイズを1000倍である`4 MB`に指定している
5. `count=250`を指定することにより、`4MB * 250 = 1,000MB = 1GB`分のデータを読み書き

これらの実行結果からは次のことが言えます。

1. Writeは 44 IOPS(=250/5.71) / 184 MB/s
2. Readは 63 IOPS(=250/3.98) / 245 MB/s 
3. Writeは4KBのブロックサイズのときと比較して（同じデータ量にたいして）52倍高速
4. Readは4KBのブロックサイズのときと比較して29倍高速

同じ方法で以下まとめた結果です

|読み書き|bs=|count=|IOPS|スループット|1 GBのIO時間|1 I/O あたりのレイテンシ|
|:--|--:|--:|--:|--:|--:|--:|
|Write|4 KB|256,000|976|4 MB/s|262 sec|1.02 msec|
|Write|16 KB|64,000|901|14.8 MB/s|71.1 sec|1.11 msec|
|Write|64 KB|16,000|762|50.0 MB/s|21.0 sec|1.31 msec|
|Write|256 KB|4,000|432|113 MB/s|9.25 sec|2.31 msec|
|Write|1 MB|1,000|162|170 MB/s|6.18 sec|6.18 msec|
|Write|4 MB|250|44|184 MB/s|5.71 sec|22.7 msec|
||||||||
|Read|4 KB|256,000|3647|14.9 MB/s|70.2 sec|0.27 msec|
|Read|16 KB|64,000|2645|43.3 MB/s|24.2 sec|0.38 msec|
|Read|64 KB|16,000|1212|79.3 MB/s|13.2 sec|0.83  msec|
|Read|256 KB|4,000|484|127 MB/s|8.27 sec|2.07 msec|
|Read|1 MB|1,000|192|201 MB/s|5.22 sec|5.22 msec|
|Read|4 MB|250|63|263 MB/s|3.98 sec|15.92 msec|

第一回で紹介したように、

* ブロックサイズが大きくなるに連れて、IOPSが低下し、スループットが上昇していること
* IOあたりのレイテンシも上昇していること

ということがわかるかと思います。

さらに、レイテンシに注目いただきたいですが、ブロックサイズが小さい場合、レイテンシに大差が無いことがわかるかと思います。
特に書込みの結果で顕著にわかるかと思いますが、ブロックサイズ（1度の転送データ量）が16倍も違う、4 KBと64 KBの結果において１IOあたりの時間の差は、`0.3　msec`しかありません。
裏を返せば`60 KB`[^calcd]のデータ転送におおよそ、`0.3 msec`程度かかっているということになります。
大雑把な計算ですが、これは`0.3 [msec] / 60 [KB] = 0.005 [msec/KB]` すなわち 1キロバイト当たり`5 usec`程度の転送率です。

[^calcd]: `64 [KB] - 4 [KB]`

この値を上記の測定結果に当てはめて考えてみましょう。

4 KB のデータには `1.02 msec` かかっています。このとき 4KB のデータを送るのには ` 0.005 [msec] * 4 [KB] = 0.02 [msec]`となる計算です。
しかし実際には `1.02 msec`かかってるわけですから、 `1.02 [msec] - 0.02 [msec]`でオーバヘッドが**1ミリ秒**ほど発生していることがわかります。

![storage-bench-estimate.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207353/37d4686e-073b-1d65-5d04-c07fafd65b5e.png)


他にも256 KBの結果で見てみると、データの転送速度である`0.005 msec/KB`にブロックサイズの`256 KB`をかけ、上記の計算で導いた、オーバヘッドの`1 msec`を足し込むと`0.005 * 256 + 1 = 2.28 [msec]` と実際の`2.31 msec`という結果に近い値が導き出されました。
同様に、1 MBですと、`0.005 * 1000 * 1 = 6 [msec]`ですので、こちらも`6.18 msec`に近い値を導き出せています。

読込みの結果で計ってみるとオーバヘッドの値がかなりばらつきました（0.25〜0.6 msec　程度）。こちらの詳細な考察は省きますが[^cachehit]、これは（おそらくですが）キャッシュの影響だと考えられます。今回の測定環境では仮想マシンを用いており、ハイパーバイザではライトスルー方式のキャッシュを設定しています。書込みはライトスルーとなるため必ず実際にディスクへのアクセスが発生し、レイテンシも安定していますが、読込みについてはハイパーバイザ等でのキャッシュにヒットしてしまうことや、また、Ceph OSDノード上でのキャッシュもあるためそれらの影響からゆらぎが生じていると想定されます。

Cephのもつ多層でのキャッシュの話は[前回](https://qiita.com/tj8000rpm/items/d01510282448ef105366#%E3%82%AD%E3%83%A3%E3%83%83%E3%82%B7%E3%83%A5%E3%81%AE%E5%AD%98%E5%9C%A8)記載していますのでご参考にしてみてください。

[^cachehit]: というか、時間もなくちゃんと中身追いきれてませんので、読込み時のバラツキの考察については若干外しているかもしれません・・・。

## Flexible IO tester / fio

さて、ここまで`dd`コマンドを用いたテスト方法を紹介してきました。
しかしながら`dd`コマンドはもともと、ディスクのデータコピーやレストア、あるいは破壊などに使うツールとなります。
テストの用途で使用するには結果の出力が不足していたり、テストパターンも限られたりしてしまいます。

また、特に「ランダムアクセス」方式でのアプリケーションの疑似動作や「マルチスレッド」での動作、「非同期IO」といった、ディスクへの読み書き性能を向上させる技術を用いることも難しくなります。

そのため、実際のストレージの性能測定には、よりストレージの性能測定に特化したソフトウェアが広く用いられており、代表的なツールが、[Flexible IO Tester / fio](https://fio.readthedocs.io/en/latest/fio_doc.html) となります。

### fio の多様なパラメータ

fioは名前にFlexibleとつくように多種多様なオプションパラメータにより、様々なストレージのワークロードを疑似する事ができます。
そのオプションの数は 2020-12-12現在で 235 のも及ぶようです[^fioopt]。

[^fioopt]: [fioのリポジトリ](https://github.com/axboe/fio.git)をCloneして、masterブランチにおいて`egrep '	\.name[^=]+=' options.c |grep -v NULL |wc -l`で集計。

さすがにすべてのオプションを理解している人は少ないと思いますので、必要になってから確認すれば良いと思います。

以下に代表的な筆者がよく用いるオプションパラメータを記載します。

|種別|パラメータ|設定例|内容|
|:--|:--|:--|:--|
|[Target file/device](https://fio.readthedocs.io/en/latest/fio_doc.html#target-file-device)|filename|/dev/vdb|測定する対象を指定します。ファイルでも良いですし、デバイスの指定でもOKです。|
|[I/O engine](https://fio.readthedocs.io/en/latest/fio_doc.html#i-o-engine)|ioengine|libaio|同期IOか、非同期IOか、あるいはRBDを直接叩くか等を指定します。何も指定しなかった場合は同期IOである`sync`が指定されます。|
|[I/O type](https://fio.readthedocs.io/en/latest/fio_doc.html#i-o-type)|direct|1|ページキャッシュを用いないでデバイスに直接書込みます。ddコマンドで用いていた `oflag=direct`と同等の処理です。|
||rw|randwrite|これは dd コマンドでは指定できなかった項目です。randwriteと書くことで、ランダムアクセスでの書込みをテストすることができます。またread/writeのIOを混ぜることも可能です|
|[Block size](https://fio.readthedocs.io/en/latest/fio_doc.html#block-size)|bs|4k|ddと同じくブロックサイズを指定できます|
|[I/O size](https://fio.readthedocs.io/en/latest/fio_doc.html#i-o-size)|size|100G|テストで読み書きする領域の大きさです。ddではファイルサイズを指定するためにブロックサイズと`count`による総IO数で計算する必要がありましたが、fioではファイルサイズで指定可能です|
|[Job description](https://fio.readthedocs.io/en/latest/fio_doc.html#i-o-size)|numjobs|1|並列に動作させるジョブ（スレッド）の数です。　fioでは複数のプロセスから同時にioを発行することが可能です。これでマルチスレッド環境やマルチプロセス環境での性能を計ることができます。`dd`コマンドでこれをやるためには自分自身で複数のプロセスを生成する必要がありましたがfioでは自動でやってくれます|
||loops|-1|`size`分のIOを行ったあとに何回まで繰り返すかです。`runtime`オプションだけ必ずテストを実行させたい場合は`-1`を指定して無限ループとしておくのが良いと思います。|
||name|file1|テストに名前をつけることができます。後述の通りレポートを作成できますのでその際の分類わけに利用できます|
|[I/O depth](https://fio.readthedocs.io/en/latest/fio_doc.html#i-o-depth)|iodepth|1|これは`numjobs`と非常に紛らわしく、また初心者には理解が難しいポイントかと思います（私も最近理解）。これは非同期IOなどの`ioengine`を利用して初めて有効となるオプションです。1つの非同期IOジョブでの並列数となります。(`ioengine=io_uring`等)。ですので、デフォルトの`ioengine=sync`を指定する（あるいは`ioengine`のオプションを指定しない）場合は必要の無い（意味のない）パラメータとなります。意外とこの事を書いている記事を見たこと無いので、このパラメータの解説はここがハイライトです|
|[Time related parameter](https://fio.readthedocs.io/en/latest/fio_doc.html#time-related-parameters)|runtime|120|ジョブの実行時間を指定します。`loop=1`の場合は`runtime`で指定した時間より先に読み書きが終わってしまうと、`runtime`より早く終わります。`runtime`の時間必ずIOをかけたい（たとえば背景負荷を加えたいなど）場合は`loop=-1`を忘れず指定する必要があります。また逆に`runtime`を指定していても`size * loop`分のデータへのアクセスがすべて終了するとテストは終了します|
|[Measurements and reporting](https://fio.readthedocs.io/en/latest/fio_doc.html#measurements-and-reporting)|group_reporting|-|(Flagのためパラメータ値は不要です)`numjobs > 1`のときに、各ジョブの結果をまとめて出力してくれます。全体で何IOPS出たか、などを見るときに使用します。逆に`numjobs`は複数並列だが、個々のジョブごとのiopsなどを知りたい場合は指定する必要がありません|
|[Command line options](https://fio.readthedocs.io/en/latest/fio_doc.html#command-line-options)|--output-format|json|テストの実行結果のレポートを出力する機能がfioには存在します。fioの出力結果のフォーマットを何にするか指定できます(csv, json)。おすすめは`json`出力で、jqをパイプして利用するのが個人的にはおすすめです|
||--status-interval|1|上記のリポートは通常ジョブの終了時に出力されます。本インターバルを指定すると、指定した秒数ごとに上記フォーマットで途中経過を出力します|


### 代表的なパラメータ設定例

以下が、 シングルスレッド、4K、同期IOでのシーケンシャルリードの実行例です。これは`dd`コマンドのときとほぼ同じ状況となります。

```shell-session
$ sudo fio --ioengine=sync --numjobs=1 --direct=1 --bs=4k --rw=read \
           --size=1G --filename=/dev/vdb --name=single-thread-seq-read
single-thread-seq-read: (g=0): rw=read, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=sync, iodepth=1
fio-3.20-dirty
Starting 1 process
Jobs: 1 (f=1): [R(1)][100.0%][r=15.2MiB/s][r=3901 IOPS][eta 00m:00s]
single-thread-seq-read: (groupid=0, jobs=1): err= 0: pid=1241: Sat Dec 12 12:22:33 2020
  read: IOPS=4105, BW=16.0MiB/s (16.8MB/s)(1024MiB/63852msec)
    clat (usec): min=136, max=5160, avg=242.42, stdev=42.38
     lat (usec): min=136, max=5160, avg=242.57, stdev=42.38
    clat percentiles (usec):
     |  1.00th=[  176],  5.00th=[  192], 10.00th=[  200], 20.00th=[  215],
     | 30.00th=[  227], 40.00th=[  241], 50.00th=[  247], 60.00th=[  251],
     | 70.00th=[  253], 80.00th=[  260], 90.00th=[  277], 95.00th=[  297],
     | 99.00th=[  351], 99.50th=[  375], 99.90th=[  490], 99.95th=[  562],
     | 99.99th=[ 1123]
   bw (  KiB/s): min=14520, max=17712, per=100.00%, avg=16449.07, stdev=478.33, samples=127
   iops        : min= 3630, max= 4428, avg=4112.27, stdev=119.58, samples=127
  lat (usec)   : 250=58.26%, 500=41.65%, 750=0.07%, 1000=0.01%
  lat (msec)   : 2=0.01%, 4=0.01%, 10=0.01%
  cpu          : usr=1.12%, sys=3.39%, ctx=262200, majf=0, minf=13
  IO depths    : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     issued rwts: total=262144,0,0,0 short=0,0,0,0 dropped=0,0,0,0
     latency   : target=0, window=0, percentile=100.00%, depth=1

Run status group 0 (all jobs):
   READ: bw=16.0MiB/s (16.8MB/s), 16.0MiB/s-16.0MiB/s (16.8MB/s-16.8MB/s), io=1024MiB (1074MB), run=63852-63852msec

Disk stats (read/write):
  vdb: ios=261315/0, merge=0/0, ticks=61256/0, in_queue=16, util=100.00%
```

`output-format`はデフォルトの出力です。かなりの情報量があります。

まず、ddコマンドと同じ値を確認していきます(単位系がMiB/KiBだったりしますが、だいたいの値で割愛)。

1. IOPSは 最小 `3630`、最大 `4428`、平均で`4112.27`だったことが以下の値からわかります。  
   `iops        : min= 3630, max= 4428, avg=4112.27, stdev=119.58, samples=127`
1. スループットについても同様に、最小 `14.52 MB/s` 最大 `17.71 MB/s`、平均で`16.45 MB/s`だったことがわかります。  
   `bw (  KiB/s): min=14520, max=17712, per=100.00%, avg=16449.07, stdev=478.33, samples=127`
1. レイテンシは若干ややこしいです。というのもfioには`clat`、`lat`と　2つのレイテンシを表すメトリクスが表示されます。これはそれぞれ、`Completion latency`、`Latency`の略です。これらはそれぞれ、計測区間が違うようです。clatはfioの内部処理を含まないIOの命令を実行してから応答があるまでの値であり、latはfioの内部処理についても含むようです[^notelat]。ddと比較する上では`lat`のあたいを見てみます。  
   レイテンシは最小`136 usec = 0.136 msec`、最大`5160 usec = 5.16 msec`、  平均で`242.57 usec = 0.243 msec`だったことがわかります。
　 `lat (usec): min=136, max=5160, avg=242.57, stdev=42.38`

上記の通り 4 KB のシーケンシャルリードにおいては `dd`コマンドの結果と近い値が出ていることがわかります。

いくつかのfioとddコマンドでのシーケンシャルアクセス、シングルスレッドの結果を記載します。

|測定ツール|読み書き|ブロック|IOPS|スループット|レイテンシ|
|:--|--:|--:|--:|--:|--:|
|dd|Read|4 KB|3647|14.9 MB/s|0.27 msec|
|fio|Read|4 KB|4112|16.8 MB/s|0.243 msec|
||||||||
|dd|Read|4 MB|63|263 MB/s|15.92 msec|
|fio|Read|4 MB|54|216 MB/s|19.37 msec|
||||||||
|dd|Write|4 KB|976|4 MB/s|1.02 msec|
|fio|Write|4 KB|973|3.89 MB/s|1.03 msec|
||||||||
|dd|Write|4 MB|44|184 MB/s|22.7 msec|
|fio|Write|4 MB|47|189 MB/s|22.1 msec|

シングルスレッド、シーケンシャルアクセスの結果においては、`dd`コマンドと同じような結果を出力している事がわかります。

### 同期IOと非同期IO

ちょうどLinux Advent Calendar 2020　6日目に解説が上がっていました、[Linuxにおける非同期IOの実装について](https://qiita.com/tmsn/items/0b9e5f84f9fbc56c1c82)こちらのポストが大変詳しいですし、勉強になります。

簡単に噛み砕いて説明すると同期IOにおいては一つのスレッドでIOしている間、後続のIOはブロックされます。
これは`write`や`read`といったPOSIX システムコールが書込み完了をまって帰ってくるためです。
そのため、同期IOにおいてはどうしてもIO中の待ち時間が発生します。

![storage-bench-sync-single.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207353/33cc7cfd-b2c9-a15f-1c4d-fe4e35b9f773.png)

この待ち時間を解決するためには、ユーザは通常スレッドを使用したりするなどアプリケーションの実装を工夫する必要があります。
ただし、後述もしますが、スレッドの利用は性能面でのコストが高い上に、アプリごとに実装する必要がでてきてしまいます。
この問題を解決をするために非同期IO方式のシステムコールをOSの側に用意しているのが、非同期IO方式です。上記のQiitaのポストでも紹介されている`linux aio`や`io_uring`がそれに当たります。

非同期IO方式はもともと、IOの待ち時間中に別のタスクを処理可能という点が有利な点ですが、昨今のストレージデバイスは高速化しており、また物理的なキューサイズ等も増えていることもあるため、非同期IOを複数走らせることによる読み書き性能の向上についても期待できるため、fioにおいてはそれらの非同期IOをioエンジンにして多数のIO負荷をかけることができます。このときにfioに非同期IOを指定するのが`ioengine=io_uring`オプションや`ioengine=libaio`です。また、図中にあるように何本のIOを同時に走らせるのかを`iodepth=32`等で指定します。

![storage-bench-async-single.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207353/5ee7326c-7854-5621-f02f-9e596fd9a572.png)

fio上でこの非同期IOと似たようなことができるのが`numjobs`オプションです。
これは単純に規定したジョブをいくつ同時で動かすかを指定します。プロセスが増えるだけですので潤沢にCPUがあれば同じ効果が得られます。

![storage-bench-sync-multi.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207353/fdb68edf-dc3b-2186-f346-a482e8c8a49a.png)

とはいえ、マルチジョブではスレッド数やプロセス数が増えるため、スケジューラ等でのOSのオーバヘッドが増えてしまうため、非同期IOに比べてスケールしにくいのは事実です（なのでCPUが潤沢にあればと記載してます）。ですので利用できる環境であるのならば、`io_uring`等の非同期IOエンジンを使用したほうがベンチマークのスコアも良くなるかと思います。

[^notelat]: 私も現状ちゃんと理解できていないです・・。


# 最後に実際のCephの測定をしてみる

またまた、思っていた以上に前置きが長くなってしまいました・・・。すみません。
最後に実際にCephを実際に計測して評価してみたいと思います。

上で記載したの観点１，観点２、観点３でそれぞれ結果や手順や結果を紹介したかったところですが、残念ながら書き始めたら他にもいっぱい書くことがあったことにより縮小してお送りします。

今回はCephの利用ユーザの観点から、「Cephは遅い」と言われる点と、「いやCephだって使い方次第でそこそこ速いよ」という２つの側面を示す例を紹介したいと思います。

## 評価１：シングルスレッドでのIO性能

まずは、Cephのシングルスレッド・同期IOの性能を見てみたいと思います。

これは上記の `dd`コマンドやfioのシングルスレッドの結果からある程度想像できるかと思いますが、改めてfioを用いて確認してみます。

### fioのパラメータ

```shell-session:fioの実行コマンド
$ sudo fio --ioengine=sync --numjobs=1 --direct=1 --bs=$BS --rw=$RW \
           --size=1G --filename=/dev/vdb --name=single-thread-$RW-$BS \
           --output-format=json
```

このパラメータを基本として、`$RW`にシーケンシャルリードである`read`、ランダムリードである`randread`、シーケンシャルライトである`write`、ランダムライトである`randwrite`の評価を行います。また`$BS`に`4k` `16k` `64k` `256k` `1m` `4m`を与えていきそれぞれの値を表示していきたいと思います。

### 実行

実際のシェルスクリプト含めて以下の形で実行します。`--output-format=json`を指定して、かつ、`jq`を用いて必要な要素だけをパースします。

以下のスクリプトを見ていただければ解ると思いますが、このforループでは、4k ライト、 4k リード、 4k ランダムライト、 4k ランダムリード、8k ライト、 8k リード、 8k ランダムライト、 8k ランダムリード・・・と進んでいくことがわかるかと思います。
この`RW`とブロックサイズの与え方には実は意図があります。

というのも今回の試験環境では仮想マシン環境を用いていますので、仮に4k　リード、 8k リード、 16k リード・・・と連続して与えた場合、
リードが連続してしまうと高確率でキャッシュにヒットしてしまうため若干歪んだグラフとなってしまいます。
もちろん、リードの前に一旦ディスクを書いたり、キャッシュをフラッシュするようなスクリプトを実行すればイイのですが、めんどくさかったのでこのようにライトのあとにリードが来るように試験パターンを工夫してます。

```shell:fioの実行コマンド
for BS in 4k 16k 64k 256k 1m 4m
do
    for RW in write read randwrite randread
    do
        sudo fio --ioengine=sync --numjobs=1 --direct=1 --bs=$BS --rw=$RW \
                 --size=1G --filename=/dev/vdb --name=single-thread-$RW-$BS \
                 --runtime=30 -output-format=json | \
                 jq -cr '[."global options".bs, ."global options".rw, 
                          .jobs[0].read.iops, .jobs[0].write.iops, 
                          .jobs[0].read.bw, .jobs[0].write.bw]|@csv'
    done
done
```

### 実行結果

実行しますと以下のようなCSVを出力するので、LibreOffice Calc[^excel]を用いてグラフ化します。

[^excel]: UbuntuのクライアントPCを用いてるので、Officeじゃないんです・・・

```csvs
"4k","write",0,972.40092,0,3889
"4k","read",3236.192127,0,12944,0
"4k","randwrite",0,776.174128,0,3104
"4k","randread",2303.556548,0,9214,0
"16k","write",0,869.737675,0,13915
"16k","read",2067.19776,0,33075,0
"16k","randwrite",0,715.509483,0,11448
"16k","randread",1981.267291,0,31700,0
"64k","write",0,673.102995,0,43078
"64k","read",620.277126,0,39697,0
"64k","randwrite",0,586.924593,0,37563
"64k","randread",570.314676,0,36500,0
"256k","write",0,337.369245,0,86366
"256k","read",496.785931,0,127177,0
"256k","randwrite",0,313.677439,0,80301
"256k","randread",470.534176,0,120456,0
"1m","write",0,118.299445,0,121138
"1m","read",156.83872,0,160602,0
"1m","randwrite",0,115.185602,0,117950
"1m","randread",153.776843,0,157467,0
"4m","write",0,33.04505,0,135352
"4m","read",47.424972,0,194252,0
"4m","randwrite",0,33.298647,0,136391
"4m","randread",46.334842,0,189787,0
```


![storage-bench-iops-sync-job1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207353/c1011f1d-7310-2938-de11-af472a64a6a7.png)


結果を見てみると、シーケンシャルアクセスでは先程の`dd`コマンドでの結果と変わらない結果が見て取れます。シーケンシャルライトで1000 IOPS弱、シーケンシャルリードで3000 IOPS程度出ています。
また、`dd`コマンドでは計れなかったランダムアクセスの結果についてもfioでは確認できます。シーケンシャルアクセスと比較して、2割ほど性能が劣化しているようです。

## 評価2：非同期IOでのIO性能

次に、評価１と同様に、「ユーザの観点」から、Cephのシングルスレッド・非同期IOの性能を見てみたいと思います。
非同期IOには`ioengine=io_uring`を指定し、並列数である`iodepth`の値を`16` `32` `64` `128` と増やして試験します。

### fioのパラメータ

```shell-session:fioの実行コマンド
$ sudo fio --ioengine=io_uring --numjobs=1 --iodepth=$DEPTH --direct=1 --bs=$BS --rw=$RW \
           --size=1G --filename=/dev/vdb --name=io_uring-$DEPTH-$RW-$BS \
           --runtime=30 --iodepth=$DEPTH --output-format=json
```


### 実行

`RW` `BS` については先程の試験１と同じ考え方です。`iodepth`については１６から倍々していき、１２８まで増やしてみます。

```shell:fioの実行コマンド
for DEPTH in 16 32 64 128
do
    for BS in 4k 16k 64k 256k 1m 4m
    do
        for RW in write read randwrite randread
        do
            sudo fio --ioengine=io_uring --numjobs=1 --direct=1 --bs=$BS --rw=$RW \
                     --size=1G --filename=/dev/vdb --name=io_uring-$DEPTH-$RW-$BS \
                     --runtime=30 --iodepth=$DEPTH --output-format=json | \
                     jq -cr '[.jobs[0]."job options".iodepth,
                              ."global options".bs, ."global options".rw, 
                              .jobs[0].read.iops, .jobs[0].write.iops, 
                              .jobs[0].read.bw, .jobs[0].write.bw]|@csv'
        done
    done
done
```

### 実行結果（書込み）

![storage-bench-iouring-write.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207353/b3a7e887-57de-55e4-9f28-0f91b43b4bc4.png)

実行結果を見てみると、シングルスレッド・同期IOとは比較にならない数字が出ていることがわかります（もちろん同じVMから計測して、同じRBDデバイスを使っています！）。最大で29,976 IOPS = 30 KIOPSの性能がました。またスループットでも 706MB/s、5Gbps程度で転送できています。IOPSについては`iodepth`の増加に対してリニアに伸びていることがわかります。今回の例ではさらなる負荷をかけていませんが、`iodepth`を更に増加させてみても良いかもしれません。

またグラフからぱっとみてわかるとおり、ランダムアクセスのほうがシーケンシャルアクセスに比べて性能が良い結果が出ています。
これは上記の同期IOとは異なる特性であり、一見すると不思議に思われます。ただ、これがCephの特徴であるとも言えます。
[前回の記事で紹介した](https://qiita.com/tj8000rpm/items/d01510282448ef105366#%E3%83%97%E3%83%AC%E3%83%BC%E3%82%B9%E3%83%A1%E3%83%B3%E3%83%88%E3%82%B0%E3%83%AB%E3%83%BC%E3%83%97%E3%81%AB%E3%82%88%E3%82%8B%E5%88%86%E6%95%A3%E8%AA%AD%E3%81%BF%E6%9B%B8%E3%81%8D)とおりCephは4MBのオブジェクトに対してプレースメントグループ＝OSDのペアやトリオが決定されます。オブジェクトサイズに満たないブロックサイズのシーケンシャルアクセスでは同一のオブジェクトに対して連続してアクセスが集中することがわかります。
対して、ランダムアクセスではPGのアクセスについても同様に分散されることがわかります。そのため、非同期IOでIO処理を並列化した際にはIOが互いに干渉することなく処理できるためランダムアクセスのほうが性能が良い結果となっていると考察できます。

![storage-bench-pg-seq.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207353/b91fd5a0-d440-dec2-c501-c7116f7a8e46.png)

グラフ化する前の数値については以下のとおりです。

|Write|IOPS(depth=16)|IOPS(depth=32)|IOPS(depth=64)|IOPS(depth=128)|MB/s(depth=16)|MB/s(depth=32)|MB/s(depth=64)|MB/s(depth=128)|
|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|
|4k|9,115.52|14,402.73|15,358.80|18,280.61|37.34|58.99|62.91|74.88|
|16k|5,720.67|8,948.12|10,682.31|12,240.57|93.73|146.61|175.02|200.55|
|64k|2,896.75|3,360.82|4,092.93|5,231.16|189.84|220.25|268.23|342.83|
|256k|1,001.96|1,288.46|1,705.25|2,154.66|262.66|337.76|447.02|564.83|
|1m|365.98|544.10|591.91|627.45|383.75|570.53|620.66|657.93|
|4m|150.23|151.66|156.10|156.86|630.13|636.10|654.72|657.93|

|randWrite|IOPS(depth=16)|IOPS(depth=32)|IOPS(depth=64)|IOPS(depth=128)|MB/s(depth=16)|MB/s(depth=32)|MB/s(depth=64)|MB/s(depth=128)|
|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|
|4k|6,804.41|11,286.18|18,278.06|29,976.44|27.87|46.23|74.87|122.78|
|16k|6,155.93|10,096.44|16,278.19|17,876.70|100.86|165.42|266.70|292.89|
|64k|4,533.48|6,108.87|8,159.36|8,967.71|297.11|400.35|534.73|587.71|
|256k|1,855.07|2,137.79|2,226.09|2,445.37|486.30|560.41|583.56|641.04|
|1m|552.32|632.10|626.68|661.50|579.15|662.80|657.12|693.63|
|4m|153.57|155.43|159.01|168.42|644.12|651.94|666.92|706.41|

### 実行結果（読込み）

![storage-bench-iouring-read.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207353/82cdd8f8-6c1f-f7e5-bcd1-01f81b4da9b4.png)


続いて、リードの結果ですリードもライト同様同期IOとは比べ物にならないくらいの性能が出ています。またランダムリードのほうが性能がよいという性能特性については概ねライトのとき同様です。特筆すべきはサチュレーションが見られる点です。
まず帯域が、今回の結果では256KB以上のブロックサイズで発生しています。VMのホストしているサーバの物理IFが10Gbpsですので、おおよそ10Gbps=1.25GB/s程度の値となっています。また、ランダムリードではIOPSについてもサチュレーションしています。おおよそ50 KIOPS強で頭打ちになっていることがわかります。

|Read|IOPS(depth=16)|IOPS(depth=32)|IOPS(depth=64)|IOPS(depth=128)|MB/s(depth=16)|MB/s(depth=32)|MB/s(depth=64)|MB/s(depth=128)|
|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|
|4k|9,148.28|10,155.11|10,440.24|11,452.34|37.47|41.59|42.76|46.91|
|16k|3,418.85|3,949.14|4,284.52|6,371.38|56.01|64.70|70.20|104.39|
|64k|1,255.86|1,641.85|2,474.55|3,821.79|82.30|107.60|162.17|250.46|
|256k|1,613.23|2,458.58|3,552.47|4,257.80|422.90|644.50|931.26|1,116.15|
|1m|801.88|1,072.25|1,108.23|1,109.43|840.83|1,124.34|1,162.06|1,163.32|
|4m|262.30|275.57|275.57|274.09|1,100.14|1,155.80|1,155.80|1,149.62|

|RandRead|IOPS(depth=16)|IOPS(depth=32)|IOPS(depth=64)|IOPS(depth=128)|MB/s(depth=16)|MB/s(depth=32)|MB/s(depth=64)|MB/s(depth=128)|
|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|
|4k|41,069.09|55,234.72|53,466.04|55,060.70|168.22|226.24|219.00|225.53|
|16k|18,864.71|26,892.08|33,694.60|34,656.80|309.08|440.60|552.05|567.82|
|64k|6,855.23|9,041.94|10,107.34|10,132.34|449.26|592.57|662.39|664.03|
|256k|4,043.44|3,831.62|4,235.78|4,362.09|1,059.96|1,004.44|1,110.38|1,143.49|
|1m|1,052.42|1,105.83|1,105.83|1,105.83|1,103.54|1,159.55|1,159.55|1,159.55|
|4m|261.22|270.61|271.47|270.90|1,095.65|1,135.03|1,138.64|1,136.23|


## 評価結果のまとめ

評価１と評価２の内容を考察すると、前回「Cephは遅いはYesでもありNoでもある」と記載した理由がわかっていただけたかと思います。Cephは分散ストレージであり、優れたスケーラビリティを持ちます。
一つの`write`システムコールに対してはお世辞にもあまり速いとは言えませんが、非同期的なIOや、複数のプロセスからの書込みなどに対しては互いに分散してアクセスすることが可能なため、高速に処理することができることがわかります。

CephのIOの特性を知ることは、Cephを正しく理解し、どういったワークロードにCephが向いているのか、あるいはアプリケーションの設定もCephに合わせてどのようなパラメータを設定すべきかといったことを理解することができると思います。


# 第二回（アドベントカレンダー13日目）まとめ

毎度蛇足的に、補足する内容を差し込んだところ、非常に記事が長くなってしまいました・・・（更に記事を分割しても良かったかもしれませんね・・・）。

また、Cephのベンチマーク自体をもっと見たかった、というご意見もあったかもしれません。当初は記載しようとしていた、ベンチマークの観点で記載したシステムアドミニストレータの観点の試験については上記の評価２の`io_uring`による非同期IOを複数のVMや複数のコンテナなどから実行してみれば良いと思います。
また、`ioengine=rbd`というIOエンジンもfioには用意されているため、VMやコンテナなどのオーバヘッドを取り払って、ネイティブマシンからRBDを直接計測することもできます。こういった試験も一緒にご紹介したかったのですが、残念ながら力尽きてしまいました。またどこかで機会があれば記載してみたいと思います。


以上で二回に渡って書かさせていただいた「【ストレージ初心者向け】分散ストレージ、Cephの速度を計る」は終了としたいと思います。
私がCephでストレージに入門したときに躓いたり悩んだりしたポイントを中心に記載しましたが、思い立って記載したこと、締切りに追われて記載したこともあり、理論の飛躍やいや、そこ飛ばすなよみたいなポイントももしかしたらあったかもしれません。
アドベントカレンダーの参加ということもあり、１３日中の投稿はMUSTかなとはおもいますので、一部甘い部分があるかと思いますが、その点はご容赦いただければと思いますしまた必要に応じて加筆訂正させていただこうかと思います。

最後に、Cephには（過去の障害事例とかもあって・・・）拒否反応を示されることも多々ありますが、非常に面白いストレージシステムだと思っています。
ぜひ、みなさんもRook/Cephをつかって、これからも盛り上げていきましょう！