User Mode Linuxを使ってLinux内部でLinuxを実行する

User Mode Linux(UML)を使えば、ホスト側LinuxカーネルのユーザモードプロセスとしてLinuxカーネルを実行することが可能であり、1台の物理的ハードウェア上で複数の独立した仮想マシンを容易に実行できる。本稿ではこのUMLに注目し、1台のハードウェアで複数のLinuxを楽しむ方法やカーネルを簡単にデバッグできる方法を紹介する。

UMLでは、各仮想マシンがそれぞれに異なるソフトウェアを実行できる。ここで言うソフトウェアには、Linuxの各種ディストリビューションおよびカーネルも含まれる。つまり、互いに独立し、ホストマシンからも独立した複数の仮想マシンを徹底的にカスタマイズできるのだ。何より、このテクノロジを使って嬉しいのは、脆弱性を封じ込めることによるシステムのセキュア化、開発者や管理者に対する開発およびテスト用のプライベートなサンドボックスの提供、gdbなど使い慣れたユーザ空間ユーティリティによるカーネルの問題のデバッグが行えることである。

UMLを試してみる

UMLの新しいホームページにある「スターターガイド(Getting Started)」の手順に従えば、今すぐUMLを実行するのも難しくはない。この手順には、コンパイル済みUMLゲストカーネルと1.6GBのFedora Core 5ルートファイルシステム(80MBに圧縮されている)をダウンロードして実行する方法が示されている。

Linux-VServerXenのような他の仮想化ソフトウェアと異なり、ホストカーネル側には特別なサポートが一切必要ないので、簡単に作業を始められる。ただし後で説明するように、開発継続中の基本システム上でUMLを実行する場合は、SKAS3パッチを用いてコンパイルしたホストカーネルが必要になることがある。「スターターガイド」の手順にある次のコマンドを見てみよう。

./linux-2.6.19-rc5 ubda=FedoraCore5-x86-root_fs mem=128M

UMLのゲストカーネル(linux-2.6.19-rc5)を、一般のプロセスをコマンドラインから実行するのと同じように実行していることがわかるだろう。ただし、UMLゲストカーネルはコマンドラインから実行できるように特別にコンパイルされていることに注意してほしい。通常のLinuxカーネルをコマンドラインから実行しようとしても動かないはずだ。UMLゲストカーネルのビルド方法については後で紹介する。

memパラメータは、仮想マシンに割り当てるRAMのサイズを単に指定するものだが、この値は物理マシンのメモリ総量を超えても構わない。ホストカーネルは、仮想マシンに対して仮想メモリを割り当てることができるためだ。

ただし、UMLカーネルはホスト上の通常のプロセスとして実行されるので、ホストマシンのアーキテクチャにおけるプロセスアドレス空間のサイズの制限を受ける。x86の場合は通常3GBである。

上記コマンドのubdaパラメータは仮想マシンの仮想ブロックデバイス/dev/udbaの作成に使うファイルの名前をUMLカーネルに与えるもので、このブロックデバイスが仮想マシンのルートファイルシステムになる。仮想ブロックデバイス/dev/udbaは仮想マシンの最初のブロックデバイスで、ホストLinuxシステムの物理ブロックデバイス/dev/hdaに相当する。

この場合は、/dev/ubdaがFedora Core 5のルートファイルシステムを含むファイルのデータを持つ仮想ブロックデバイスになるように設定している。つまり、UMLシステムはFedora Core 5のルートファイルシステムからブートされるわけだ。また、ブロックデバイスは複数の指定が可能であり、指定先はファイルシステムでなくてもよい。たとえば、/dev/ubdbというデバイスを作成してスワップパーティションにすることもできる。その場合はUMLカーネルを実行する前に、次のようにしてホストシステム上にファイルを作成しておく必要がある。

dd if=/dev/zero of=swap bs=1M count=128

このコマンドによってサイズ128MBのファイルができるので、UMLカーネルの起動時には次のようなubdbパラメータを使えばよい。

./linux-2.6.19-rc5 ubda=FedoraCore5-x86-root_fs ubdb=swap mem=128M

仮想マシンの起動時には、次のように2つのブロックデバイスの存在が確認できる。

[root@localhost ~]# ls -l /dev/ubd*
brw-r----- 1 root disk 98,  0 Dec 14 02:19 /dev/ubda
brw-r----- 1 root disk 98, 16 Dec 14 02:19 /dev/ubdb

/dev/ubdbをスワップ空間として設定し、その結果を確認するには、以下のコマンドを実行する。

