VM の storage I/O は、仮想 disk format だけでは決まりません。guest filesystem、virtio device、QEMU block layer、host filesystem、logical volume、network、storage backend を通り、各層の queue と cache が latency と durability に影響します。
この記事では、guest から backend までを同じ時間窓で測定し、virtio-blk / virtio-scsi、raw / qcow2、cache mode、IOThread、discard、passthrough を、性能、データ保護、運用制約の 3 つで判断します。
書籍
CPU 仮想化支援、メモリ仮想化、割り込み、仮想デバイスなど、VM の実行モデルを低レイヤから理解する参考書籍です。価格や在庫はリンク先で確認してください。
Amazon で見るこのリンクは Amazon アソシエイトリンクです。
storage I/O の経路を分ける
| 層 | 主な要素 | 確認するもの |
|---|---|---|
| guest | application、filesystem、block layer | request size、queue、latency、fsync |
| virtual device | virtio-blk、virtio-scsi | queue 数、driver、device feature |
| QEMU | block driver、cache、AIO、IOThread | process / thread、block counter、error |
| host | page cache、filesystem、LVM、ZFS | await、queue、reclaim、thin pool |
| backend | NVMe、RAID、Ceph、SAN、NAS | volume latency、controller、network、rebuild |
guest の disk latency が高くても、原因が guest にあるとは限りません。逆に backend の平均 latency が低くても、QEMU queue、snapshot chain、host cache、同期 write の待ち時間が guest の p99 に現れる場合があります。
症状と測定時間を固定する
調査では、遅い I/O の種類と発生時間を先に決めます。read / write、sequential / random、sync / async、block size、queue depth が違えば、同じ storage でも結果は変わります。
- どの application operation が遅いか
- read、write、flush、discard のどれか
- 平均 latency と p95 / p99 latency はいくつか
- IOPS、throughput、queue depth を同時に記録しているか
- snapshot、backup、migration、rebuild と重なっていないか
- 正常時と同じ workload 条件で比較できるか
counter は累積値を含むため、1 回だけ取得して判断しません。guest、libvirt、host、backend を同じ間隔で 2 回以上取得し、差分と workload の完了量を対応付けます。
guest 側で latency と queue を確認する
lsblk -o NAME,TYPE,SIZE,FSTYPE,MOUNTPOINTS,ROTA,DISC-MAX,DISC-GRAN
iostat -xz 1
vmstat 1
cat /proc/pressure/io
mount
findmnt -o TARGET,SOURCE,FSTYPE,OPTIONS| 所見 | 候補 | 次の確認 |
|---|---|---|
| await と queue が増える | device または下位層の待ち | host と backend の同時 latency |
| utilization が高い | device が busy | 完了 IOPS と latency が SLO 内か |
| I/O PSI が増える | task の I/O stall | どの process と cgroup が待つか |
| write だけ遅い | flush、cache、replication | sync write と backend commit |
| discard 時だけ遅い | unmap の伝搬と backend 処理 | online discard と fstrim の違い |
%util が高いことだけで saturation と断定しません。並列処理できる device や storage array では、queue、latency、throughput、error を一緒に見ます。
libvirt から disk と counter を対応付ける
最初に domain の target device と source path を確認し、counter を対象 disk へ対応付けます。domblkstat と domstats で QEMU が公開する block 統計を取得し、XML で cache、I/O backend、discard、IOThread を確認します。
virsh domblklist vm01 --details
virsh domblkinfo vm01 vda
virsh domblkstat vm01 vda --human
virsh domstats vm01 --block --iothread
virsh dumpxml vm01
virsh dumpxml vm01 --inactive
qemu-img info --backing-chain /var/lib/libvirt/images/vm01.qcow2qemu-img info の path は domblklist で確認します。active image を扱う操作は、read-only の情報取得と image を変更する操作を区別し、snapshot、commit、rebase、check などは maintenance 手順に分けます。
host と backend の同時負荷を確認する
iostat -xz 1
pidstat -d -p 12345 1
cat /proc/pressure/io
lsblk -o NAME,TYPE,SIZE,MODEL,ROTA,SCHED,DISC-MAX,DISC-GRAN
dmsetup status
lvs -o lv_name,vg_name,lv_size,data_percent,metadata_percentQEMU の process ID は対象 VM と照合した値を使います。host device の latency が低いまま QEMU thread の待ちが増える場合と、同じ backend を使う複数 VM が同時に遅くなる場合では、優先して調べる層が異なります。
| 発生範囲 | 優先する調査 |
|---|---|
| 1 application だけ | fsync、lock、database log、filesystem |
| 1 VM の 1 disk | virtual device、image、IOThread、snapshot chain |
| 1 VM の全 disk | QEMU、host CPU / memory、共通 controller |
| 同じ host の複数 VM | host device、thin pool、page cache、backup |
| 複数 host の同じ volume 群 | storage network、array、Ceph、SAN / NAS |
virtio-blk と virtio-scsi を用途で選ぶ
| 方式 | 特徴 | 確認すること |
|---|---|---|
| virtio-blk | block device を直接提示する単純な構成 | disk 数、queue、hotplug、guest driver |
| virtio-scsi | SCSI controller 配下へ複数 device を接続する | controller、SCSI feature、queue、hotplug |
virtio device は paravirtualized device であり、一般的な VM では標準的な選択です。ただし、virtio-blk と virtio-scsi のどちらが速いかを形式名だけで決めません。guest kernel、queue 数、IOThread、block size、backend の並列性をそろえて測定します。
raw と qcow2 の差を image chain まで見る
| 形式 | 得られるもの | 注意点 |
|---|---|---|
| raw | 単純な block address と読みやすい経路 | sparse file、filesystem、thin volume の層は別に存在する |
| qcow2 | backing file、snapshot、thin allocation など | metadata、allocation、fragmentation、chain depth |
raw file でも host filesystem や thin provisioning を使えば、allocation と snapshot の層は残ります。qcow2 でも workload と cache によって必要性能を満たせます。format だけでなく source type、backing chain、cluster allocation、snapshot 数、backend を記録します。
- backing chain が意図せず深くなっていないか
- internal snapshot と external snapshot の運用を混同していないか
- backup 後の temporary overlay が残っていないか
- virtual size と host の実使用量を分けているか
- thin pool の data と metadata に余力があるか
- image の変換や compact を active VM に対して実行していないか
cache mode は durability 契約として決める
cache mode は benchmark の速さだけでなく、guest の flush がどこまで伝わり、host や backend の障害でどの write を失う可能性があるかを決めます。guest filesystem、QEMU、host cache、storage controller の各層が flush と write ordering を守る必要があります。
| mode | 経路の特徴 | 検証すること |
|---|---|---|
| none | host page cache を避ける構成で使われる | direct I/O の要件と backend の alignment |
| directsync | direct I/O と同期 write を組み合わせる | latency と durability の要件 |
| writethrough | write 完了前の永続化を重視する | backend までの flush と性能 |
| writeback | cache を利用して完了を早く返せる | guest flush、power loss protection、障害試験 |
storage に battery-backed cache や power loss protection があるか、network storage が write を何段階で acknowledge するかも確認します。mode 名だけでデータ保護を保証せず、guest の fsync、QEMU の flush、backend commit を一つの経路として検証します。
AIO と IOThread の役割を分ける
libvirt の disk driver では、環境に応じて threads、native、io_uring などの I/O backend を選択できます。IOThread は disk I/O 処理を QEMU main loop から分けるための thread であり、AIO backend の名前とは別の設計要素です。
<iothreads>2</iothreads>
<disk type='file' device='disk'>
<driver name='qemu' type='qcow2' cache='none' io='io_uring' discard='unmap' iothread='1'/>
<source file='/var/lib/libvirt/images/vm01.qcow2'/>
<target dev='vda' bus='virtio'/>
<iotune>
<total_iops_sec>5000</total_iops_sec>
</iotune>
</disk>この XML は要素の関係を示す例です。io_uring、IOThread assignment、queue、iotune の対応可否は libvirt、QEMU、disk bus、source type によって異なります。現在の version と生成された QEMU command line を確認し、未対応の組み合わせを前提にしません。
I/O limit は noisy neighbor を抑える一方で、上限自体が latency の原因になります。iotune を設定している場合は、backend saturation と rate limit のどちらで待っているかを分けます。
discard は guest から backend まで追う
discard は guest で不要になった block を下位層へ伝える仕組みです。libvirt の discard='unmap' は要求を下位へ渡す指定ですが、guest filesystem、virtual device、image format、host filesystem、thin pool、storage backend の全層が対応する必要があります。
| 方式 | 利点 | 注意点 |
|---|---|---|
| online discard | 解放時に随時伝える | workload 中の latency と backend 負荷 |
| 定期 fstrim | 実行時間を制御しやすい | 一度に大きな unmap が発生する可能性 |
| discard 無効 | 実行時の変動を避けやすい | thin allocation を回収できない |
detect_zeroes は zero write の検出に関係し、unmap との組み合わせでは capacity 回収に使える場合があります。一方で検出処理には CPU cost があるため、要件と実測で判断します。
passthrough の性能と運用制約を交換する
NVMe や storage controller を PCI Passthrough で VM に渡すと、QEMU block layer や host filesystem を大きく迂回できます。その代わり、device の共有、snapshot、host 側監視、live migration、backup、障害交換の方法が変わります。
- IOMMU group と device reset の対応
- VM 停止中と障害時の device ownership
- snapshot と backup をどの層で行うか
- migration できない場合の maintenance 手順
- SMART、firmware、temperature を誰が監視するか
- 交換用 device と復旧時間を確保しているか
性能試験と障害試験を分けない
| 試験 | 確認するもの |
|---|---|
| baseline | IOPS、throughput、p50 / p95 / p99、CPU、queue |
| sync write | fsync latency、flush、backend commit |
| mixed workload | read / write 比率と queue depth が本番に近いか |
| snapshot / backup | 通常 workload への latency 影響 |
| capacity pressure | thin pool、qcow2、filesystem の残量 |
| failure / recovery | host、backend、network 障害後の整合性と再開時間 |
benchmark は専用の test volume と再現可能な dataset で行います。本番 filesystem や active image に対して destructive な write test を実行しません。変更前の XML と測定値を保存し、1 変更ごとに同じ workload で比較します。
よくある誤判断
| 誤判断 | 見落とすこと | 確認方法 |
|---|---|---|
| qcow2 だから遅い | chain、allocation、cache、backend | 経路ごとの latency を測る |
| writeback だから速くてよい | flush と power loss 時の durability | 障害と recovery を試す |
| device utilization 100% だから限界 | 並列 queue と完了 throughput | latency、queue、IOPS を一緒に見る |
| discard を有効にすれば回収できる | 途中の非対応層 | guest から backend まで追う |
| io_uring にすれば必ず速い | version、source type、workload | 同条件で AIO backend を比較する |
| passthrough なら問題が消える | backup、migration、monitoring | 運用 lifecycle を評価する |
まとめ
VM の storage I/O は、virtio、QEMU、image format、cache、host、backend の連続した経路です。raw / qcow2 や virtio-blk / virtio-scsi の名前だけで判断せず、同じ時間窓で latency、queue、throughput、error を対応付けます。
cache mode はデータ保護、discard は capacity 回収、IOThread と AIO は処理場所、passthrough は運用制約に関係します。性能試験と障害復旧を一組にし、変更を 1 つずつ比較することが、安全な storage I/O 設計の要点です。

