VirtIOドライバーとQEMUチューニングによるKVM仮想マシンのパフォーマンス最適化:Linuxゲストの高速化技術

Virtualization tutorial - IT technology blog
Virtualization tutorial - IT technology blog

背景と必要性:仮想マシンを「もっさり」させないために

KVMをいじってホームラボを構築したり、Proxmox VE上で重要なLinux仮想マシンを多数管理していますか?もしそうなら、仮想マシン(VM)が物理マシンと比べて著しく遅いと感じたことがあるでしょう。私も同じです。現在、本番環境に投入する前のテスト環境として、12台のVMとコンテナを運用しています。この「もっさり」とした状態は、ディスクI/O速度やネットワークパフォーマンスで最も顕著に現れます。

KVM (Kernel-based Virtual Machine)について少し説明すると、これはLinuxカーネルをハイパーバイザーに変える強力な仮想化ソリューションです。KVMはQEMUを使用してハードウェアをエミュレートします。しかし、このエミュレーション自体がパフォーマンスに影響を与える可能性があります。想像してみてください。仮想マシンがディスクにアクセスしたり、ネットワーク経由でデータを送受信したりするたびに、ハイパーバイザー(QEMU)に「許可を求め」、そのアクションが実行されるのを待たなければなりません。これは無視できないほどの遅延につながります。

では、どうすれば仮想マシンを物理マシンとほぼ同じくらい速く、スムーズに動作させることができるのでしょうか?主な解決策は、VirtIOドライバーといくつかのQEMUチューニング技術です。VirtIOは仮想化ドライバーセットであり、ゲストOS(ゲストオペレーティングシステム)がQEMUの不要なハードウェアエミュレーション層をバイパスして、ハイパーバイザーと直接かつ効率的に通信できるようにします。これは、曲がりくねった田舎道を通る代わりに、専用の高速道路を利用するようなものです。

VirtIOドライバーのインストール:パフォーマンスの基盤

良いニュースは、VirtIOドライバーが最新のほとんどのLinuxディストリビューション(Ubuntu、CentOS、Debian、Fedoraなど)のカーネルにプリインストールされていることです。これは、新しいLinuxオペレーティングシステムを仮想マシンとしてインストールする場合、ハイパーバイザーがサポートしていれば、ほぼ確実に自動的にVirtIOを認識し、使用することを意味します。

VirtIOが使用されているか確認する

確認のため、Linux仮想マシンにロードされているVirtIOカーネルモジュールを次のコマンドで確認できます。


lsmod | grep virtio

virtio_blk(ディスク用)、virtio_net(ネットワーク用)、virtio_pcivirtio_consoleなどのモジュールが表示されるはずです。このリストが表示されていれば、VirtIOは正しく機能しています。

ディスクデバイスの確認:


lsblk -o NAME,DISC-GRAN,DISC-MAX,DISC-ZERO,MOUNTPOINT,FSTYPE,MODEL,VENDOR

この場合、ディスク名はsd(SATA/SCSI)やhd(IDE)ではなく、vd(例:vdavdb)で始まります。これはVirtIOブロックディスクを使用していることを示す兆候です。

ネットワークカードの確認:


ip a

VirtIOネットワークカードは通常、ethまたはenpで始まる名前を持っています。さらに重要なのは、QEMUによってVirtIOとして設定されている必要があることです。virt-managerまたはProxmox VEを介してVMを作成する際は、ディスクとネットワークデバイスの両方で常に`VirtIO`を選択してください。

既存の仮想マシン(レガシーデバイス)の場合

古い仮想マシンでIDE/SATA/E1000ドライバーを使用している場合は、最大限のパフォーマンスを引き出すためにVirtIOに変換することをお勧めします。このプロセスは、特にシステムディスクの場合、慎重な対応が必要です。ディスクの種類を変更する前に、VirtIOドライバーをinitramfs/initrdに追加して、システムが起動できるようにする必要があります。

ステップ1:initramfsをインストールおよび更新する(VM内)


