先日リリースされた VirtualBox 6.0 からは AMD の CPU で、6.1 からは Intel の CPU で Nested Virtualization がサポートされた。 Nested Virtualization というのは、仮想マシンの中に仮想マシンを入れ子に作ることを指す。 ようするに、仮想マシンをマトリョーシカのようにする。 この機能は、すでに VMware や KVM といったハイパーバイザではサポートされていたものの、今回それが VirtualBox でも使えるようになったというわけ。 この機能があると、サーバ周りのインフラ系をやっている人たちは、検証環境が作りやすくなってうれしい。 ただし、この機能を実現するには、仮想マシンの中で CPU の仮想化支援機能 (Intel-VT / AMD-V) が有効になっている必要がある 1 。

VirtualBox 6.1 のリリースノート 2 を見ると、次のような記載がある。

Virtualization core: Support for nested hardware-virtualization on Intel CPUs (starting with 5th generation Core i, codename Broadwell), so far tested only with guest running VirtualBox

どうやら、Intel であれば第 5 世代 Core i 以降の CPU で仮想化支援機能を使った Nested Virtualization ができるようになったらしい。 このニュースは、個人的に感慨深いものだった。 というのも、次のチケットを見てもらいたい。

www.virtualbox.org

このチケットは、VirtualBox に Nested Virtualization の機能を要望したものになっている。 問題は、チケットが作成された日付で、見ると "Opened 11 years ago" とある。 つまり、11 年という歳月をこえて、ユーザに要望されてきた機能がついに実現したというわけ。 ちなみに、これまで開発側の反応はどうだったかというと、チケットには「便利だろうけど実装するの大変だから...」みたいなコメントがあった。 なお、この機能について自分で調べていた頃のブログを調べると、ポストした日付が 8 年前になっていた。

Mac で仮想マシンの入れ子 (Nested Virtualization) をする | CUBE SUGAR STORAGE momijiame.tumblr.com

今回は、せっかくなので VirtualBox を使った Nested Virtualization を試してみる。 使った環境は次のとおり。

$ sw_vers ProductName: Mac OS X ProductVersion: 10.14 . 6 BuildVersion: 18G2022 $ sysctl -a | grep brand_string machdep.cpu.brand_string: Intel(R) Core(TM) m3-7Y32 CPU @ 1 .10GHz $ vagrant version | head -n 1 Installed Version: 2.2 . 7 $ vboxmanage | head -n 1 Oracle VM VirtualBox Command Line Management Interface Version 6.1 . 2

もくじ

下準備

はじめに、Homebrew を使って Vagrant と VirtualBox をインストールしておく。 もちろん、Vagrant を使わずに VirtualBox の GUI フロントエンドを使ってもかまわない。

$ brew cask install vagrant virtualbox

Vagrant + VirtualBox で仮想マシンを用意する (L1)

物理的なハードウェア上で直接動作する仮想化のことを L1 と呼ぶことがあるようだ。 ようするに、一般的な状況としての仮想マシンがこれ。 まずは L1 の仮想マシンとして Vagrant + VirtualBox を使って Ubuntu 18.04 LTS をインストールする。

仮想マシンのイメージをダウンロードしたら、設定ファイルを生成する。

$ vagrant box add ubuntu/bionic64 $ vagrant init ubuntu/bionic64

次のように Vagrant の設定ファイルができる。

$ head Vagrantfile Vagrant .configure( " 2 " ) do | config |

ここで、設定ファイルを編集する必要がある。 ポイントは最後の vb.customize に渡している引数の --nested-hw-virt on で、これがないと L1 の仮想マシンで CPU の仮想化支援機能が有効にならない。 あと、Nested Virtulization をするには、かなり処理のオーバーヘッドがあるので仮想マシンのリソースは多めに確保しておいた方が良い。

config.vm.provider "virtualbox" do |vb| vb.cpus = "2" vb.memory = "2048" vb.customize ["modifyvm", :id, "--nested-hw-virt", "on"] end

