HalNiの雑記

備忘録を兼ねて。サーバマシンとかネットワークとか

KVM 環境で virtio-mem を使用する ( openSUSE Leap 15.4 )

の記事はopenSUSE Advent Calendar 2022 8日目の記事です。

virtio-mem とは

virtio-memとは、ゲスト(仮想マシン)のメモリ提供手段の一つです。

Overview

A virtio-mem device manages a memory region in the the guest physical address space of a virtual machine and provides a dynamic amount of memory via this memory region to the virtual machine.

https://virtio-mem.gitlab.io/user-guide/

従来のメモリ管理の方法として virtio balloon memory がありましたが、virtio-memには ゲストに割り当てたメモリを、ゲストの起動中に縮小できる という特徴があります。

なお現在 Tech-preview扱いのため、本番環境での使用は推奨されていません。念のため。

動作要件

ネスト環境で virtio-mem は正常に動作しないため、L2ゲストに virtio-mem は使用できません。
またメモリを動的に縮小・完全に取り外し可能であるというvirtio-memの性質上、virtio-mem とは別に初期メモリの領域をゲストに割り当てる必要があります。

virtio-mem は新しい実装のため、qemu5.1.0-rc1 以降、libvirtv7.9.0-rc1 以降の環境が必要です。
注意点として、ゲストOSはLinuxのみ、かつ kernel 5.8-rc1 以降のみ対応しています。 また対応アーキテクチャは現時点で x64・aarch64のみです。

メジャーどころでは openSUSE Leap 15.4 や Fedora 36/37、RHEL 9.0/9.1 が上記の環境を備えています。

今回は openSUSE Leap 15.4 で構築したKVMホスト上で検証を行います。

検証環境:

GIGABYTE MZ32-AR0
- AMD EPYC 7401P / DDR4 256GB / PM1725(HHHL) 3.2TB

openSUSE Leap 15.4
- kernel : 5.14.21-150400.24.11
- qemu : 6.2.0-150400.37.5.3
- libvirt : 8.0.0-150400.5.8

現在の手元環境たち。機会があればそのうちまとめたい。

virtio-mem を使用するゲストを新規に作成する

virtio-memを使用するゲストを作成する場合、virt-install を使用することでデプロイが可能です。
virt-install で virtio-memを設定する場合、以下のオプションの設定が必要になります。

--cpu numa.cell.id=作成するNUMAノードID,numa.cell.cpus=NUMAに割り当てるコア,numa.cell.memory=初期メモリのサイズ(kiB),model=host

--memory maxMemory=初期メモリ+virtio-memのサイズ(MiB),maxMemory.slots=virtio-mem用に確保するメモリスロット数

--memdev model=virtio-mem,target.size=virtio-memのサイズ(MiB),target.node=割り当てるNUMAノードID,target.block=virtio-memの割り当て最小単位(kiB),target.requested=0,address.type=pci,address.domain=0x0000,address.bus=0x00,address.slot=0x08,address.function=0x0

...見ずらいですね。
初期メモリ 4GiB、virtio-mem 8GiB を設定する場合、virt-install 実行時の引数は以下のようになります。

#!/bin/sh
HOSTNAME=test01
#IMAGE=/mnt/vm/iso/openSUSE-Leap-15.4-DVD-x86_64-Build243.2-Media.iso
IMAGE=/mnt/vm/iso/Fedora-Server-dvd-x86_64-36-1.5.iso

virt-install \
--name $HOSTNAME \
--cpu numa.cell.id=0,numa.cell.cpus=0-1,numa.cell.memory=4194304,model=host \ ★
--hvm \
--virt-type kvm \
--memory maxMemory=12288,maxMemory.slots=2 \ ★
--memdev model=virtio-mem,target.size=8192,target.node=0,target.block=2048,target.requested=0,address.type=pci,address.domain=0x0000,address.bus=0x00,address.slot=0x08,address.function=0x0 \ ★
--vcpus 2 \
--arch x86_64 \
--boot hd \
--disk path=/mnt/vm/$HOSTNAME.qcow2,format=qcow2,size=5,bus=virtio \
--network network=management,model=virtio \
--location $IMAGE \
--noautoconsole

実際に作成してみます。

