オフィシャルサイト: Qumranet - Kernel Based Virtual Machine

KVM

移転しました

独自ドメインサイトへ移行しました。5秒後に

https://straypenguin.winfield-net.com/

へジャンプします。

KVM = Kernel-based Virtual Machine の中核は Linux カーネルのローダブルモジュールで、QEMU と協力してホストOS上で仮想マシンを実行することができる。Xen と異なるのは、XenNemesis という異種OS をハイパーバイザとし、ホストOS はその上で動く特権ゲストと位置づけられるのに対して、KVM では Linux ホストOS 自体がハイパーバイザとなり、個々のゲストプラットフォームが各々ひとつのプロセスとして実行される点だ。また、Xen がフルバーチャライゼーション (完全仮想化) とパラバーチャライゼーション (準仮想化) の両方を提供しているのに対して、KVM はフルバーチャライゼーション専用。そのため、ゲストOS のカーネルを Xen 用に改変してハイパーバイザとの話し方を教えてやるというようなことが必要ない。完全仮想化によるオーバーヘッドは QEMU だけでは確かに無視できないレベルなのだが、KVM と併用する QEMU (qemu-kvm) は /dev/kvm デバイスを通じてハイパーバイザと遣り取りをするようチューンナップされたものなので、オーバーヘッドはかなり低減されている。このあたりの記事なら、IBM の `Discover the Linux Kernel Virtual Machine' (英語) / 「Linux カーネル仮想マシンを探る」(日本語) がよくまとまっている。

また、更に最近になって、virtio という仕組みが導入された。KVM ではこれまで、例えば NIC なら RTL8139 だったりドライブのバスなら IDE だったりと、実在する一般的なハードウェアをエミュレートしてゲストOS に提供し、ゲストOS はそれを仮想ハードウェアだとは知らずに使用していた。しかし、virtio では、ゲストOS に virtio ドライバをインストールすることによって、NIC やディスクなど一部のデバイスではあるが Xen の準仮想化のように高速なアクセスが可能となった。そうしたドライバのことを 準仮想化ドライバ (Para-virtualized Driver) と呼んでいるようだ。

ホストOS としての検証は、以下のディストリビューションで行なった:

Table of Contents

ホストOSの準備

ハイパーバイザとなるホストOS 及びハードウェアの要件などについて述べる。

ハードウェア

KVM は CPU の備える仮想化構造 (x86仮想化) を利用しているため、マシンの CPU が x86仮想化機能を持ち、チップセット、BIOS ともそれを活用できるものでなければならない。搭載メモリ量は、ゲストに分け与える分を勘案すると最低 2GB、複数のゲストを載せるなら 4GB 以上を実用上の最低ラインと考えるべきだろう。

カーネル

KVM + QEMU を実行するのに Xen カーネルのような特別なカーネルは必要ない。KVM コードの採用されたカーネルが動作していればOKだ。KVM のコードは Linux カーネル 2.6.20 からメインストリームにマージされた。ただし、RedHat 系では、2.6.18-x 台の途中から、KVM パッチを適用したカーネルパッケージが提供され始めている。Fedora Core 9 では元来 2.6.20 以降なので問題なし。RHEL 5/CentOS 5 では、後述の KVM パッケージのインストール時に依存関係がチェックされて、必要であれば KVM パッチ適用済みのカーネルもインストールされる。

ゲストへ virtio によるディスクやネットワークの提供を行ないたい場合には、Virtio のオフィシャルページによると、ホストOS にはカーネル 2.6.25 以降と KVM Ver.60 以降が必要とある。ただし、カーネルバージョンはそれ以前でも、RHEL/CentOS の 5.4 (kernel-2.6.18-164) 、Fedora 9 のいずれも、virtio の提供が可能だった。Fedora 10 は kernel-2.6.27 なので当然対応している。

ホストOS を 32ビット版にするか 64ビット版にするかは、載せるゲストをどちらにするかによって計画しておく必要がある。KVM 用でない素の QEMUPC アーキテクチャ版実行プログラム (qemu-system-x86_64) は、基本的に 32ビットと 64ビットの区別がなく、32ビットホストの上で 64ビットのゲストプラットフォームを提供することも可能だが、KVM 用の QEMU (qemu-kvm) では、64ビットゲストは 64ビットホスト上でしか動かすことができない。また、物理メモリ量との兼ね合いから言えば、64ビットカーネルなら自ずと 4GB 以上の物理メモリを認識できるのに対して、32ビットでは PAE 版のカーネルが必要となることも忘れてはいけない。結論的には、x86仮想化機能を搭載した CPU はそのほとんどが 64ビットであることからして、ホストOS は 64ビット版にしておくほうが妥当だろう。

なお、ホストOS のインストール時に、筆者の好みでは 仮想化 パッケージグループは選択しない。今のところ、Xen のコンポーネントも一緒に根こそぎインストールされてしまうようだからだ。

パーティション

ゲストに何をハードディスクとして提供するかは、Xen と同様に主に3つの方法がある。ひとつのディスクイメージファイルをひとつのディスクとしてエミュレートする方法、ディスクパーティションをディスクに見せる方法、LVMLV をディスクに見せる方法だ。

イメージファイルを利用する場合は、/var/ 配下に格納するので、/var は別パーティションとして切り、サイズもたっぷりととる。

ディスクパーティションをゲストディスクとして使う場合は、インストール時にはそれらパーティションを切らずに、空き領域として残しておく。インストール時に切ってしまうとフォーマットさせられたりマウントされてしまったりと面倒だ。

LVM を利用して LV をゲストディスクとして見せる場合は、後で作る PV 用に、空き領域を残しておく。インストール中に PVVolumeGroup だけ作成 (LV は作らない) しておくという手もある。

ネットワーク基本設定

とりあえず、ホストOS を単独で使用する時と同様に設定しておく。少々解説が必要なので別項とした。

パッケージ

依存関係で悩まずにすむように、基本的に yum を使って追加インストールを行う。

必須パッケージ
パッケージ コメント
kvm kvm カーネルモジュールと、kvm 用にコンパイルされた qemu (/usr/libexec/qemu-kvm) など。
virt-manager ゲストOS のインストールや定義、削除、起動/停止などを行う GUI アプリケーション。yum でインストールすれば、libvirt, xen-libs, bridge-utils, qemu-img なども引きずられて入る。ディストリビューションによっては、kvm には必須でない qemu パッケージ (/usr/bin/qemu-system-x86_64 等) もインストールされる。
xen-runtime Fedora Core にはあるが RHEL/CentOS 5.x には存在しない。シェルスクリプトなど、Xen を動かす時に使われるツール群で、Xen カーネルや xend デーモンは含まれない、いわば xen パッケージのミニセット。仮想ネットワーク (ブリッジ) をパートタイム的に使いたい場合には、/etc/xen/scripts/ 下のスクリプト群があると便利なのでインストールする。ブリッジを恒久的に作っておく方式なら不要だ。こうしたスクリプトは RHEL/CentOS 5 では xen 本体パッケージにしか含まれていない。
オプショナルなパッケージ
パッケージ コメント
tunctl 後で出てくる TAP ネットワークデバイスを操作するユーティリティコマンド。実際には QEMU が内部的に面倒を見てくれるのでどうしても要るというわけではないが、研究のために入れておくとよい。
virtio-win RHEL 5.4 にのみある。Windows ゲストのための virtio ドライバ集。2010/1/31 時点では、Windows Server 2003 と 2008 (ともに i386 及び amd64) のブロック(ディスク)ドライバとネットワークドライバが含まれている。Windows Server 2008 64ビット用のドライバはちゃんと署名されている。詳しくは後述
virtio Windowsドライバ ホストが CentOS や Fedora Core の場合は、KVM のサイトから最新の virtio ドライバを入手すべし。2010/1/31 現在、ブロックドライバは Vista/7/2003/2008 の x86 及び amd64 と、XP の x86。ネットワークドライバは Vista/7/XP/2003/2008 の x86 及び amd64 と、Windows 2000 用が含まれている。ただし、いずれのドライバも署名はされていない。これらと同じドライバを ISO イメージにまとめたものを、"/contrib/famzah" というサイトが提供してくれている。詳しくは後述

上記のもの、特に kvmlibvirt をインストールしたら、次の作業に取りかかる前に、kvm カーネルモジュールをロードさせたり libvirtd を起動させるために、ホストOS を一度リブートさせる。

その他

仮想ネットワークの構築

ふたつの手法

基本的には Xen で利用する仮想ネットワークと同じで、QEMU/KVM でも、ゲストの仮想ネットワークインターフェイスはホストOS のブリッジを介してホストや外界と通信を行なう。ただし、QEMU/KVM は、仮想プラットフォームの生成時に TAP デバイスというものを作成してブリッジにつなぎ込んでくる仕組みになっている (TAPの説明は TUN/TAP - Wikipedia, the free encyclopediakernel.orgのtuntap.txt 参照のこと)。

当ページでは、libvirt を併用するので、libvirtd によって作成される仮想スイッチと、ホストOS とも外部とも同サブネットで遣り取りできるブリッジの、どちらも利用できる。これも、Xen でやったのと同じだ。図式しておこう。

例として、ふたつのゲストOS があるとしよう。

図のゲスト#1 は libvirtd の仮想スイッチに接続させており、仮想スイッチ virbr0 の NAT 機能を介してマシン外界と通信ができる。ただしこちらの場合、ゲスト -> 外界方向のコネクションは成立するが、そのままでは外界からゲストに向かって接続をイニシエートすることはできない。ホストOS との通信は両方向ともできる。

ゲスト#2 では、物理デバイスへとつながる単純なブリッジをホストOS 上にあらかじめ作成しておき、ゲストをそこへ接続させている。この方法だと、ホスト上の eth0 ブリッジは単純なハブのように機能するので、ゲスト#2 からホストや外界へはもちろん、外界からゲストへのコネクションも簡単に成り立つ。左図で、ホストOS 側の物理デバイスが peth0、ブリッジが eth0 という名前になっているが、これらの名前は、xen-runtime パッケージの提供する network-bridge スクリプトを利用する (パートタイムなブリッジ) か、ネットワークインターフェイススクリプト (ifcfg-*) を使って恒久的なブリッジを作るかによって違ってくる 。

上記は RedHat 系の OS で提供されている仮想ネットワークのフレームワークを紹介しただけであり、例えばノード内だけで通信可能な閉鎖系 LAN を追加したり、いろいろと応用が利く。それらについては既出の内容と重複するので、Xen のページの「Xenネットワーク」や「Xenネットワーク クイックレシピ」の節を参照していただきたい。libvirtd の起動時に自動的に作られる virbrX 仮想スイッチについても、設定方法は Xen の時と変わらないので、そちらを見ていただこう。ここでは、ちょっと工夫の要る単純ブリッジの作成方法についてのみ述べる。

単純ブリッジの作成

やり方としては、通常の eth0 などと同様に sysconfig/network-scripts/ifcfg-* を整備して恒久的なブリッジを作る方法と、使いたい時 (つまりゲストを起動したい時) だけブリッジを生成するパートタームな方法とがある。通常は前者の恒久的な方法がいいだろう。

恒久的なブリッジの作成

RHEL/Centos 5.4 及び Fedora Core 10 の initscripts では、ブリッジの作成と、それへの実インターフェイスの組み込みが ifcfg-* ファイルでできる (RHEL/CeontOS 5.3 以前や FC9 以前では未検証)。ここでは、ブリッジ br0 を生成し、実インターフェイス eth0 とつなげる例を示す。やるべき事は、/etc/sysconfig/network-scripts/ ディレクトリ下の ifcfg-br0 の新規作成と、ifcfg-eth0 の修正のふたつだ。

ifcfg-br0

# Bridge interface 0
DEVICE=br0
TYPE=Bridge
ONBOOT=yes
BOOTPROTO=static
IPADDR=192.168.0.5
NETMASK=255.255.255.0
DELAY=0

ifcfg-eth0

DEVICE=eth0
HWADDR=00:1f:e2:0c:78:1b  <--もともと書いてあったなら残したまま
ONBOOT=yes
BOOTPROTO=none
#IPADDR=192.168.0.5       <--コメントアウトするか削除
#NETMASK=255.255.255.0    <--コメントアウトするか削除
BRIDGE=br0                <--接続するブリッジ

あとは `service network restart' すればインターフェイスが出来上がる。ただしひとつだけ注意がある -- この設定以後、ホストマシンのゲートウェイデバイスは eth0 ではなく br0 になるので、サーバデーモンなどの設定ファイルでインターフェイス名を指定してあるものは、eth0br0 に書き換えなくてはならない。