# Ubuntu/Debian
sudo apt update
sudo apt install linux-image-extra-$(uname -r)
sudo update-initramfs -u -k all

# CentOS/RHEL/Fedora
sudo yum update
sudo dracut -f -v

ステップ2:仮想マシンをシャットダウンし、デバイスタイプを変更する(ホスト上またはGUI経由で)

virshを使用する場合(ホスト上):


virsh edit <仮想マシン名>

ディスクとネットワークカードの設定セクションを見つけます。device='disk' / type='network'からdriver='qemu'model='virtio'を使用するように次のように変更します。


<disk type='file' device='disk'>
  <driver name='qemu' type='qcow2' cache='none' io='native'/>
  <source file='/var/lib/libvirt/images/yourvm.qcow2'/>
  <target dev='vda' bus='virtio'/>
</disk>

<interface type='bridge'>
  <mac address='52:54:00:xx:xx:xx'/>
  <source bridge='br0'/>
  <model type='virtio'/>
</interface>

保存後、VMを再起動します。仮想マシンが正常に起動すれば、おめでとうございます!

詳細設定:KVMを安全に「オーバークロック」する

VirtIOは重要な一歩ですが、最高のパフォーマンスを実現するためには、さらに深いチューニングを行う必要があります。

1. VirtIOブロック(ディスクI/O)の最適化

ディスクI/Oパフォーマンスは、多くの場合、最大の「ボトルネック」です。VirtIOを使用すると、パフォーマンスを向上させるために以下のQEMUオプションを追加で設定できます。

  • キャッシュモード:cache='none'およびio='native'
    これは、本番環境とホームラボの両方で最も効果的だと私が感じている設定です。これにより、ゲストOSがディスクキャッシュを直接管理できるようになり、「ダブルキャッシング」現象を回避し、オーバーヘッドを最小限に抑えます。同時に、`io=’native’`オプションはLinux AIO(非同期I/O)を使用し、I/O操作が互いにブロックしないことを保証します。
    VMのXMLでの設定(`virsh edit`またはProxmoxインターフェース):

    
    <driver name='qemu' type='qcow2' cache='none' io='native'/>
        
  • Discard/TRIMのサポート:
    この機能により、ゲストOSは使用されなくなったデータブロックをホストに通知できます。これにより、ホストはストレージスペースを解放し、特にSSDにおいて時間の経過とともにディスクのパフォーマンスを維持するのに役立ちます。
    XMLのディスク設定セクションに追加:

    
    <driver name='qemu' type='qcow2' cache='none' io='native'>
      <discard enable='yes'/>
    </driver>
        

    ゲストOS内では、discardオプションを使用してファイルシステムをマウントするか、定期的にfstrimを実行する必要があります。

    
    # fstabを確認
    cat /etc/fstab
    # 通常、discardオプションまたはfstrim.timer(systemd)が使用されていることがわかります。
        