halni@SUSE-MZ32:~> sudo virt-install \
> --name virtiomem01 \
> --cpu numa.cell.id=0,numa.cell.cpus=0-1,numa.cell.memory=4194304,model=host \
> --hvm \
> --virt-type kvm \
> --memory maxMemory=12288,target.size=8192,target.node=0,target.block=2048,target.requested=0,address.type=pci,address.domain=0x0000,address.bus=0x00,address.slot=0x08,address.function=0x0 \
> --vcpus 2 \
> --arch x86_64 \
> --boot hd \
> --disk path=/mnt/vm/virtiomem01.qcow2,format=qcow2,size=5,bus=virtio \
> --network network=management,model=virtio \
> --location /mnt/vm/iso/openSUSE-Leap-15.4-DVD-x86_64-Build243.2-Media.iso \
> --noautoconsole

Starting install...
Retrieving 'linux'                                          |    0 B  00:00:00 ...
Retrieving 'initrd'                                         |    0 B  00:00:00 ...
Allocating 'virtiomem01.qcow2'                              |    0 B  00:00:00 ...
Creating domain...                                          |    0 B  00:00:00

Domain is still running. Installation may be in progress.
You can reconnect to the console to complete the installation process.
halni@SUSE-MZ32:~> sudo virsh list --all 
 Id   Name                State
------------------------------------
~~~
 24   virtiomem01         running
~~~

halni@SUSE-MZ32:~> sudo virsh dumpxml virtiomem01 | xmllint --xpath //memory -
<memory unit="KiB">12582912</memory>
<memory model="virtio-mem">
      <target>
        <size unit="KiB">8388608</size>
        <node>0</node>
        <block unit="KiB">2048</block>
        <requested unit="KiB">0</requested>
        <current unit="KiB">0</current>
      </target>
      <alias name="virtiomem0"/>
      <address type="pci" domain="0x0000" bus="0x00" slot="0x08" function="0x0"/>
    </memory>
halni@SUSE-MZ32:~>

virtio-memが設定されたゲストが作成されています。

なお、virtio-memで指定したメモリはまだ使用されていないため、現時点でゲストに見えているメモリサイズは初期メモリの4GiBのみとなります。
(ゲストへのメモリ追加・縮小方法については後に説明します)

定義済みのゲストに virtio-mem を設定する

定義済みのゲストに virtio-memを設定していきます。

今回は初期メモリ4GiB + virtio-mem 4GiB分 を追加していきます。
virtio-memはnumaに紐づくため、1CPU構成であってもNUMAの設定が必要になります。

設定項目としては

halni@SUSE-MZ32:~> diff -u SUSE-cloud01.xml SUSE-cloud01-virtiomem.xml
--- SUSE-cloud01.xml    2022-12-04 09:49:17.487963281 +0900
+++ SUSE-cloud01-virtiomem.xml  2022-12-04 10:03:23.665284521 +0900
@@ -6,7 +6,8 @@
       <libosinfo:os id="http://opensuse.org/opensuse/15.4"/>
     </libosinfo:libosinfo>
   </metadata>
-  <memory unit='KiB'>4194304</memory>
+  <maxMemory slots='2' unit='KiB'>8388608</maxMemory>
+  <memory unit='KiB'>8388608</memory>
   <currentMemory unit='KiB'>4194304</currentMemory>
   <vcpu placement='static'>4</vcpu>
   <os>
@@ -18,7 +19,11 @@
     <apic/>
     <vmport state='off'/>
   </features>
-  <cpu mode='host-passthrough' check='none' migratable='on'/>
+  <cpu mode='host-passthrough' check='none' migratable='on'>
+    <numa>
+      <cell id='0' cpus='0-3' memory='4194304' unit='KiB'/>
+    </numa>
+  </cpu>
   <clock offset='utc'>
     <timer name='rtc' tickpolicy='catchup'/>
     <timer name='pit' tickpolicy='delay'/>
@@ -177,6 +182,16 @@
       <backend model='random'>/dev/urandom</backend>
       <address type='pci' domain='0x0000' bus='0x05' slot='0x00' function='0x0'/>
     </rng>
+    <memory model='virtio-mem'>
+      <target>
+        <size unit='KiB'>4194304</size>
+        <node>0</node>
+        <block unit='KiB'>2048</block>
+        <requested unit='KiB'>0</requested>
+      </target>
+      <alias name='ua-virtiomem0'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
+    </memory>
   </devices>
 </domain>

halni@SUSE-MZ32:~>

といった感じ。

ゲストメモリの追加・縮小方法

ゲストへの virtio-mem の追加・縮小は

$ virsh update-memory-device $DOMAIN --requested-size $SIZE

で可能です。

実際に変更してみます。