なお、Vagrant ではなく VirtualBox の GUI フロントエンドを使って操作しているときは、仮想マシンの設定画面を開いて次の項目にチェックをつければ良い。

VirtualBox で Nested Virtualization するのに必要な GUI 設定画面のチェック項目

仮想マシンを起動したらログインする。

$ vagrant up $ vagrant ssh

これで L1 の仮想マシンとして Ubuntu 18.04 LTS が利用できるようになった。

$ cat /etc/lsb-release DISTRIB_ID=Ubuntu DISTRIB_RELEASE= 18.04 DISTRIB_CODENAME=bionic DISTRIB_DESCRIPTION= "Ubuntu 18.04.3 LTS" $ uname -r 4.15 .0-72-generic

CPU に仮想化支援機能のフラグが立っていることを確認する

それでは、CPU に仮想化支援機能のフラグが立っていることを確認してみよう。 Linux では proc ファイルシステムの /proc/cpuinfo で CPU のフラグが確認できる。 今回使っているのは Intel の CPU なので "vmx" というフラグを探す。

$ grep vmx /proc/cpuinfo flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc cpuid tsc_known_freq pni pclmulqdq vmx ssse3 cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single pti tpr_shadow flexpriority fsgsbase avx2 invpcid rdseed clflushopt md_clear flush_l1d flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc cpuid tsc_known_freq pni pclmulqdq vmx ssse3 cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single pti tpr_shadow flexpriority fsgsbase avx2 invpcid rdseed clflushopt md_clear flush_l1d

ちゃんと "vmx" フラグが立っていることがわかった。

仮想マシンの中に Vagrant + Libvirt (KVM) で仮想マシンを作る (L2)

続いては、L1 の仮想マシンの中に、さらに仮想マシンを作る。 先ほどのチケットには L1 / L2 共に VirtualBox を使った検証しかしていない、とあった。 そこで、せっかくなので L2 に KVM を使っても動くのかどうか調べてみることにした。 使う環境としては Libvirt 経由で KVM を Vagrant から扱う。

まずは必要なパッケージをインストールしておく。

$ sudo apt-get update $ sudo apt-get -y install vagrant-libvirt qemu-kvm libvirt-bin gawk

KVM が使える状態になっていることを kvm-ok コマンドや、カーネルモジュールがロードされていることから確認する。

$ kvm-ok INFO: /dev/kvm exists KVM acceleration can be used $ lsmod | grep kvm kvm_intel 217088 0 kvm 610304 1 kvm_intel irqbypass 16384 1 kvm

現在のユーザを libvirt および kvm グループに参加させる。

$ sudo usermod -aG libvirt,kvm $( who am i | awk '{print $1}' )

ここで、いったん L1 の仮想マシンを再起動しておく。

$ exit $ vagrant reload

そして、もう一度 L1 の仮想マシンにログインする。

$ vagrant ssh

L2 の仮想マシンとしては、違いがわかりやすいように CentOS 7 を使うことにした。 次のようにして仮想マシンを起動する。

$ vagrant box add centos/ 7 --provider=libvirt $ vagrant init centos/ 7 $ vagrant up

ちなみに、前述したとおり Nested Virtualization はオーバーヘッドが大きいので、この作業には大変に時間がかかる。 作業の進捗状況を確認したいときは、次のようにして仮想マシンのコンソールを取って見ると良い。

$ virsh list $ virsh console < name >

仮想マシンが起動したら、ログインする。

$ vagrant ssh

確認すると、ちゃんと CentOS 7 が動作している。 これで、macOS / Ubuntu 18.04 LTS / CentOS 7 という仮想マシンのマトリョーシカが完成した。

$ cat /etc/redhat-release CentOS Linux release 7.6 . 1810 (Core) $ uname -r 3.10 .0 -957.12 . 2 .el7.x86_64

なんとも感慨深い。