2. VirtIOネットワーク(ネットワークI/O)の最適化

  • オフロード機能:
    TSO (TCP Segmentation Offload)、GSO (Generic Segmentation Offload)、LRO (Large Receive Offload)などの機能により、仮想ネットワークカードは通常CPUが実行する必要があるいくつかのタスクを処理できます。これにより、CPUの負荷が軽減され、ネットワークスループットが向上します。
    ゲストOSで確認するには、次のコマンドを使用します。

    
    ethtool -k <interface_name>
        

    tx-checksummingtcp-segmentation-offloadgeneric-segmentation-offloadgeneric-receive-offloadの各オプションが有効になっていること([fixed]またはon)を確認してください。もし有効になっていない場合、VirtIOは通常自動的に処理しますが、試して有効にすることもできます。

    
    sudo ethtool -K <interface_name> tso on gso on gro on
        
  • マルチキューネットワーク:
    複数のCPUコアを持つVMの場合、VirtIOネットワークカードに複数のキューを設定すると、ネットワークI/O処理の負荷を複数のコアに分散できます。これにより、高負荷なネットワークタスクのパフォーマンスが大幅に向上します。
    VMのXMLでの設定:

    
    <interface type='bridge'>
      ...
      <model type='virtio'/>
      <driver name='vhost' queues='4'/> <!-- vCPUの数と同じか少ないキュー数 -->
    </interface>
        

    その後、ゲストOS内で、各キューにrps_cpusを設定して特定のCPUに割り当てる必要があります。スクリプトファイルを作成し、systemdサービスに追加して起動時に実行するようにします。

    
    #!/bin/bash
    
    INTERFACE="eth0"
    NUM_QUEUES=4 # VMのXMLで設定したキューの数
    
    # ネットワークカードのRSSキューへのパスを取得
    QUEUE_PATH="/sys/class/net/$INTERFACE/queues/rx-0/rps_cpus"
    
    # 各キューをCPUコアに割り当てる(例:最初の4コアに4つのキュー)
    # コア番号をビットマスクに変換:0001, 0010, 0100, 1000...
    
    for i in $(seq 0 $((NUM_QUEUES-1))); do
      CPU_MASK=$(printf "%%x" $((1<<i)))
      echo $CPU_MASK > "/sys/class/net/$INTERFACE/queues/rx-$i/rps_cpus"
      echo $CPU_MASK > "/sys/class/net/$INTERFACE/queues/tx-$i/xps_cpus"
    done
    
    # 必要に応じて、Receive Packet Steering (RPS)とReceive Flow Steering (RFS)を有効にする
    echo 1 > /proc/sys/net/core/rps_sock_flow_entries
        

3. VirtIOバルーン(メモリ管理)

バルーニングにより、ハイパーバイザーはゲストOSからメモリを柔軟に要求または解放できます。この機能は、ホスト上でRAMをより効率的に利用するのに役立ちます。

VMに割り当てられたRAMを使い切っていない場合、バルーンドライバーはそのメモリを「縮小」し、他のVMのためにRAMを解放します。
VMのXMLに追加:


<memorybacking>
  <balloon model='virtio'/>
</memorybacking&gt>

注意:バルーニングは便利ですが、ホストがRAMを過剰にプロビジョニングしすぎると、パフォーマンスが不安定になる可能性があります。高性能で安定した環境で使用する場合は、慎重に検討してください。

4. VirtIO RNG (乱数生成器)

高品質なエントロピーソースは、暗号化やセキュリティアプリケーション(SSH、SSL/TLSなど)にとって非常に重要です。仮想マシンはエントロピー不足の問題を抱えがちで、これらのタスクを遅らせることがあります。VirtIO RNGは、ゲストOSがホストハイパーバイザーから直接エントロピーを取得する方法を提供します。
VMのXMLに追加:


<rng model='virtio'>
  <backend model='random'/> <!-- ホストに応じて、またはmodel='egd' -->
</rng>

ゲストOS内では、このエントロピーソースを使用するためにqemu-kvm-evまたはrng-toolsをインストールしてください。


# Ubuntu/Debian
sudo apt install rng-tools
sudo systemctl enable rngd
sudo systemctl start rngd

# CentOS/RHEL/Fedora
sudo yum install rng-tools
sudo systemctl enable rngd
sudo systemctl start rngd

# エントロピーを確認
cat /proc/sys/kernel/random/entropy_avail
# rngd実行後、この数値は高くなるはずです(1000以上)。