halni@SUSE-MZ32:~> ssh root@SUSE-cloud01 cat /proc/meminfo | grep Total
MemTotal:        4010632 kB          ★ 4GiB
SwapTotal:             0 kB
VmallocTotal:   34359738367 kB
CmaTotal:              0 kB
HugePages_Total:       0
halni@SUSE-MZ32:~> 
halni@SUSE-MZ32:~> sudo virsh update-memory-device SUSE-cloud01 --requested-size 2GiB   ★ 2GiB追加

halni@SUSE-MZ32:~> sudo virsh dumpxml SUSE-cloud01 | xmllint --xpath //memory -
<memory unit="KiB">8388608</memory>
<memory model="virtio-mem">
      <target>
        <size unit="KiB">4194304</size>
        <node>0</node>
        <block unit="KiB">2048</block>
        <requested unit="KiB">2097152</requested>
        <current unit="KiB">2097152</current>     ★ メモリ量が反映されている
      </target>
      <alias name="ua-virtiomem0"/>
      <address type="pci" domain="0x0000" bus="0x00" slot="0x04" function="0x0"/>
    </memory>
halni@SUSE-MZ32:~>

halni@SUSE-MZ32:~> ssh root@SUSE-cloud01 cat /proc/meminfo | grep Total
MemTotal:        6107784 kB          ★ 6GiB
SwapTotal:             0 kB
VmallocTotal:   34359738367 kB
CmaTotal:              0 kB
HugePages_Total:       0
halni@SUSE-MZ32:~> 

--requested-size 0 とすると virtio-mem 分のメモリ領域が 0 B になります。

halni@SUSE-MZ32:~> sudo virsh update-memory-device SUSE-cloud01 --requested-size 0

halni@SUSE-MZ32:~> sudo virsh dumpxml SUSE-cloud01 | xmllint --xpath //memory -
<memory unit="KiB">8388608</memory>
<memory model="virtio-mem">
      <target>
        <size unit="KiB">4194304</size>
        <node>0</node>
        <block unit="KiB">2048</block>
        <requested unit="KiB">0</requested>
        <current unit="KiB">0</current>     ★ 0 になっている
      </target>
      <alias name="ua-virtiomem0"/>
      <address type="pci" domain="0x0000" bus="0x00" slot="0x04" function="0x0"/>
    </memory>
halni@SUSE-MZ32:~> ssh root@SUSE-cloud01 cat /proc/meminfo | grep Total
MemTotal:        4010632 kB          ★ 4GiB
SwapTotal:             0 kB
VmallocTotal:   34359738367 kB
CmaTotal:              0 kB
HugePages_Total:       0
halni@SUSE-MZ32:~>

virtio-mem の挙動

基本的に virtio-mem では update-memory-deviceで設定したメモリサイズはゲストに即時反映されます。
ゲストが virtio-mem 領域を使用していた場合、 ゲストは頑張ってメモリを解放します。

virtio-mem を設定した Fedora ゲスト上で、メモリを使用した状態で virtio-mem を 0GiB に設定してみます。 virto-mem 領域の変化を観察するため、メモリ構成は

初期メモリ 2GiB + virtio-mem 4GiB  計 6GiB

としています。

まずは virto-mem を計 4GiB アタッチ

halni@SUSE-MZ32:~> sudo virsh update-memory-device Fedora01 --requested-size 4GiB

halni@SUSE-MZ32:~> ssh halni@Fedora01 cat /proc/meminfo | grep Total
MemTotal:        6145712 kB
SwapTotal:       1950716 kB
VmallocTotal:   34359738367 kB
CmaTotal:              0 kB
HugePages_Total:       0
halni@SUSE-MZ32:~> sudo virsh dumpxml Fedora01 | xmllint --xpath //memory -
<memory unit="KiB">6291456</memory>
<memory model="virtio-mem">
      <target>
        <size unit="KiB">4194304</size>
        <node>0</node>
        <block unit="KiB">2048</block>
        <requested unit="KiB">4194304</requested>
        <current unit="KiB">4194304</current>
      </target>
      <alias name="ua-virtiomem0"/>
      <address type="pci" domain="0x0000" bus="0x00" slot="0x04" function="0x0"/>
    </memory>
halni@SUSE-MZ32:~>

ゲスト上でメモリ使用量を増やします。現時点でのメモリ使用量はこんな感じ。

6GiB中 5GiB 使用、1GiB空きといったところ
ここら辺で virtio-mem の割り当てを 0B にします。

halni@SUSE-MZ32:~> sudo virsh update-memory-device Fedora01 --requested-size 0GiB