[root@localhost ~]# mkswap /dev/ubdb
Setting up swapspace version 1, size = 134213 kB
[root@localhost ~]# swapon /dev/ubdb
[root@localhost ~]# swapon -s
Filename                                Type            Size    Used    Priority
/dev/ubdb                               partition       131064  0       -1

気が変わって/dev/ubdbをext3ファイルシステムとして使いたくなったら、次のようにすればよい。

[root@localhost ~]# swapoff /dev/ubdb
[root@localhost ~]# mkfs.ext3 /dev/ubdb
...
[root@localhost ~]# mkdir /mnt/ubdb
[root@localhost ~]# mount /dev/ubdb /mnt/ubdb
[root@localhost ~]# ls -l /mnt/ubdb
total 12
drwx------ 2 root root 12288 Dec 14 02:22 lost+found

仮想ブロックデバイスは、ホストシステムの物理ブロックデバイスを参照することもできる。たとえば、UMLカーネル起動時にubdb=/dev/cdromと指定すれば、仮想マシンからホストシステムのCD-ROMドライブへのアクセスが可能になる。

また、ホストシステム上のtarファイルを仮想マシンのブロックデバイスとして指定することもでき、UMLカーネル起動時にubdb=hello.tarと指定しておけば、仮想マシン内でコマンドtar -xf /dev/udbdを実行することでこのtarファイルの中身を取り出せる。こうすれば、複数のファイルを簡単にホストマシンから仮想マシンに転送することができる。

COW: Copy-On-Write

非常によく似た複数の仮想マシンがあったとする。できれば、それらすべての仮想マシンには同じルートファイルシステムを使わせたい。そうすれば多くの領域を節約できるからだ。とはいえ、こうした考え方ではうまく行かない。それは、すべての仮想マシンがこの共有されたイメージに対して書き込みを行う可能性があり、そうすると他の仮想マシンで問題が起こるためである。では書き込み可能なファイルシステムをそれぞれの仮想マシンに持たせてはどうかと考えたくなるが、そうすると仮想マシンごとにファイルのコピーを作ることになるので、多くのディスク領域が消費されてしまう。それでいて各ファイルの類似性が非常に高く、内容にほとんど違いがない可能性もあるのだ。

UMLは、こうした問題をCopy-on-write(COW)と呼ばれる機能によって解決している。COWの基本的な考え方は、1つの仮想ブロックデバイスを2つのファイルで構成できるというものだ。一方は読み取り専用ですべての共有データを持ち、他方は読み書き可能で個別の変更内容のすべてを格納する。UMLカーネルの起動時にubdb=cowfile,sharedfileと指定することで、cowfileというファイルに変更内容を書き出し、もっとサイズの大きい読み取り専用データはsharedfileを使って共有する、/dev/ubdbデバイスが用意される。ここで留意すべき重要な点は、cowfileにはほとんど中身がないことだ。一見するとsharedfileと同じサイズのように思えるが、ls -lsを実行すれば非常に小さなファイルであることがわかる。

host% ls -lsh cowfile
2.9K -rw-r--r-- 1 marc marc 1.6G Dec 22 16:58 cowfile

1.6GBというサイズが目に入るものの、実はディスク上の占有バイト数がわずか2.9KBに過ぎないことを最初のカラムが示している。COWファイルシステムにアクセスする際のパフォーマンスを比較計測するテストは行わなかったが、理論上はCOWの利用によってパフォーマンスは向上する可能性が高い。ホストシステムは同じデータに対して2つのコピーをキャッシュする必要がなく、物理メモリを節約できるためだ。

COWの詳細については、UMLのWebサイトにある「Sharing Filesystems between Virtual Machines(複数の仮想マシンでファイルシステムを共有する)」のページを参照してもらいたい。

hostfsを使って仮想マシンから物理ホストマシン上のファイルにアクセスする

UMLには、仮想マシンから物理ホスト上のファイルにアクセスする方法がいくつか用意されている。一番簡単な方法はhostfsを使うことだ。ゲスト側で次のコマンドを実行すると、ホストファイルシステム全体が仮想マシン内の/hostとして利用できるようになる。

mount -t hostfs none /host

次はもう少し複雑な例で、ホストファイルシステム上にある特定のディレクトリを1つだけマウントする方法を示している。

mount -t hostfs none /home/marc -o /home/marc

