前回記事では、弊社サービス基盤として用いている Kubernetes クラスタ、ならびに Rook/Ceph クラスタのスペックを紹介しました。 今回記事から数回に分けて、実運用中の Rook/Ceph クラスタのストレージパフォーマンスについて実測していきます。

Rook/Ceph は、バージョンにより増減しますが、複数種類のストレージインタフェースを提供しています。今回は Pod からはブロックデバイスとして利用可能な RBD について実測します。

計測方法

Qiita に掲載されている記事 のスクリプトを引用(流用)します。なお、この記事(その2)およびその1に該当する記事は、Rook/Ceph の性能特性が簡潔に纏まっていて参考になります。

上記紹介記事の環境と今回計測環境とは、ハードウェアスペックが同一ではありません。また、次回以降で予定している計測計画の都合上、--filename の引数はブロックデバイスそのものではなく、マウントされたファイルシステム上のファイルを与えています。よって、数値の単純比較はできません。ただし、傾向は類似になることが期待されます。

最近のプログラミング言語では、マルチスレッドまたは非同期 I/O のサポートが一般的です。そこで、シングルスレッドでの I/O 性能は評価から外します。

もろもろ踏まえ、計測に用いるスクリプトは下記のとおりとなります。

# original by: https://qiita.com/tj8000rpm
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
            fio --ioengine=io_uring --numjobs=1 --direct=1 --bs=$BS --rw=$RW \
                     --size=1G --filename=/mnt/rbd/file  --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

厳密を期すならば、複数回計測し誤差を計算する必要があります(弊社からの有償サポート時には必要に応じ統計誤差を提供します)。今回は概算値のみ情報提供という観点から、一回計測でグラフ化します。

RBD の計測結果

今回は測定誤差を無視しているので、数値そのものに拘る理由がありません。グラフのみ掲載します。具体的な数値等にご興味のある方は、弊社までお問い合わせください。

利用したいアプリケーションにより注目点は変わりますが、全部盛りすると要点がボケますので randread と randwrite のみ掲載します。

総評

読み書きともに depth が大きいほど IOPS は大きくなります。これはアプリケーションがマルチスレッドや非同期 I/O を活用すると RBD もスケールし得ることを示唆します。

また、スループットはある程度を上限に飽和していく傾向も見て取れます。

randread

randread

randread では凡そ 1.5Gbps 辺りにスループットの壁がありそうです。このグラフからだけでは断言できませんが、今回はノード VM のネットワークスループットの上限が関連している可能性はありそうです。

randwrite

randwrite

他方 randwrite では一桁少ない辺りにスループットの壁がありそうです。Ceph は同一のデータを複数のストレージ(OSD)に書き込むことで耐障害性を担保しており、デフォルトでは 3 箇所、同期的に書き込みます。そのため、Ceph の書き込み性能は低めに出ます。randread に比してサイズごとの IOPS の変動がばらついているように見えますが、これは計測環境が実運用環境であり、他の Pod も同一クラスタから RBD を振り出している(書き込んでいる)ことが影響している可能性があります。

IOPS にまつわる誤解

読み書きともに、ブロックサイズが大きくなるにつれて IOPS 性能が低下していきます。 この点は、弊社にご相談いただいたお客様から、最もよく尋ねられる点です。

今回ベンチマーク環境としている Azure のみならず、大手クラウドプロバイダはストレージの性能を公表しています。それらの公表値に比べてグラフの結果、特に 4m の値があまりにも心許なく見えます。1〜2 桁低いのではないか、と。

そこで、追加の計測として、Azure の Managed Disk (AWS では EBS が相当します) を CSI ドライバ経由で Pod にマウントし、上記と同様の計測を行いました。Azure では複数のグレードの Disk が存在しますが、今回は AKS のデフォルトの StorageClass である Standard_SSD を用いました。

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: azuredisk-pvc
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 300Gi
  storageClassName: managed-csi

さて、計測結果はこちらとなります。

Standard_SSD での randread

Standard_SSD での randwrite

Standard_SSD の公称性能は、バースト時で最大 600 IOPS、スループット150MB/s です。 公称値に比べて randread の IOPS が高い値になっています。

(20,000 という値は、Standard_SSD より上位のグレードである Premium_SSD で出てくる IOPS 数値です。 これは全くの想像なのですが、Azure 側に余裕がある場合には IOPS をオマケしているのでは…という気もします。)

randwrite のほうの IOPS は早期からカタログスペックに張り付き、ブロックサイズの増加につれ、スループットも最大値になった辺りから減少していきます。

データ量・スループット・IOPS の関係を思い起こして頂ければ明快なのですが、ごく稀なケースを除き、カタログ値での IOPS は実運用時には達成できません。特に大きなコンテンツの転送を要する、ファイルサーバやリポジトリサーバのような用途では顕著なものとなります。

まとめ

Rook/Ceph が提供するストレージのうち、ブロックデバイスに相当する RBD の性能を計測し、Azure Disk のそれと比較しました。

「比較対象として Standard_SSD を選んだのは Ceph 側びいき」という声は聞こえてきそうではあります。高性能なグレードを選んでも構わなかったのですが「Ceph は IOPS が低いので使えない」という誤解の解消のためには、どのグレードを選んでも大差は無いと思っております。

RBD には「ReadWriteOnce な PVC を利用する Pod が、ストレージのゾーン縛りで起動困難になる」という問題を大幅に緩和できる、という大きなメリットがあります。 高可用性を求められ、複数 AZ で K8s クラスタを運用しているチームにとっては、大きな戦力となる可能性を秘めています。

「いや、そうは言っても、うちの Rook/Ceph クラスタは遅い」とお悩みの方がいらっしゃいましたら、ぜひ弊社までお声がけください。いくつかのチューニングポイントを踏まえていなかったゆえに、本来の性能を発揮できずにいた、お客様の Rook/Ceph クラスタに弊社は寄り添ってきました。 本稿では Azure を例に取りましたが、AWS や GoogleCloud 等、各社クラウドにも対応可能です。