5. QEMU CPU Tuning

  • CPUパススルー(host-modelまたはhost-passthrough):
    QEMUが一般的なCPUをエミュレートする代わりに、ホストのCPU情報をゲストに直接渡すことができます。これにより、ゲストOSは物理CPUのすべての機能(AVX、AES-NIなどの最適化された命令セットを含む)を認識し、使用できるようになります。QEMUではこれらの機能を完全にエミュレートできない場合があります。
    同様のCPUを持つホスト間でVMをライブマイグレーションしたい場合はhost-modelを使用します。特定のホスト上でVMを実行し、最高のパフォーマンスが必要な場合はhost-passthroughを使用します。
    VMのXMLでの設定:

    
    <cpu mode='host-model' check='partial'/> <!-- またはmode='host-passthrough' -->
        
  • Huge Pages:
    Linuxカーネルは通常4KBサイズのメモリページを使用します。Huge Pagesはより大きなメモリページ(2MBまたは1GB)であり、CPUのTranslation Lookaside Buffer(TLB)の負荷を軽減します。これにより、「メモリを大量に消費する」アプリケーションのパフォーマンスが向上します。データベースサーバーや重いアプリケーションの場合、Huge Pagesは大きな違いを生み出す可能性があります。
    ホスト上(例:16GB RAMがあり、Huge Pages 2MBを使用してVMに4GBを割り当てたい場合):

    
    sudo sysctl vm.nr_hugepages=$((4096 * 1024 / 2048))
    # 値はhuge pagesの数です。4096MB / 2MB per page = 2048 pages。
    # 永続的な設定のため/etc/sysctl.confに追加します。
    
    sudo mkdir -p /dev/hugepages
    sudo mount -t hugetlbfs none /dev/hugepages
    # 自動マウントのため/etc/fstabに追加します。
        

    VMのXML内:

    
    <memorybacking>
      <hugepages/
    </memorybacking>
        

確認と監視:常に現状を把握する

調整後、変更が効果的かどうかを測定することが重要です。感覚だけに頼らず、ベンチマークツールを使用してください。

ディスクI/Oパフォーマンスの測定

私がよく使うツールはfioです。非常に柔軟で、さまざまな種類のI/O負荷をシミュレートできます。ゲストVM内にインストールして実行します。


# Ubuntu/Debian
sudo apt install fio

# CentOS/RHEL/Fedora
sudo yum install fio

# 4KBランダムリードのテスト例
sudo fio --randrepeat=1 --ioengine=libaio --direct=1 --gtod_reduce=1 --name=test --filename=testfile.img --bs=4k --iodepth=64 --size=1G --readwrite=randread --rwmixread=75 --numjobs=4 --runtime=60 --group_reporting

最適化前後のiopsbandwidthの結果を比較してください。驚くべき違いが見られるはずです!

ネットワークパフォーマンスの測定

iperf3は、2点間のネットワーク帯域幅をテストするための優れたツールです。iperf3サーバー(KVMホストまたは別のVM)とiperf3クライアント(テスト中のVM)が必要です。


# サーバーとクライアントの両方にインストール
sudo apt install iperf3 # Ubuntu/Debian
sudo yum install iperf3 # CentOS/RHEL/Fedora

# サーバー上(サーバーモード):
iperf3 -s

# 仮想マシン上(クライアントモード、<server_ip>をサーバーのIPに置き換える):
iperf3 -c <server_ip> -P 4 -t 30 # -Pはスレッド数、-tは時間

監視

htopnmonsariostatmpstatなどのツールは、リアルタイムまたは履歴データでリソース(CPU、RAM、ディスクI/O、ネットワークI/O)を監視するのに役立ちます。htopまたはmpstat%wa(I/O待機)に注意してください。これが高い場合、I/Oのボトルネックの兆候です。iostatでディスクのutilizationをチェックして、ディスクが過負荷になっていないか確認してください。

最後に

KVM仮想マシンのパフォーマンス最適化は、一度行ったら終わりという作業ではなく、継続的なプロセスです。これには、コンポーネントがどのように相互作用するかについての理解と、測定および調整能力が必要です。私が共有したVirtIOドライバーとQEMUチューニング技術を使用すれば、Linux KVM仮想マシンの潜在能力を最大限に引き出すための強力なツールを手に入れることができます。

ホームラボから小規模な本番環境まで、これらのヒントを適切に適用することで、システムははるかにスムーズに、迅速に、安定して動作するようになります。ITの旅路において、どんな小さな最適化も堅牢で効率的なシステムの構築に貢献することを忘れないでください!

Share: