10月28日、Uroš Popoviciが「Linux VM without VM software」と題した記事を公開した。この記事では、仮想化ソフトウェアを使わずに「LinuxカーネルをLinuxカーネル上で動かす」――いわば“カーネルの中にもうひとつのカーネルを起動する”という、User-Mode Linux(UML)の仕組みと実践方法について詳しく紹介されている。以下に、その内容を紹介する。
ハードウェア抽象化と仮想化の基礎
Linuxカーネルの基本的な役割は、ハードウェアを抽象化し、ユーザー空間に一貫したインターフェースを提供することである。CPUやメモリなどの共有リソースを複数のタスクで管理し、デバイスツリーなどを介して適切なドライバと接続する。
このハードウェアは、仮想マシン上ではすべて仮想的に再現される。たとえばQEMUでは、メモリやディスクといったリソースはユーザー空間アプリケーションによって仮想化され、一定の性能オーバーヘッドを伴う。CPUの仮想化もまた、異なるアーキテクチャをエミュレーションする場合にはユーザー空間レベルで行われる。
特筆すべきは、「準仮想化(paravirtualization)」と呼ばれる仕組みだ。これはドライバが自ら仮想環境で動作していることを認識し、仮想ハードウェアと効率的に通信する手法である。物理ハードウェアでは不可能な最適化も可能となり、物理デバイスに近い性能を実現できるとされている。
UML ― ユーザー空間で動くカーネル
UML(User-Mode Linux)は、まさにこの「準仮想化」カーネルの一形態といえる。UMLカーネルはベアメタル上ではなく、既存のカーネルのユーザー空間で動作し、ファイルやソケットといった既存の仕組みを活用して新しいLinuxカーネルを起動する。
たとえば、物理UARTデバイスではなく標準入出力(stdin/stdout)を利用するコンソールドライバや、物理ディスクではなくホストファイルをブロックデバイスとして扱うドライバなどがある。
カーネルドキュメントにも、その構造を示す図が掲載されている。
+----------------+
| Process 2 | ...|
+-----------+----------------+
| Process 1 | User-Mode Linux|
+----------------------------+
| Linux Kernel |
+----------------------------+
| Hardware |
+----------------------------+
このように、UMLは「プロセスとして動作するLinuxカーネル」であり、ホスト上のプロセス・スレッドに対応づけられた形で独立したLinux環境を構築する。コンテナに似ているが、コンテナとは異なりカーネル自体を分離して動作させる点が特徴である。
UMLカーネルのビルド
UMLカーネルはx86環境専用であり、x86上のLinuxカーネルのユーザー空間でのみ実行できる。まずは通常のカーネルビルドと同様に構成を行う。
ARCH=um make menuconfig
設定画面では、UML特有のオプションが表示される。特に有用なのが BLK_DEV_UBD オプションで、これはホスト上の任意のファイルを仮想ブロックデバイスとして扱うためのドライバだ。デフォルトでは無効になっているため、Y を選択することが推奨されている。
ビルドは通常通り進められる。
ARCH=um make -j16
ビルドが完了すると、linux というバイナリが生成される。
$ file linux
linux: ELF 64-bit LSB executable, x86-64, dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, ...
興味深いことに、このバイナリは動的リンクされた実行ファイルであり、標準Cライブラリ(libc.so.6)に依存している。
ユーザー空間の構築と実行
UML内で意味のある操作を行うためには、ユーザー空間(rootファイルシステム)が必要になる。著者はシンプルな方法として、Buildroot を使ってx86/64向けに最小限のrootfsをビルドした。
これを使い、ホスト上のファイルをブロックデバイスとして提供し、UMLカーネルを起動する。
$ dd if=/dev/urandom of=./disk.ext4 bs=1M count=100
$ sudo mkfs.ext4 ./disk.ext4
$ ./linux ubd0=/tmp/uml/rootfs.ext2 ubd1=/tmp/uml/disk.ext4 root=/dev/ubda
起動すると、見慣れたLinuxカーネルのブートログが流れ、やがて buildroot login: が表示される。ここでログインすれば、完全に独立したLinux環境に入ることができる。
次に、仮想ディスクをマウントしてテストファイルを作成する。
# mkdir /mnt/disk
# mount /dev/ubdb /mnt/disk/
# echo "This is a UML test!" > /mnt/disk/foo.txt
# cat /mnt/disk/foo.txt
This is a UML test!
UMLをシャットダウンしたあと、ホスト側でディスクをマウントすれば、同じファイルが確認できる。
$ sudo mount ./disk.ext4 ./img
$ cat ./img/foo.txt
This is a UML test!
これにより、UML内で作成したデータがホスト上のブロックデバイスを介して永続化されていることが確認できる。
結論 ― 仮想化でもあり、そうでなくもある
UMLは「仮想マシン」と呼ばれることもあるが、その性質は少し異なる。カーネルを分離して動作させるという点では仮想化に近いが、UMLのゲストカーネルはホストカーネルと密接に結びついており、完全な隔離環境ではない。
そのため、UMLは本番環境での仮想化基盤としてよりも、カーネルのデバッグや実験環境としての利用に最も適している。KVMなどのハードウェア支援型仮想化が本格的な運用向けであるのに対し、UMLは「コンテナとKVMの中間」に位置する存在だといえる。
本格的な分離を求める用途には向かないが、Linuxカーネルの仕組みを深く理解したり、安全に実験したりするには、非常に魅力的なツールである。著者も述べるように、「何よりも楽しい」というのが最大の利点だろう。
詳細はLinux VM without VM softwareを参照していただきたい。