hostfsを用いる方法には制限があるため、そうした制限の一部を解消したhumfsという別の方法も存在する。hostfsとhumfsの詳細については、UMLのWebサイトにある「Host File Access page(ホスト側ファイルへのアクセス)」のページを参照していただきたい。

自作のUMLカーネルを構築する

これまでに紹介したのはコンパイル済みゲストカーネルをダウンロードしてUMLを使用する方法だったが、独自のUMLゲストカーネルを構築することもできる。最近のカーネルバージョンを使うなら、ホストLinuxカーネルをコンパイルするほど難しくはない。

ゲスト側で使うLinuxカーネルのソースコードは、kernel.orgからダウンロードするか、該当するLinuxディストリビューションの関連パッケージをインストールするかして入手する。バージョン2.6.9以降のLinuxカーネルのソースコードを使えば、比較的簡単にUMLゲストカーネルを構築できる。カーネル2.6.9以降にはUMLサポート機能が組み込まれているからだ。

それよりも古いカーネルだと適切なパッチのダウンロードと適用が必要になるので、できるだけ最近のカーネルを使うことをお勧めする。ダウンロードしたカーネルのソースコードは、普通に解凍と展開ができる。通常のカーネルと比べてUMLゲストカーネルのコンパイルが大きく違うのは、makeコマンドにARCH=umを追加しなければならない点だ。たとえば、デフォルトUMLカーネルの設定とカスタマイズの後、UMLカーネルの構築を行うための典型的なコマンド系列は次のようになる。

make defconfig ARCH=um
make menuconfig ARCH=um
make ARCH=um

カーネルの構築が完了すると、「linux」という大きな実行ファイルができる。このファイルが巨大なのは、デバッグ用シンボルを含んでいるためである。デバッガを使う予定がなくてカーネルのサイズを縮小したければ、stripコマンドを使ってデバッグ用シンボルを取り除いても構わない。新しいカーネルを走らせるには、先に説明したように、該当するルートファイルシステムファイルを含むファイルをパラメータubda=の後ろに指定して./linuxを実行すればよい。たとえば次のようになる。

./linux ubda=FedoraCore5-x86-root_fs

UMLカーネルの構築について詳しく知りたければ、UMLのWebサイトにある「Building from source(ソースからの構築)」のページを参照するとよい。

SKAS(Separate Kernel Address Space)パッチについて

たとえ初期のバージョンのUMLでも、Tracing Thread(TT)モードと呼ばれるものを使えばホストカーネルに特別なサポートがなくてもゲストカーネルを実行することができた。ただし、TTモードにはUMLのセキュリティとパフォーマンスに悪影響を与えるという欠点がある。こうした問題を解決するためにUMLの開発者Jeff Dike氏によって実装されたのが、SKAS(Separate Kernel Address Space)モードと呼ばれる優先モード(現在はSKAS3モードとして知られる)でUMLを実行できるようにする、ホストカーネルのパッチである。

しばらくの間はTTモードとSKAS3モードしか選択肢がなかったため、より優れているSKAS3モードのほうが広く受け入れられた。そのため、UMLに真剣に取り組むつもりがあれば、現在はSKAS3パッチとして知られているパッチを使って自分の手でホストカーネルを構築するのが最善だっただろう。TTおよびSKAS3モードについては、UML Webサイトのskasモードのページにもっと詳しい説明がある。

最近になってDike氏が開発したUMLゲストカーネル向けのパッチはSKAS0と呼ばれ、これを使えばUMLゲストカーネルはTTモードよりも優れたセキュリティとパフォーマンスを発揮でき、ホストカーネルにパッチをあてる必要もない。SKAS0はバージョン2.6.13以降のLinuxカーネルでサポートされているため、最近のカーネルを使っていれば特に何もしなくてもSKAS0モードでUMLゲストカーネルを実行できるという恩恵を受けられる。UMLゲストカーネルが本当にSKAS0モードで実行されているかどうかは、UMLゲストカーネルのブート時に出力される情報の最初のほうに次のような表示があるかどうかでわかる。

Checking for the skas3 patch in the host:
  - /proc/mm...not found
  - PTRACE_FAULTINFO...not found
  - PTRACE_LDT...not found
UML running in SKAS0 mode

この表示は、SKAS3モードに必要な機能がホストカーネル内に見つからなかったためにUMLがSKAS0モードの使用に切り換えたことを示している。ただし、この動作はそれほど都合の悪いものではない。パフォーマンスとセキュリティの点でSKAS3モードには及ばないとはいえ、SKAS0モードはTTモードより優れているからだ。したがって、UMLを使って多くの処理を行うつもりがない限り、SKAS3をサポートするカーネルをわざわざ構築する必要はないだろう。Dike氏はSKAS4パッチが登場する可能性にも言及しているので、この記事が読まれるタイミングによってはすでにSKAS4パッチがリリースされているかもしれない。

