FreeBSDのネットワークブート with qemu on FreeBSD
FreeBSDのPXEブートについて、仮想マシンを用いたテスト環境の作成した記録です。
ホストOSがFreeBSD、ブートの対象マシンはqemuの仮想マシンです。そして、ブートするOSはFreeBSDです。
こんな感じ。
+---------+ +----------+ | Host | pxe | VM(qemu) | | |---> boot --> | | | FreeBSD | | FreeBSD | +---------+ +----------+
まず始めに。
PXEの仕組みは、簡単に説明すると以下の通りとなっている。
- NICの中にBIOSが入っており、マザーのBIOSからNICのBIOSに移る
- NICのBIOSがDHCPサーバにリクエストを出す
- DHCPサーバがTFTPサーバのIPアドレスを投げる
- TFTPのサーバからブートローダを読み込み、実行する
今回、FreeBSDをブートするに当たっては
- TFTPで公開するファイルとしてBSDのpxebootというファイルを用いる
- これは、通常のbsdloaderのpxe対応版である
- pxebootはNFSのクライアントとしてもう一度サーバにアクセスする
- NFSで/boot以下が読めるので、ここにあるカーネルとモジュールを読み込み、実行する
- この時mfsrootという、ディスクイメージもダウンロードしてくる
- カーネル起動後、カーネルはHDDをマウントする代わりに先にダウンロードしたmfsrootをルートファイルシステムとしてマウントする
という流れとなります。
qemuの設定
今回実験環境をFreeBSDとしたため、qemuを利用して実験した。
ブートイメージをゲットする
qemuでpxeブートを実現するために、http://rom-o-matic.net/からブートイメージを生成した。
ネットワーク周りの設定
+-----+ +---------+ +------+ | tap |---| 仮想lan |---| qemu | +-----+ +---------+ +------+ (vlan)
上図のような構成にする。
tapが使えるように、カーネルモジュールをロード。デバイスを作る為に、適当にデバイスにリードを発生させる(catなりfileなり何なり)。
# kldload if_tap
# file /dev/tap0
ifconfigでtap0が出来ている事が確認出来たら、tapに適当なipアドレスを振る。今回は192.168.64.1とした。
# ifconfig tap0 192.168.64.1
qemu動作確認
qemuにはカーネルモジュールaioが必要なので、読み込む。
# kldload aio
という事で、ブートするするか確かめる。尚、-cdromの引数には、先に生成したisoイメージの名前を渡す。今回はeb-5.4.4-rtl8139.isoとした。
# qemu -cdrom eb-5.4.4-rtl8139.iso -boot d -net nic,vlan=1,model=rtl8139 -net tap,ifname=tap0,vlan=1,model=rtl8139
tapの設定
以上の手動でtapデバイスを作成する事は可能であるが、nfsサーバの起動時(つまり、OSのブート時)にtapデバイスが無いとnfsdは起動しない。
よって、起動時にtapデバイスが作成されるように以下の設定を行う。
- /boot/loader.confに追記
if_tap_load="YES" aio_load="YES"
tapデバイスを作成するのに必要なカーネルモジュールと、qemuを起動するのに必要なカーネルモジュールが起動時に読み込まれるようになる。
- /etc/start_if.tap0 を作成、以下を記述
file /dev/tap0
tap0の初期化処理が行われる。
これが実行される事により、起動時にtap0が作成される。
- /etc/rc.confに以下を追加。
network_interfaces="lo0 em0 tap0" ifconfig_lo0="inet 127.0.0.1" ifconfig_tap0="inet 192.168.64.1 netmask 255.255.255.0 broadcast 192.168.64.255"
network_interfacesにネットワークインタフェースを羅列する事により、マシンが持っているインタフェースを明示的に示す事が出来る。
これが指定されていない場合は自動検出モードになっているが、実在しない"tap0"インタフェースは検出されないので自動検出モードを使わず、インタフェースを明示する。
この例の場合は左から順に
- lo0: ループバック
- em0: 実在するnic(ここは適宜それぞれの環境によって書き換える)
- tap0: 仮想インタフェース
となっている。
しかし、インタフェースが存在している事を示すだけでは不十分である。ループバックインタフェースのipアドレス、tap0のipアドレスも明示することも必要である。それが"ifconfig_lo0"及び"ifconfig_tap0"の行である。特にループバックインタフェースの事を忘れやすいので注意する。
尚、実在するnic(この場合em0)もdhcpを使わずにipを固定している場合も同様に書かなければならない。
デーモンの設定
dhcpサーバの設定
portsより、isc-dhcp30-serverをインストール。
dhcpd.confは以下の通り。
"option root-path"の項目部分は、bootloaderが必要とするファイルを置く場所を指定。これはnfsで提供する。今回は/net-boot/pxerootとした。
ddns-update-style none; default-lease-time 7200; max-lease-time 7200; subnet 192.168.64.0 netmask 255.255.255.0 { filename "pxeboot"; next-server 192.168.64.1; # tftp server option broadcast-address 192.168.64.255; range 192.168.64.2 192.168.64.32; option root-path "192.168.64.1:/net-boot/pxeroot"; }
以上、dhcpd.confとして適当な場所に置いた後、起動する事を確認をする。今回、このスクリプトは/net-bootに置いた。
# dhcpd -cf dhcpd.conf -lf tmp tap0
# ps aux | grep dhcpd
tftpサーバの設定
ここら辺を参考に、コンパイル。
勿論portsにも入っているが、inetdを使うバージョンがコンパイルされるので非常に使いづらい。
因みに、FreeBSDはデフォルトがbsdmakeなのでgmakeでコンパイル。
$ wget http://www.kernel.org/pub/software/network/tftp/tftp-hpa-0.48.tar.bz2
$ tar jxvf tftp-hpa-0.48.tar.bz2
$ cd tftp-hpa-0.48
$ ./configure --without-tcpwrappers
$ gmake
$ cp tftpd/tftpd ../
$ cd ..
makeが終わったら、起動する事を確認する。
# ./tftpd/tftpd -l -s
# ps aux | grep tftpd
今回、出来たバイナリは/net-boot/tftpdに置いた。
nfsサーバの設定
- rc.conf
# network boot portmap_enable="YES" rpcbind_enable="YES" nfs_server_enable="YES" nfs_server_flags="-u -n 4 -h 192.168.64.1" mountd_enable="YES" mountd_flags="-r" nfsd_enable="YES"
- /etc/exports
nfsで提供するディレクトリを指定。先に書いたdhcp.confの"option root-path"項と同じものにする。
/net-boot/pxeroot -network 192.168.64.0 -mask 255.255.255.0
ここまでの設定を反映するために、一旦リブートをする。
ブート最終準備
ブートに必要なファイル類を揃える
FreeBSDのインストールディスクから、pxeboot及び/bootを持ってくる。
pxebootは任意のディレクトリを作成しそこの中へコピーする。
/bootについては、先に書いたdhcp.confの"option root-path"項に書いたディレクトリにコピーする。
# mdconfig -a -t vnode -f 7.0-RELEASE-i386-bootonly.iso
# mount -t cd9660 md0 /mnt
# cp /mnt/boot/pxeboot /net-boot/pxeboot/
# cp -Rp /mnt/boot /net-boot/pxeroot
デーモン起動
dhcpdとtftpdを立ち上げる。
尚、tftpdの引数には、'pxeboot'をコピーしたディレクトリを指定する。
# dhcpd -cf dhcpd.conf -lf tmp tap0
# ./tftpd/tftpd -l -s ./pxeboot
レッツブート!
# qemu -cdrom eb-5.4.4-rtl8139.iso -boot d -net nic,vlan=1,model=rtl8139 -net tap,ifname=tap0,vlan=1,model=rtl8139
便利に。
毎回デーモン起動の手順を踏むのは非常に面倒なので、以下のようなスクリプトを準備すると非常にお得感が溢れる。
OSの初回起動時にsettings.shを実行し、それ以後はstartqemu.shのみ実行すれば良い。
- settings.sh
#!/bin/sh sudo dhcpd -cf dhcpd.conf -lf tmp tap0 sudo ./tftpd/tftpd -l -s ./pxeboot
- startqemu.sh
#!/bin/sh sudo ifconfig tap0 192.168.64.1 sudo qemu -cdrom eb-5.4.4-rtl8139.iso -boot d -net nic,vlan=1,model=rtl8139 -net tap,ifname=tap0,vlan=1,model=rtl8139
startqemu.shでtap0のipを指定しているが、これはqemuが終了と同時にtap0のipアドレスを初期化してしまうからである。