手当たり次第に書くんだ

飽きっぽいのは本能

KVM の io_uring はどこで効くのか – QEMU ストレージ I/O と libvirt 設定

KVM / QEMU のストレージ I/O 性能を見る時、ゲスト OS 内のアプリケーションだけを見ていると、重要な層を見落とします。仮想ディスクの I/O は、ゲスト OS、virtio、QEMU、ホスト kernel、ファイルシステム、物理ストレージを通って処理されます。

この記事で見ている io_uring は、ゲスト OS のアプリケーションが使う I/O API ではなく、ホスト側 QEMU が仮想ディスク I/O を処理する時の backend としての io_uring です。fio の ioengine はゲスト側では libaio のまま固定し、libvirt の disk driver に io='io_uring' を指定した場合と、指定しない場合を比較しています。

結論から言えば、この検証環境では 4KB randread / iodepth 32 の条件で IOPS と帯域が約 15% 改善し、p50 から p95 の latency も低下しました。ただし、これは io_uring を入れれば必ず速くなるという意味ではありません。QEMU、kernel、libvirt、ディスク形式、ストレージ backend、I/O パターンによって結果は変わります。

参考
書籍
参考書籍

作って理解する仮想化技術 – ハイパーバイザを実装しながら仕組みを学ぶ

ハイパーバイザー、CPU 仮想化支援、メモリ仮想化、割り込み、仮想デバイスなど、VM の性能設計を低レイヤーから理解したい場合の参考書籍です。価格や在庫はリンク先で確認してください。

Amazon で見る

このリンクは Amazon アソシエイトリンクです。

どの層の io_uring を見ているのか

io_uring は Linux kernel が提供する非同期 I/O の仕組みです。ユーザー空間と kernel 空間の間で submission queue と completion queue を使い、I/O 要求と完了通知を効率よく扱うことを狙います。

ただし、KVM の文脈では、io_uring という言葉が複数の層に出てきます。ゲスト OS のアプリケーションが io_uring を使う場合と、ホスト側 QEMU が仮想ディスク I/O の backend として io_uring を使う場合は、同じ話ではありません。

今回の扱い意味
ゲスト OS 内のアプリケーションfio --ioengine=libaio のまま固定ゲスト側の I/O API は変えない
仮想ディスクqcow2 / virtio を固定ディスク形式や virtio 経路は同じにする
ホスト側 QEMUio='io_uring' の有無を比較QEMU が使う I/O backend を変える
評価対象ゲスト内 fio の結果IOPS、帯域、latency、CPU 使用率を見る

今回の検証は、VM 内のアプリケーションを io_uring 化する話ではありません。QEMU からホスト kernel へ出ていく I/O 経路を変えることで、ゲストから見えるストレージ性能がどう変わるかを見るものです。

libvirt で io_uring を指定する

libvirt の domain XML では、disk の driver 要素に io 属性を指定できます。libvirt のドキュメントでは、QEMU guest の disk driver で threadsnativeio_uring が扱われることが説明されています。

まず、ホスト kernel で io_uring が有効になっているかを確認します。

grep IO_URING /boot/config-$(uname -r)

有効な kernel であれば、次のような出力になります。

CONFIG_IO_URING=y

io_uring を指定しない場合

比較対象として、disk driver に io 属性を付けない構成を用意します。

<driver name='qemu' type='qcow2'/>

io_uring を指定する場合

io_uring を使う場合は、disk driver に io='io_uring' を指定します。

<driver name='qemu' type='qcow2' io='io_uring'/>

実際に使う時は、対象 VM の XML を変更したあと、VM を停止して起動し直します。ディスク設定は稼働中に安全に差し替えられるとは限らないため、検証 VM で手順を確認してから本番 VM に適用します。

fio のテスト条件

テストはゲスト OS 上で fio を実行し、4KB のランダムリードを 60 秒間行いました。ゲスト側の ioenginelibaio のまま固定しています。

sudo fio --name=uringtest \
  --filename=/tmp/test.img \
  --size=4G \
  --rw=randread \
  --bs=4k \
  --iodepth=32 \
  --ioengine=libaio \
  --direct=1 \
  --numjobs=1 \
  --time_based=1 \
  --runtime=60 \
  --group_reporting
オプション意味
--rw=randread4KB ランダムリードを実行する
--bs=4k小さい I/O を高頻度に発行する
--iodepth=32同時に 32 個の I/O を発行する
--ioengine=libaioゲスト側 I/O engine は libaio のまま固定する
--direct=1ゲスト OS のページキャッシュの影響を減らす
--runtime=6060 秒間の time based テストにする

