VM の性能低下は、guest OS の CPU 使用率だけでは特定できません。guest、QEMU、host kernel、storage backend、network datapath が重なり、ある層の待ち時間が別の層の高負荷に見えることがあります。
この記事では、CPU、メモリ、storage I/O、network I/O の症状を入口に、どの層で仕事が進まず待たされているのかを切り分けます。最初に観測時間をそろえ、次に guest と host を対応付け、最後に変更を 1 つずつ試します。
書籍
CPU 仮想化支援、メモリ仮想化、割り込み、仮想デバイスなど、VM の実行モデルを低レイヤから理解する参考書籍です。価格や在庫はリンク先で確認してください。
Amazon で見るこのリンクは Amazon アソシエイトリンクです。
ボトルネックは利用率ではなく待ち時間で見分ける
utilization が高い資源は候補ですが、高いだけで原因とは限りません。CPU が高くても処理量が伸びていれば正常な場合があり、CPU が低くても memory や I/O を待って仕事が進んでいない場合があります。
| 指標 | 意味 | 注意点 |
|---|---|---|
| utilization | 資源が使われた割合 | 高いことと、待ち時間の原因であることは同じではない |
| queue | 処理を待つ仕事の量 | 短い spike は平均値に埋もれる |
| latency | 完了までに要した時間 | 平均値だけでなく p95 / p99 を見る |
| throughput | 単位時間に完了した仕事 | latency と同時に比較する |
| pressure | 資源不足で task が止まった時間 | CPU、memory、I/O の PSI を分ける |
| error / drop | 処理できなかった仕事 | 再送や retry が負荷を増やす場合がある |
Linux の Pressure Stall Information は、CPU、memory、I/O の競合で task が止まった時間を /proc/pressure/ から確認できます。単純な使用率とは違い、資源不足が workload の進行を妨げたかを見る指標です。
最初に症状と観測時間を固定する
調査前に、遅い操作、発生時刻、継続時間、対象 VM、比較対象、期待値を記録します。guest と host の統計を別の時刻に取ると、同じ事象を比較できません。時刻同期を確認し、同じ負荷区間で採取します。
- どの request、batch、packet flow、disk operation が遅いか
- 常時遅いのか、peak 時だけ遅いのか
- 単一 VM だけか、同じ host の複数 VM に出るか
- 変更前の正常時と比べて何が増えたか
- p95 / p99 latency、throughput、error rate の期待値はいくつか
- migration、snapshot、backup、deploy と発生時刻が重なっていないか
guest 側で症状の入口を確認する
guest では workload に近い指標から確認します。CPU、run queue、memory、swap、block device、interface、PSI を同じ時間帯で採取し、どの資源の待ちが増えたかを見ます。
uptime
vmstat 1
mpstat -P ALL 1
iostat -xz 1
free -h
ip -s link
cat /proc/pressure/cpu
cat /proc/pressure/memory
cat /proc/pressure/io| guest の所見 | 次に疑うもの | まだ断定できない理由 |
|---|---|---|
| run queue が長い | vCPU 不足、steal、CPU quota | host の pCPU 競合でも同じ症状になる |
| swap / major fault が増える | guest memory 不足 | ballooning や host memory pressure も確認が必要 |
| disk await が増える | 仮想 disk または backend | QEMU queue、host filesystem、storage 装置を通る |
| network drop が増える | guest queue、vhost、bridge、NIC | drop した層を interface ごとに追う必要がある |
| PSI が増える | 該当資源の競合 | system 全体か対象 process / cgroup かを分ける |
VM と host の対応を確認する
guest の所見を host の VM 統計へ対応付けます。libvirt の domstats は domain の CPU、vCPU、memory、interface、block、I/O thread などをまとめて取得できますが、hypervisor が提供しない field は表示されません。
virsh domstats vm01 --state --cpu-total --balloon --vcpu --interface --block --iothread --memory
virsh vcpuinfo vm01
virsh vcpupin vm01
virsh emulatorpin vm01
virsh numatune vm01
virsh domblklist vm01 --details
virsh domiflist vm01
virsh dumpxml vm01domstats の counter は累積値を含むため、1 回の値だけで判断しません。同じ間隔で 2 回以上取得して差分を計算し、guest の負荷区間と対応付けます。disk target と interface target は domblklist と domiflist で確認します。
CPU は busy、steal、run queue を分ける
| 症状 | guest で見るもの | host で見るもの |
|---|---|---|
| vCPU が常に busy | user / system、thread 数、throughput | pCPU 使用率、vCPU thread、CPU affinity |
| CPU は低いが遅い | I/O wait、PSI、lock wait | storage / network latency、QEMU thread |
| steal が増える | steal と p99 latency | CPU overcommit、同居 VM、run queue |
| 一部 vCPU だけ高い | single thread、IRQ、queue | pinning、SMT sibling、NUMA node |
| 負荷時だけ揺らぐ | run queue と tail latency | co-tenant、IRQ、emulator thread |
vCPU を増やす前に、workload が並列化できるか、host に物理 core の余力があるか、NUMA node をまたがないかを確認します。vCPU の追加は、CPU 競合や lock contention を増やす場合があります。
メモリは容量、配置、回収を分ける
空き memory が少ないだけでは不足とは限りません。guest の swap、major fault、memory PSI に加え、host の NUMA remote access、ballooning、KSM、HugePages、page reclaim、OOM を確認します。
| 層 | 確認すること |
|---|---|
| guest | available memory、swap in / out、major fault、memory PSI |
| libvirt | balloon current / maximum、NUMA policy、memory backing |
| host | node ごとの空き memory、NUMA miss、reclaim、swap、OOM |
| HugePages | page size、node ごとの予約量、free pages、起動失敗 |
| workload | cache、heap、working set、GC pause |
CPU を node 0 に固定し、guest memory が node 1 に多く配置されている場合は、容量に余裕があっても remote access が増えます。CPU affinity と memory policy を同じ topology 上で確認します。
ストレージ I/O は経路ごとに latency を追う
guest filesystem から physical storage までには、仮想 block device、QEMU、host page cache、host filesystem、logical volume、network storage、storage controller などがあります。guest の await だけで disk 本体を原因と断定しません。
| 層 | 主な確認項目 |
|---|---|
| guest | queue depth、await、utilization、filesystem、sync write |
| QEMU / libvirt | block counter、I/O thread、cache mode、discard |
| host | device await、queue、page cache、filesystem、thin pool |
| backend | volume latency、controller queue、network latency、snapshot |
| operation | backup、migration、trim、scrub、rebuild との競合 |
raw と qcow2、virtio-blk と virtio-scsi、cache mode、io_uring は、機能と経路を変える設定です。測定前に方式を変更すると、原因調査と tuning が混ざります。まず現在の経路で遅延が増えた層を特定します。
ネットワーク I/O は drop した場所を追う
network throughput が伸びない場合は、guest virtio-net、tap、vhost、bridge または Open vSwitch、physical NIC の順に packet と drop を追います。TCP retransmission が増えている場合は、CPU 不足や queue drop が network latency として見えている可能性もあります。
ip -s link
ethtool -S enp1s0
ss -s
cat /proc/net/softnet_stat
cat /proc/interrupts
virsh domifstat vm01 vnet0- guest と host の interface counter を同じ時間窓で比較する
- RX / TX queue 数と vCPU 数の対応を確認する
- softirq が特定 CPU に偏っていないか確認する
- MTU、offload、multiqueue、RSS の前提を記録する
- bridge、firewall、tunnel、software switch の処理を確認する
- SR-IOV や passthrough では host から見えない範囲を明記する
複数 VM に出るかで故障範囲を絞る
| 発生範囲 | 優先して確認する層 |
|---|---|
| 1 process だけ | application、lock、GC、connection pool |
| 1 VM 全体 | guest kernel、仮想 device、VM 設定、QEMU thread |
| 同じ host の複数 VM | pCPU、NUMA memory、host I/O、NIC、同居 workload |
| 同じ storage の複数 host | storage network、volume、controller、backend operation |
| 同じ network path の複数 VM | switch、firewall、tunnel、uplink、physical NIC |
| 基盤全体 | 共有 service、capacity、変更作業、外部 dependency |
故障範囲を広げる比較は、個別 VM の設定変更より先に実施できます。正常な VM、別 host、別 storage、別 network path と比較すると、調査対象の層を減らせます。
変更は 1 つずつ同じ負荷で比較する
| 段階 | 実施すること |
|---|---|
| 1 | 症状、SLO、発生時刻、対象範囲を記録する |
| 2 | guest、libvirt、host、backend を同じ時間窓で測定する |
| 3 | 利用率、queue、latency、throughput、pressure を対応付ける |
| 4 | 原因候補と、反証できる指標を 1 組にする |
| 5 | 設定を 1 つだけ変更し、同じ workload で再測定する |
| 6 | 改善しない場合は戻し、次の仮説を試す |
CPU pinning、HugePages、cache mode、multiqueue、SR-IOV、DPDK を同時に適用すると、どの変更が効いたのか分かりません。変更前の XML と測定値を保存し、成功条件とロールバック条件を先に決めます。
よくある誤判断
| 誤判断 | 見落とすこと | 確認方法 |
|---|---|---|
| CPU 100% だから vCPU を増やす | 並列性、steal、lock、host capacity | throughput と run queue を同時に見る |
| free memory が少ないから不足 | page cache と reclaim | available、swap、fault、PSI を見る |
| guest の await が高いから disk 故障 | QEMU と backend の queue | 経路ごとの latency を比較する |
| network が遅いから NIC を交換 | softirq、queue、drop、retransmission | interface ごとの counter を追う |
| 平均値が正常だから問題なし | 短い stall と tail latency | p95 / p99 と PSI total の差分を見る |
| tuning を全部入れて比較 | 因果関係と rollback | 1 変更 1 測定を守る |
まとめ
VM の性能ボトルネックは、guest OS の中だけでは判断できません。症状と時間窓を固定し、guest、QEMU、host、storage、network の counter と latency を対応付けることで、待ち時間が発生した層を絞れます。
利用率だけでなく queue、latency、throughput、PSI、error / drop を一緒に見ます。その後で変更を 1 つだけ適用し、同じ workload で再測定します。この順序を守ると、高速化設定を増やす前に、本当に制約になっている資源を特定できます。

