■aws EC2を「on KVM」にするため、ネストされた仮想化のサポートを試してみる
個人的には、そもそもネステッドにできないものは「仮想化基盤」とは言わないと思っている。
metalは高額なので、砂場にするのももったいない。
ネステッド要件のインスタンスタイプについて今回、制限範囲が分かったので、常用も「あり」ですね。
特にコンテナじゃだめだけど、EC2立てるほどじゃないという使い捨て環境の構築がはかどる印象
カスタムAMI作るためのrawディスクの作成環境としても良いかも(?)
Amazon EC2 は仮想 Amazon EC2 インスタンス上でネストされた仮想化のサポートを開始
https://aws.amazon.com/jp/about-aws/whats-new/2026/02/amazon-ec2-nested-virtualization-on-virtual/
この機能は、すべての商用リージョンの C8i、M8i、および R8i インスタンスでご利用いただけます
■現状のインスタンス確認
「NestedVirt」に対応していないと「None」、無効なら「disabled」。
ここを「enabled」にする方法とも言いかえられる。
$ # Get instance IDs for nested-virt-*
INSTANCE_IDS=$(aws ec2 describe-instances \
--filters "Name=tag:Name,Values=nested-virt-*" "Name=instance-state-name,Values=running,pending" \
--query "Reservations[].Instances[].InstanceId" \
--output text \
--region ap-northeast-1)
# Check CPU Options with the retrieved IDs
aws ec2 describe-instances \
--instance-ids ${INSTANCE_IDS} \
--query "Reservations[].Instances[].{Name:Tags[?Key=='Name'].Value|[0],InstanceId:InstanceId,NestedVirt:CpuOptions.NestedVirtualization}" \
--output table \
--region ap-northeast-1
----------------------------------------------------------------
| DescribeInstances |
+----------------------+-------------------------+-------------+
| InstanceId | Name | NestedVirt |
+----------------------+-------------------------+-------------+
| i-XXXXXXXXXXXXXXXXX | ec2winSV2025-20260129 | None |
+----------------------+-------------------------+-------------+
■us-east-1で、[cmr]8iの料金で$0.9〜1.2の範囲で選択可能なインスタンスタイプの調査
最終的にはap-northeast-1に置くとしても、これ以上は下がらないという最安環境を見ておきたい。
以下、過去に作ったスクリプトを流用。
awscliでAWSリージョンとAZの一覧を取得する
https://labunix.hateblo.jp/entry/20180918/1537269823
■インスタントタイプは「インスタンスタイプ: m8i.16xlarge, r8i.16xlarge, c8id.16xlarge, m8id.16xlarge, r8id.16xlarge」が候補
※「c8i」はメモリ128GB、「m8i」だと256GB、「r8i」なら512GBの違い。
vCPU 32C(x2T)、NIC 30 Gigabit、GPU,FPGA なし
「ssd 3800GB」があると「d」が付くようだ。
※「c8i.metal-48xl,r8i.metal-48xl,m8i.metal-48xl」は約3倍の上位性能なので料金の参考値のみ
ここで人によっては「metalで良い」と、なるかも知れないので。
$ aws ec2 describe-spot-price-history \
--start-time $(date -d '1 days ago' -u '+%Y-%m-%dT%H:%M:%S.000Z') \
--product-description "Linux/UNIX" --region us-east-1 | \
jq -r '.SpotPriceHistory[] | [ .InstanceType , .ProductDescription , .AvailabilityZone , .SpotPrice ] | @csv' | \
tr '"' ' ' | awk -F\, '{if($1 ~ /[cmr]8id.metal-48xl|[cmr]8i.16xlarge|[cmr]8id.16xlarge/){a[$1]=a[$1]_$4}}END{for(n in a){print n,a[n]}}' | sort -k 1 -V
c8id.16xlarge 1.043300 0.877900 0.835600 0.773600 0.891600 0.952300 0.785200 0.828800 0.929500 0.895900 1.037700 0.799900 0.826300 0.897700 0.924800 1.036500 0.831500 0.814600 0.907200 0.908300 0.833800 1.040000 0.819200 0.914600
c8id.metal-48xl 3.153600 3.414100 3.191300 2.514100 3.527300 3.428700 2.546900 3.215100 3.343800 3.444100 3.559200 2.548500 3.219200 3.332500 2.562900 3.449400 3.564700 3.228500 3.347100 2.586500 3.472100 3.578700
c8i.16xlarge 1.470200 1.026100 1.158800 1.029400 1.052300 1.002900 1.455900 1.030300 1.159900 0.999500 1.041000 1.034400 1.439200 1.023500 1.038500 1.159600 0.998900 1.166400 1.426000 1.005100 1.019500 1.044500 1.004300 1.418500 1.165300
m8id.16xlarge 1.568400 0.930400 1.762700 0.928300 1.471600 2.653900 1.532500 0.925900 1.466500 2.619800 1.746800 1.496900 1.462600 1.704700 0.931100 2.574000 1.482000 2.562400 0.941900 1.678100 1.466900 1.471300 2.556100 0.942500 1.676300
m8id.metal-48xl 2.183500 2.842200 2.518600 1.461300 2.187100 3.251600 2.536300 2.852300 1.474700 3.233200 2.216800 2.574600 2.842700 3.190400 2.197600 1.498600 2.871700 2.621100 3.142600 1.503300 2.893000 2.205600 2.654900
m8i.16xlarge 1.193100 1.213400 1.024500 1.102500 1.046800 1.191900 1.042900 1.216800 1.193400 1.105600 1.019300 1.031100 1.014800 1.228500 1.196700 1.105500 1.034700 1.015100 1.233300 1.212100 1.116500 1.018500 1.012400
r8id.16xlarge 4.125100 4.206100 2.497200 4.150400 1.233100 4.218800 2.542900 4.189400 4.285400 1.236900 5.322200 4.197700 1.244900 2.526600 4.345100 4.252700 4.269000 4.353500 2.482500 1.255400 5.322200
r8id.metal-48xl 2.487000 2.778200 4.119700 2.186700 2.445300 3.500500 2.826800 2.217600 3.526100 4.145300 2.420600 2.255600 2.827200 3.533100 2.416500 4.158400 2.873200 2.267700 2.378700 3.567400 2.925400 2.348400 2.282200 4.165100 3.610000
r8i.16xlarge 1.096000 1.884300 1.298000 1.351800 1.353300 1.884200 1.350600 1.276300 1.091200 1.897400 1.355600 1.246800 1.347900 1.080600 1.909500 1.379600 1.255800 1.345500 1.070200 1.383100 1.338100 1.258000 1.060500 1.930400
■「m8id」が「バランスのとれたワークロード」とのことなので、とりあえずこれで。
※「m8id.metal-48xl」は3倍の性能、NICだけ3倍の90でなく75Gigabitなくらい。
「m8id.16xlarge」は逆に1/3の性能なので、料金も1/3を期待したいところ。
C8id インスタンスは、高性能ウェブサーバー、バッチ処理、分散分析、広告配信、動画エンコーディング、ゲームサーバーなど、コンピューティング負荷の高いワークロードに最適です。
M8id インスタンスは、アプリケーションサーバー、マイクロサービス、エンタープライズアプリケーション、小規模から中規模のデータベースなど、バランスの取れたワークロードに適しています。
R8id インスタンスは、インメモリデータベース、リアルタイムビッグデータ分析、大規模インメモリキャッシュ、科学計算アプリケーションなど、メモリ負荷の高いワークロードに最適です。
■「m8i.large」vCPU 2C(x2T)、メモリ8GB、NIC 12.5 Gigabit、GPU,FPGA なしで確認
「ssd 118GB」がある「m8id」ではネステッドを「有効」にする選択肢がグレーアウトしていた
一応ドキュメント上は「etc」となっていたが、m8iまたはm8idかつxlarge,metal以外のインスタンスタイプを抽出するとlargeがあるので、「使えるかどうかすら分からなくても」この料金なら気軽に手が出せる。
$ aws ec2 describe-spot-price-history \
--start-time $(date -d '1 days ago' -u '+%Y-%m-%dT%H:%M:%S.000Z') \
--product-description "Linux/UNIX" --region us-east-1 | \
jq -r '.SpotPriceHistory[] | [ .InstanceType , .ProductDescription , .AvailabilityZone , .SpotPrice ] | @csv' | tr '"' ' ' | awk -F\, '{if($1 ~ /m8id|m8i\./&& $1 !~ /xlarge|metal/){a[$1]=a[$1]_$4}}END{for(n in a){print n,a[n]}}' | sort -k 1 -V
m8id.large 0.046100 0.035100 0.050700 0.064000 0.064800 0.045500 0.050200 0.035300 0.038000 0.065200 0.045100 0.050400 0.035200 0.044600 0.037900 0.050300 0.066200 0.044300 0.035100 0.049600 0.037800
m8i.large 0.042700 0.044500 0.048000 0.044600 0.046600 0.042800 0.041600 0.047800 0.041800 0.043000 0.044700 0.041900 0.043100 0.046500 0.044600 0.047700
■インスタンスの作成画面で以下が選べればOKで、m8idはグレーアウトしていたのでm8i.*で検証
■NestedVirtが「Enabled」になっている
$ # Get instance IDs for nested-virt-*
INSTANCE_IDS=$(aws ec2 describe-instances \
--filters "Name=tag:Name,Values=nested-virt-*" "Name=instance-state-name,Values=running,pending" \
--query "Reservations[].Instances[].InstanceId" \
--output text \
--region ap-northeast-1)
# Check CPU Options with the retrieved IDs
aws ec2 describe-instances \
--instance-ids ${INSTANCE_IDS} \
--query "Reservations[].Instances[].{Name:Tags[?Key=='Name'].Value|[0],InstanceId:InstanceId,NestedVirt:CpuOptions.NestedVirtualization}" \
--output table \
--region ap-northeast-1
-----------------------------------------------------------------
| DescribeInstances |
+----------------------+--------------------------+-------------+
| InstanceId | Name | NestedVirt |
+----------------------+--------------------------+-------------+
| i-XXXXXXXXXXXXXXXXX | ec2winSV2025-20260129 | None |
| i-XXXXXXXXXXXXXXXXY | debian-nested-m8i | enabled |
+----------------------+--------------------------+-------------+
■debianはSSM Agentを入れてIAMはいつもどおりアサイン、踏み台EC2からssh接続する方針
ここはKVMホスト要件を満たせるなら、awsのお作法は好きなようにしてもらえれば。
■これでNestedが使える。見た感じCPUフラグをパススルーしてるっぽい。
余談だけど、それが良いのかは置いといて、EC2のNICはNetworkManager管理。
$ lscpu | grep -A 2 "Model name:\|Virtualization:"
Model name: Intel(R) Xeon(R) 6975P-C
CPU family: 6
Model: 173
--
Virtualization: VT-x
Hypervisor vendor: KVM
Virtualization type: full
$ grep flags /proc/cpuinfo | awk '{for(a=1;a<=NF;a++){if($a ~ /vmx|svm/){print $a | "sort | uniq -c"}}}'
4 vmx
sudo apt install -y qemu-kvm libvirt-daemon-system libvirt-daemon virtinst bridge-utils libosinfo-bin
$ lsmod | grep kvm
kvm_intel 413696 0
kvm 1376256 1 kvm_intel
irqbypass 12288 1 kvm
$ virsh list --all
Id Name State
--------------------
$ ls /sys/class/net/
enp39s0 lo
$ nmcli device status
DEVICE TYPE STATE CONNECTION
enp39s0 ethernet connected Wired connection 1
lo loopback connected (externally) lo
■NetworkManagerの管理外として、virshで管理するvirbr1を作成
$ cat kvm-nat.xml
<network>
<name>kvm-nat</name>
<forward mode='nat'>
<nat>
<port start='1024' end='65535'/>
</nat>
</forward>
<bridge name='virbr1' stp='on' delay='0'/>
<ip address='192.168.100.1' netmask='255.255.255.0'>
<dhcp>
<range start='192.168.100.128' end='192.168.100.254'/>
</dhcp>
</ip>
</network>
$ sudo virsh net-define kvm-nat.xml
Network kvm-nat defined from kvm-nat.xml
$ sudo virsh net-start kvm-nat
Network kvm-nat started
$ nmcli device status
DEVICE TYPE STATE CONNECTION
enp39s0 ethernet connected Wired connection 1
lo loopback connected (externally) lo
virbr1 bridge connected (externally) virbr1
■小さめ(400MB未満)のqcow2を持って来てrootパスワードを設定
※genericcloudは様々なクラウドベンダ向けに使われている
# cloudイメージのダウンロード
$ wget https://cdimage.debian.org/cdimage/cloud/trixie/latest/debian-13-genericcloud-amd64.qcow2
--2026-05-03 13:09:29-- https://cdimage.debian.org/cdimage/cloud/trixie/latest/debian-13-genericcloud-amd64.qcow2
$ du debian-13-genericcloud-amd64.qcow2
331584 debian-13-genericcloud-amd64.qcow2
# cloud-initのカスタマイズでrootのパスワードをdebianにする(※デフォルトはrootパスワードなし)
$ sudo apt install -y libguestfs-tools
$ sudo virt-customize -a debian-13-genericcloud-amd64.qcow2 --root-password password:debian
[ 0.0] Examining the guest ...
[ 12.5] Setting a random seed
[ 12.5] Setting passwords
[ 13.1] SELinux relabelling
[ 13.1] Finishing off
■どうでもいい余談として、genericcloudの初期ユーザはdebianだがパスワードログイン禁止なので、何もしないとログインできずに詰むというcloud-initの記載箇所をピックアップ
$ sudo modprobe nbd
$ lsmod | grep nbd
nbd 65536 0
$ sudo qemu-nbd -c /dev/nbd0 debian-13-genericcloud-amd64.qcow2
$ lsblk -l /dev/nbd0
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
nbd0 43:0 0 3G 0 disk
nbd0p1 43:1 0 2.9G 0 part
nbd0p14 43:14 0 3M 0 part
nbd0p15 43:15 0 124M 0 part
$ sudo mount /dev/nbd0p1 /mnt
$ sudo grep -A 20 ^system_info: /mnt/etc/cloud/cloud.cfg
system_info:
# This will affect which distro class gets used
distro: debian
# Default user name + that default users groups (if added/used)
default_user:
name: debian
lock_passwd: True
gecos: Debian
groups: [adm, audio, cdrom, dialout, dip, floppy, plugdev, sudo, video]
sudo: ["ALL=(ALL) NOPASSWD:ALL"]
shell: /bin/bash
# Other config here will be given to the distro class and/or path classes
paths:
cloud_dir: /var/lib/cloud/
templates_dir: /etc/cloud/templates/
package_mirrors:
- arches: [default]
failsafe:
primary: https://deb.debian.org/debian
security: https://deb.debian.org/debian-security
ssh_svcname: ssh
$ sudo umount /mnt
$ sudo qemu-nbd -d /dev/nbd0
■話を戻して、virbr1を使えるようにしてvirsh管理下のKVM-QEMUで起動
※virsh管理下のネットワークについては、本題から逸れるので「動くだけ」で雑に進める
# virbr1 の使用許可
$ sudo mkdir -p /etc/qemu;echo "allow virbr1" | sudo tee /etc/qemu/bridge.conf
$ cat debian13.xml
<domain type='kvm'>
<name>debian13</name>
<memory unit='MiB'>4096</memory>
<vcpu>2</vcpu>
<os>
<type arch='x86_64' machine='pc-i440fx-10.0'>hvm</type>
</os>
<cpu mode='host-passthrough'/>
<devices>
<disk type='file' device='disk'>
<driver name='qemu' type='qcow2'/>
<source file='/var/lib/libvirt/images/debian-13-genericcloud-amd64.qcow2'/>
<target dev='vda' bus='virtio'/>
</disk>
<interface type='bridge'>
<source bridge='virbr1'/>
<model type='virtio'/>
</interface>
<serial type='pty'>
<target port='0'/>
</serial>
<console type='pty'>
<target type='serial' port='0'/>
</console>
</devices>
</domain>
$ sudo virsh define debian13.xml
$ sudo virsh start debian13
$ sudo virsh list
Id Name State
--------------------------
2 debian13 running
$ sudo virsh console debian13
...
Debian GNU/Linux 13 debian ttyS0
debian login: root
Password:
Linux debian 6.12.85+deb13-cloud-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.12.85-1 (2026-04-30) x86_64
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
root@debian:~# lscpu | grep -A 3 "Model name\|Virtualization:"
Model name: Intel(R) Xeon(R) 6975P-C
CPU family: 6
Model: 173
Thread(s) per core: 1
--
Virtualization: VT-x
Hypervisor vendor: KVM
Virtualization type: full
L1d cache: 32 KiB (1 instance)
root@debian:~# hostnamectl | grep -v ID
Static hostname: debian
Icon name: computer-vm
Chassis: vm 🖴
Virtualization: kvm
Operating System: Debian GNU/Linux 13 (trixie)
Kernel: Linux 6.12.85+deb13-cloud-amd64
Architecture: x86-64
Hardware Vendor: QEMU
Hardware Model: Standard PC _i440FX + PIIX, 1996_
Firmware Version: 1.16.3-debian-1.16.3-2
Firmware Date: Tue 2014-04-01
Firmware Age: 12y 1month 2d
■vCPUが2、メモリは4GB、ディスクは余談のlsblkで見た通り3GB(qcow2としては400MB未満)
root@debian:~# grep flags /proc/cpuinfo | awk '{for(a=1;a<=NF;a++){if($a ~ /vmx|svm/){print $a | "sort | uniq -c"}}}'
2 vmx
root@debian:~# free -m
total used free shared buff/cache available
Mem: 3922 251 3750 0 76 3671
Swap: 0 0 0
root@debian:~# lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
vda 254:0 0 3G 0 disk
├─vda1 254:1 0 2.9G 0 part /
├─vda14 254:14 0 3M 0 part
└─vda15 254:15 0 124M 0 part /boot/efi
root@debian:~# fdisk -l
Disk /dev/vda: 3 GiB, 3221225472 bytes, 6291456 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: A6A14BD2-2D1F-4065-9CC0-D3983F38D8AF
Device Start End Sectors Size Type
/dev/vda1 262144 6289407 6027264 2.9G Linux root (x86-64)
/dev/vda14 2048 8191 6144 3M BIOS boot
/dev/vda15 8192 262143 253952 124M EFI System
Partition table entries are not in disk order.
■一時的な確認だけで、メインはNestedVirtなので、ネットワークの確認はサラッと。
root@debian:~# sudo ip addr add 192.168.100.2/24 dev enp0s2
root@debian:~# sudo ip link set enp0s2 up
root@debian:~# ping 192.168.100.2
PING 192.168.100.2 (192.168.100.2) 56(84) bytes of data.
64 bytes from 192.168.100.2: icmp_seq=1 ttl=64 time=0.013 ms
64 bytes from 192.168.100.2: icmp_seq=2 ttl=64 time=0.012 ms
--- 192.168.100.2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 0.012/0.012/0.013/0.000 ms
root@debian:~# ping 192.168.100.1
PING 192.168.100.1 (192.168.100.1) 56(84) bytes of data.
64 bytes from 192.168.100.1: icmp_seq=1 ttl=64 time=0.197 ms
64 bytes from 192.168.100.1: icmp_seq=2 ttl=64 time=0.075 ms
--- 192.168.100.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 0.075/0.136/0.197/0.061 ms
root@debian:~# sudo ip route add default via 192.168.100.1
■とはいえ、pingだけでは信用できないので、socatでechoサーバ立ててクライアントからの応答を確認
※最後は[Ctrl]+[c]でシグナル「2」で落とした
root@debian:~# socat -d -d -v TCP4-LISTEN:8007,reuseaddr,fork PIPE
2026/05/03 15:25:02 socat[1279] N listening on AF=2 0.0.0.0:8007
2026/05/03 15:25:35 socat[1279] N accepting connection from AF=2 192.168.100.1:50490 on AF=2 192.168.100.2:8007
2026/05/03 15:25:35 socat[1280] N writing to and reading from unnamed pipe
2026/05/03 15:25:35 socat[1279] N forked off child process 1280
2026/05/03 15:25:35 socat[1280] N starting data transfer loop with FDs [6,6] and [5,7]
2026/05/03 15:25:35 socat[1279] N listening on AF=2 0.0.0.0:8007
> 2026/05/03 15:25:37.000259439 length=2 from=0 to=1
\r
< 2026/05/03 15:25:37.000262284 length=2 from=0 to=1
\r
> 2026/05/03 15:25:37.000560655 length=2 from=2 to=3
\r
< 2026/05/03 15:25:37.000563169 length=2 from=2 to=3
\r
> 2026/05/03 15:25:39.000005191 length=6 from=4 to=9
echo\r
< 2026/05/03 15:25:39.000008686 length=6 from=4 to=9
echo\r
2026/05/03 15:29:09 socat[1279] N socat_signal(): handling signal 2
2026/05/03 15:29:09 socat[1279] N exiting on signal 2
2026/05/03 15:29:09 socat[1280] N socat_signal(): handling signal 2
2026/05/03 15:29:09 socat[1280] N exiting on signal 2
2026/05/03 15:29:09 socat[1279] N socat_signal(): finishing signal 2
2026/05/03 15:29:09 socat[1279] N exit(130)
2026/05/03 15:29:09 socat[1280] N socat_signal(): finishing signal 2
2026/05/03 15:29:09 socat[1280] N exit(130)
■上記のsocatサーバのechoログは、ec2NestedVirtのホストからtelnetクライアントで与えたもの
※KVMホストとなったEC2と、KVMゲストのgenericcloud間の通信が正常なので、DNSとかルーティングとかまでの確認に手を伸ばさないことにする。
$ telnet 192.168.100.2 8007
Trying 192.168.100.2...
Connected to 192.168.100.2.
Escape character is '^]'.
echo
echo
Connection closed by foreign host.
■ちなみにインスタントタイプは後からでも変えられる。
インスタントタイプが対応している範囲にリセットされるのは当然として、「前の設定は一旦リセットで」というパターンもあるようなので、インスタントタイプを変える前後のNestedVirtの状態はきちんと確認しておくと良さそう。
