この記事は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.2TBopenSUSE 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:~>
ゲスト上でメモリ使用量を増やします。現時点でのメモリ使用量はこんな感じ。
ここら辺で 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 を実行しています。
まずはメモリから。
8:37 を境に、avail が大きく減少し、buffer や cache への割り当ても減っていることがわかります。
システムの負荷についても確認してみます。
変更したタイミングで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 によってメモリ不足を理由に停止していることがわかります。