UMLは急速に発展を遂げつつあるテクノロジなので、残念ながらWebサイト上のドキュメントのなかには最新の情報が反映されていないものもある。具体的な例を挙げると、この記事の執筆時点ではSKASモードのページにSKAS0モードの説明がなかった。この記事が読まれている頃にはドキュメントが更新されていることを願おう。

なお、Dike氏の著書『 User Mode Linux 』とLinux Kernel Mailing List(LKML)宛てのメールにはSKAS0モードの説明がある。

自作のルートファイルシステムを作成する

UMLを使い始めるにあたって一番簡単なのは、http://uml.nagafix.co.uk/から手に入る素晴しいファイルシステムなど、事前に構築されたルートファイルシステムをダウンロードしてくることだ。だが最終的には、自前でルートファイルシステムを作成したくなるかもしれない。

そんな人のためにUML Webサイトの「Creating your own filesystems(自作のファイルシステムを作成する)」のページには、mkrootfs、UML Builder、gBootRoot、rootstrapなど、ルートファイルシステムの構築に役立つ多数のツールが紹介されている。

UMLゲストのネットワークを構築する

UMLゲストのためのネットワーク構築が必要になることもあるだろう。その方法はいくつかあり、詳細はネットワークの設定によってかなり違ってくるが、最も容易なのは、ホストのTUN/TAPインタフェースに接続された仮想的なeth0インタフェースをゲスト側に作成することだ。

ホスト側のLinuxカーネルがすでにTUN/TAPをサポートしていれば(あるいはmodprobe tunを実行してロードされるtun.koカーネルモジュールが存在すれば)、UMLのtunctlというユーティリティを使ってホスト上にTAPインタフェースを作成し、利用しているネットワークのIPアドレスをifconfigによって設定できる。

ゲスト側の設定を行うには、eth0=tuntap,,,<ホスト側TAPインタフェースのIPアドレス>という指定をUMLのコマンドラインに追加する。これにより、UMLゲストにeth0インタフェースが作成される。また、uml_mconsoleというユーティリティを使ってeh0インタフェースのホットプラグを行うこともできる。

UMLゲストのeth0インタフェースにはIPアドレスの割り当て(またはDHCP経由での取得)が必要なので、たいていの場合はrouteコマンドを使ってこの新しいインタフェースを通るデフォルト経路を設定することになるだろう。またネットワークの設定によっては、ホスト側のIPフォワーディングの有効化やiptablesによるフォワーディングルールの設定が必要になる場合がある。

UMLゲスト上でXプログラムを実行する

UMLゲスト上でXのプログラムは実行できないのかという質問がそろそろ出て来そうだが、その答えはイエスだ。ネットワークの設定が終わったら、UMLゲストのDISPLAY環境変数の設定により、Xのプログラムをゲスト側で実行してホスト側のXサーバ上に表示させることができる。これで満足できれば結構だが、ゲスト上でXのプログラムをいくつか実行するだけでは飽き足らず、GNOMEやKDEのような完全なデスクトップ環境を使いたいという人もいるだろう。実は、それも可能だ。UMLゲスト上でXnestを実行すれば、ゲスト側の表示内容をホストのXサーバに表示することができる。Xnestはホスト側Xサーバのクライアントとして動作すると同時に、ゲスト側仮想マシンのプログラムにとってはXサーバとして動作する。したがって次のようにすれば、ゲスト側でGNOMEデスクトップを実行し、ホスト側のウィンドウでそのデスクトップを見ることが可能だ。

%guest Xnest :1 &
%guest gnome-session --display=:1 &

まとめ

UMLは、完全に独立した仮想マシンの作成を可能にする。つまり、各仮想マシンを互いに分離された形で実行し、それぞれに個別のLinuxカーネルを持たせることができるのだ。サーバプロセスどうしを相互にかつ物理マシンからも分離し、セキュリティ性を高めるためにUMLを利用してもよい。あるいは、他の開発またはプロダクション環境の模倣や、さらには仮想マシンに対するrootのアクセス権をユーザに与えることさえできる。また、システムを破壊してしまうのではないかという心配なしに新しいソフトウェアを試すことも可能だ。

NewsForge.com 原文