パートタイムなブリッジを使う

物理インターフェイス eth0 を出入り口とするブリッジを、必要な時にだけ手動で生成する方法について述べる。この方法だと、いつでもまた元の物理 eth0 の状態に戻すことができる。ただし、RHEL 5.4 ホスト上ではこの方法はうまくいかなかった。Fedora Core 9/10 では問題なく動いている。

既に何度か言及したが、この方法では、xen-runtime パッケージによって提供される /etc/xen/scripts/network-bridge スクリプトを活用する。このスクリプトについては Xen のページでさんざん述べたのでそちらも参照していただきたい(特に "Fedora Core 8 における仮想ネットワーク" という章)。RHEL/CentOS 5.x にはそういうパッケージはなく、同様のスクリプト群は xen パッケージ本体にしか含まれていない。どうしてもインストールしたければ xen パッケージをインストールし、依存関係で入る kernel-xen を `rpm -e --nodeps' でアンインストールし、xend サービスと xendomains サービスをスタートアップから外すという手順で配備は可能だが、どうやら、RHEL/CentOS 5.x の network-bridge は、ホスト上で Xen が稼働し vethX があらかじめ存在していないと機能しない仕様のようだ。

network-bridge は下記のようなことをやっている。Fedora Core 8 以降の新型network-bridge と、RHEL/CentOS 5.x の旧型network-bridge とでは動作が異なり、下記は新型の動き;

  1. 一時的なブリッジ tmpbridge を作る。
  2. 物理インターフェイス eth0 を停止する。
  3. eth0 の MACアドレスと IPアドレスを tmpbridge にコピーする。
  4. eth0peth0 に改名する。
  5. tmpbridgeeth0 に改名する。
  6. peth0eth0 ブリッジに組み込む。
  7. それらを活性化させる。

これを確実にコントロールするため、やはり Xen のページ (特に "Xenネットワーク クイックレシピ" の "単純ブリッジとLAN用L3スイッチ(非NAT)" の項) で述べた、ラッパースクリプトセットを使う。

最小限の準備としては、

下記に、network-bridge-custom の内容を示す。より詳しい解説は "単純ブリッジとLAN用L3スイッチ(非NAT)" を参照のこと。

#!/bin/bash
# network-bridge-custom 0.3.0
# Use with network-bridge-util > 0.3.0
 
dir=$(dirname "$0")

# Resolv symbolic link for $dir
if [ -L $0 ]; then
    link=$(ls -l $0 |cut -d' ' -f10)
    if grep -q / <<<$link ; then
        linkdir=${link%/*}
        if [ -z "$linkdir" ]; then
            dir=
        elif [ x${linkdir%%/*} = x ]; then
            dir=$linkdir
        else
            dir=${dir}/$linkdir
        fi
    fi
fi

. ${dir}/network-bridge-util
 
## Auto-detect VirNet variables.
set_virnet_vars

function do_bridge () {
    ## This is the very heart of network-bridge script.
    ${dir}/network-bridge "$@" bridge=eth0 netdev=eth0 antispoof=no
   #${dir}/network-bridge "$@" bridge=eth1 netdev=eth1 antispoof=no
}

case "$1" in
  status)
    do_bridge "$@"
    exit
    ;;
  *)
    ;;
esac

## Iptables optimization.
optimize_ipt
 
## This is the very heart of network-bridge script.
do_bridge "$@"

## Disable kernel IP forwarding if there is no NAT bridges.
disable_ipfwd

## Additional routing configuration.
set_route /etc/xen/route.conf
#sysctl -w net.ipv4.conf.eth1.rp_filter=1 &>/dev/null

以上の準備が整ったら、Xen の場合に xend が起動時にやっていたコールを、手動で唱えてやる。

root# /etc/xen/scripts/network-bridge-custom start

これで、前出の図に示した ホスト側の構造が出来上がる。ただし、tapX デバイスは実際に QEMU/KVM ゲストを立ち上げる際に作られるのでまだない。
逆に、peth0 を本来の eth0 に戻したくなった時には、

root# /etc/xen/scripts/network-bridge-custom stop

と唱えてやればいい。また、

root# /etc/xen/scripts/network-bridge-custom status

とコールするとブリッジ関係のステータスが表示される。

"/etc/xen/scripts/network-bridge..." というのは度々手動でコマンドするには長すぎる...。私と同意見なら、上記 network-bridge-custom ファイルへのシンボリックリンクだけを、PATH の通った場所 (例えば /usr/local/bin) に作ればいい。シンボリックリンクの名前も実体と異なって構わない。

パートタイムなブリッジの自動起動の試み

上記の network-bridge-custom を OSブート時に SysV Init から呼べないかと思い、起動スクリプトを書いてみた。

これを /etc/init.d/ にコピーして実行ビットを立て、`chkconfig --add bridgectl' で各runレベルに登録すれば、一応機能することは確認した。だたし、起動順が曲者。network-bridge-custom が完璧に機能するためには libvirtd よりも後に起動されなければならない。しかし libvirtd の元々の起動序列は 97 と遅く、終了序列は 03 とかなり早いのだ。この bridgectl スクリプトでは起動順 98、終了順 02 にしてあるが、本当は libvirtd の起動順をもう少し早く、終了をもう少し遅く調整すべきかもしれない。さらに、後述する virtdomains (ゲストドメイン自動起動/終了スクリプト) を装備する場合、bridgectlvirtdomains より先に起動していなくてはならず、順序的にかなりタイトになってしまう。