テスト結果

この環境では、io_uring を有効化した場合に IOPS と帯域が約 15% 改善しました。平均 latency だけでなく、p50、p70、p90、p95 も低下しています。

指標io_uring 無効io_uring 有効差分
IOPS8,2139,425+14.7%
帯域32.1 MiB/s36.8 MiB/s+14.6%
平均 latency3.89 ms3.39 ms-0.50 ms
p503,490 us2,933 us-557 us
p706,194 us5,538 us-656 us
p908,455 us7,767 us-688 us
p959,503 us8,717 us-786 us
sys CPU18.29%28.70%+10.41%
disk util99.88%99.90%ほぼ同等

結果の読み方

IOPS と帯域は改善した

今回の条件では、IOPS と帯域はどちらも約 15% 改善しました。4KB randread / iodepth 32 のように小さい I/O を多く発行する条件では、QEMU の I/O backend を io_uring にすることで、ホスト側 I/O 経路の効率が改善した可能性があります。

latency の分布も下がった

平均値だけではなく、p50 から p95 までの latency が下がっている点も重要です。これは一部の極端な値だけではなく、通常時からやや遅い側までの応答性が改善していることを示します。

sys CPU は増えている

一方で、sys CPU は 18.29% から 28.70% に増えています。これは必ずしも悪い結果ではありません。I/O をより多く処理できるようになった結果として kernel 側の CPU 使用率が増え、その代わりに IOPS と latency が改善している可能性があります。

ただし、ホスト CPU に余裕がない場合は、この sys CPU 増加が別の制約になります。io_uring を標準設定にする前に、CPU 使用率、steal、iowait、VM 数、同居ワークロードを合わせて見る必要があります。

io_uring が効きやすい条件

この検証だけで一般化はできませんが、io_uring は小さい I/O が多く、QEMU 側の I/O 経路が効いている VM で効果を期待しやすいと考えられます。

  • 小さいランダム I/O が多い VM
  • qcow2 など QEMU 側の処理を通る構成
  • ホスト CPU に余裕があり、I/O 処理を増やせる構成
  • ストレージ backend が完全には詰まっていない構成
  • p95 / p99 latency を下げたいワークロード

逆に、物理ストレージやネットワークストレージが先に詰まっている場合、大きな sequential I/O が中心の場合、CPU が先に飽和している場合は、同じ効果が出るとは限りません。

標準設定にする前に確認すること

io_uring は有力な選択肢ですが、KVM 環境で無条件に標準化するものではありません。少なくとも次の観点を確認してから適用するべきです。

  • QEMU、libvirt、kernel が io_uring を安定して扱えるバージョンか
  • raw、qcow2、block device、RBD、ZFS など backend ごとの差を測ったか
  • IOPS だけでなく p95 / p99 latency を見たか
  • sys CPU の増加をホストが許容できるか
  • 本番 VM と同じ I/O パターンで測定したか
  • トラブル時に元の disk driver 設定へ戻せる手順があるか

VM パフォーマンス設計の中での位置づけ

io_uring は、KVM のストレージ性能を決める唯一の要素ではありません。virtio、キャッシュ設定、ディスク形式、IOThread、ホスト filesystem、物理ストレージ、NUMA、CPU pinning など、複数の要素が重なって VM の I/O 性能が決まります。

その中で io_uring は、QEMU がホスト側で I/O をどう発行するかに関わる設定です。ゲスト内のアプリケーションを変える技術ではなく、VM の外側にある QEMU の I/O backend を変える設定として扱うと、評価範囲を間違えにくくなります。

まとめ

この検証環境では、QEMU の disk driver に io='io_uring' を指定することで、4KB randread の IOPS と帯域が約 15% 改善し、p50 から p95 の latency も低下しました。

ただし、同時に sys CPU は増えています。io_uring は単純な高速化スイッチではなく、ホスト側 QEMU の I/O 処理方式を変える設計項目です。効果は QEMU、kernel、libvirt、ストレージ backend、ディスク形式、I/O パターンに依存します。

KVM のストレージ I/O を見る時は、ゲスト側の fio 結果だけではなく、QEMU がどの I/O backend を使い、ホスト側でどの資源を消費しているかまで含めて判断する必要があります。

参考情報

参考:

関連する記事

あわせて読みたい:

KVM の io_uring はどこで効くのか – QEMU ストレージ I/O と libvirt 設定

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)

トップへ戻る