halni@SUSE-MZ32:~> sudo virsh dumpxml Fedora01 | xmllint --xpath //memory -
<memory unit="KiB">6291456</memory>
<memory model="virtio-mem">
      <target>
        <size unit="KiB">4194304</size>
        <node>0</node>
        <block unit="KiB">2048</block>
        <requested unit="KiB">0</requested>
        <current unit="KiB">3418112</current>
      </target>
      <alias name="ua-virtiomem0"/>
      <address type="pci" domain="0x0000" bus="0x00" slot="0x04" function="0x0"/>
    </memory>
halni@SUSE-MZ32:~> sudo virsh dumpxml Fedora01 | xmllint --xpath //memory - | grep current
        <current unit="KiB">3155968</current>
halni@SUSE-MZ32:~> sudo virsh dumpxml Fedora01 | xmllint --xpath //memory - | grep current
        <current unit="KiB">2895872</current>
halni@SUSE-MZ32:~> sudo virsh dumpxml Fedora01 | xmllint --xpath //memory - | grep current
        <current unit="KiB">1691648</current>
halni@SUSE-MZ32:~> sudo virsh dumpxml Fedora01 | xmllint --xpath //memory - | grep current
        <current unit="KiB">1560576</current>
halni@SUSE-MZ32:~> sudo virsh dumpxml Fedora01 | xmllint --xpath //memory - | grep current
        <current unit="KiB">1443840</current>
halni@SUSE-MZ32:~>
~~~
halni@SUSE-MZ32:~> sudo virsh dumpxml Fedora01 | xmllint --xpath //memory - | grep current
        <current unit="KiB">212992</current>
halni@SUSE-MZ32:~>

大体5分くらい掛けて 3.8GiB 程度が解放されました。

この時の sar を確認してみます。
図に引いた赤線 8:37 あたりで --requested-size 0GiB を実行しています。

まずはメモリから。

sar -r の実行結果

8:37 を境に、avail が大きく減少し、buffer や cache への割り当ても減っていることがわかります。

システムの負荷についても確認してみます。

sar の実行結果

変更したタイミングでCPUの負荷が上昇しています。内部で大きな処理が走っていたようです。

ゲストの messages を確認してみます。

Dec  7 20:37:28 Fedora01 kernel: Out of memory: Killed process 1835 (wireplumber) total-vm:472732kB, anon-rss:4944kB, file-rss:0kB, shmem-rss:0kB, UID:1000 pgtables:140kB oom_score_adj:200
Dec  7 20:39:41 Fedora01 kernel: Out of memory: Killed process 1833 (pipewire) total-vm:360468kB, anon-rss:4172kB, file-rss:0kB, shmem-rss:20kB, UID:1000 pgtables:148kB oom_score_adj:200
Dec  7 20:39:41 Fedora01 kernel: Out of memory: Killed process 2547 (pipewire-pulse) total-vm:244328kB, anon-rss:2296kB, file-rss:0kB, shmem-rss:0kB, UID:1000 pgtables:100kB oom_score_adj:200
Dec  7 20:39:41 Fedora01 kernel: Out of memory: Killed process 1837 (dbus-broker-lau) total-vm:10388kB, anon-rss:344kB, file-rss:0kB, shmem-rss:0kB, UID:1000 pgtables:60kB oom_score_adj:200
Dec  7 20:39:41 Fedora01 kernel: Out of memory: Killed process 1050 ((sd-pam)) total-vm:176376kB, anon-rss:2244kB, file-rss:0kB, shmem-rss:0kB, UID:1000 pgtables:100kB oom_score_adj:100
Dec  7 20:39:41 Fedora01 kernel: Out of memory: Killed process 4949 (pipewire) total-vm:226584kB, anon-rss:556kB, file-rss:0kB, shmem-rss:0kB, UID:1000 pgtables:80kB oom_score_adj:200
Dec  7 20:39:41 Fedora01 kernel: Out of memory: Killed process 1048 (systemd) total-vm:22408kB, anon-rss:2664kB, file-rss:0kB, shmem-rss:0kB, UID:1000 pgtables:88kB oom_score_adj:100
Dec  7 20:39:41 Fedora01 kernel: Out of memory: Killed process 3817 (qemu-system-x86) total-vm:3369484kB, anon-rss:139572kB, file-rss:0kB, shmem-rss:0kB, UID:107 pgtables:1372kB oom_score_adj:0

多くのプロセスが OOM Killer によってメモリ不足を理由に停止していることがわかります。

参考文献

virtio-mem.gitlab.io