ejzailを使ったVIMAGE対応jailの作り方

背景

NFS サーバのメモリが余っているのが勿体無いので jail 環境を構築しようと思った.
しかしながら,通常の jail 環境の作り方では,ホスト OS の NIC に jail 環境用 IP アドレスを alias する必要があり,NFS は受け待ち IP アドレスを指定できないため,jail 環境用 IP アドレスで NFS サービスを待ち受けてしまう.これは意図しないことなのでよろしくない.

ということがわかったのでツイートしたら,VIMAGE でいけるよ,と教えてもらったので ezjail を使って VIMAGE 対応 jail を作ってみた.


(追記)
nfsd に -h [bindip] というオプションがあるらしい.これを使えばよかったかもしれない.

KVM との比較

KVM だと vnet インターフェースを自動で生成してくれるし,さらに libvirt を使えばブリッジまで行なってくれるが, jail や ezjail ではそこまでやってくれない.
vnet インターフェース相当である epair インタフェース(epair0a, epair0b の様なペアのインターフェースができる)を作って片方をゲスト OS に持って行けば,ホスト OS とゲスト OS をブリッジのように繋がる.また,ホスト OS 側の epair と外に繋がるインターフェースをブリッジすることでゲスト OS は外と繋がることができる.

ezjail のメモ

  • ports は sysutils/ezjail
  • 設定ファイルは /usr/local/etc/ezjail.conf
  • デフォルトだと ezjail のルートは /usr/jails
  • /usr/jails/basejail にバイナリがある.
  • /usr/jails/newjail は各 jail のスケルトン.ツリー構造が重要.バイナリの実体は /usr/jails/basejail.必要なものだけをシンボリックリンクで持ってくる.
  • /usr/jails/flavour は各 jail 毎に設定ファイル等を上書きするのに使う.puppet と連携させると便利と思った.
  • jail の作り方:ezjail-admin create -f [flavour] [name] 0.0.0.0 (アドレスは最終的に消すので適当でOK)
  • 各 jail の設定ファイル(各 jail のルートディレクトリや IP アドレス等):/usr/local/etc/ezjail/[name]
  • jail 環境を作ったらすること
    • /etc/fstab.[name] を編集./usr/ports, /home をマウントするようにする.
    • /usr/local/etc/ezjail/[name] を編集.IP アドレスを消す.後は後述の変更をする.

問題点と課題の整理

ezjail のデフォルトでは以下3点の問題点があるために ejzail だけを使った VIMAGE 対応 jail の作成が出来ない.

  1. VIMAGE は対応 jail にするには jail に -c vnet オプションを渡す必要があるが,/etc/rc.d/jail は -c オプション指定時のフォーマットに対応していない*1ため,素の /etc/rc.d/jail は使えない.
  2. デフォルトの devfs のマウント方法は VIMAGE 対応を前提としていないため,jail 環境に /dev/*mem がなく,jail 環境でルーティングが出来ない.
  3. 通常の jail とは違って起動時に IP アドレスを割り当てない.そのためゲスト側で IP アドレス設定,ルーティング設定,サービス(sshd等)実行を jail 環境を立ち上げた後に行う必要がある.

ここでは課題としては以下の3点をあげた.

  1. /etc/rc.d/jail で -c vnet オプションを使えるようにして VIMAGE 対応 jail を作成できるようにする.
  2. jail 環境で /dev/*mem を使えるようにする.
  3. jail 環境が起動した後に epair インターフェースを渡して,IP アドレスの設定,ルーティングの設定,サービスの立ち上げを行うようにする.

以下で3つの課題に対する解決手順をまとめる.

解決策(1つ目):/etc/rc.d/jail で VIMAGE 対応 jail を作成できるようにする

まずは jail に渡すオプションを/etc/rc.conf に設定する.

  • /etc/rc.conf
jail_flags="-c vnet"

次にこのオプションを解釈できるように /etc/rc.d/jail を書き換える.
デフォルトでは -c オプション無しなのでスペース区切りで path hostname [ip[,..] ] command を渡しているが,-c オプション指定時はparameter=value形式で指定する必要がある.

  • /etc/rc.d/jail
> diff -u /etc/rc.d/jail /usr/src/etc/rc.d/jail
--- /etc/rc.d/jail	2011-05-06 00:18:19.000000000 +0900
+++ /usr/src/etc/rc.d/jail	2010-12-22 02:09:25.000000000 +0900
@@ -635,10 +635,8 @@
 			i=$((i + 1))
 		done
 
-		eval ${_setfib} jail -J ${_tmp_jail}_ ${_flags} host.hostname=${_hostname} name=`echo ${_hostname} | cut -f 1 -d .` \
-			path=${_rootdir} persist
-		cat ${_tmp_jail}_ | cut -f 1 -d " " | cut -f 2 -d = > ${_tmp_jail}
-		rm -f ${_tmp_jail}_
+		eval ${_setfib} jail ${_flags} -i ${_rootdir} ${_hostname} \
+			\"${_addrl}\" ${_exec_start} > ${_tmp_jail} 2>&1
 
 		if [ "$?" -eq 0 ] ; then
 			_jail_id=$(head -1 ${_tmp_jail})
@@ -699,7 +697,9 @@
 					eval env -i /usr/sbin/jexec ${_jail_id} ${_exec_stop} \
 						>> ${_consolelog} 2>&1
 				fi
-				jail -r ${_jail_id}
+				killall -j ${_jail_id} -TERM > /dev/null 2>&1
+				sleep 1
+				killall -j ${_jail_id} -KILL > /dev/null 2>&1
 				jail_umount_fs
 				echo -n " $_hostname"

解決策(2つ目):jail 環境で /dev/*mem を使えるようにする

devfs をマウントするときにどのデバイスを使えるようにするかは /etc/default/devfs.rules に書いているルールに従っている.どのルールを使うかは /usr/local/etc/ezjail/[name] の変数 jail_[name]_devfs_ruleset で指定している.
ルールを見ると devfsrules_unhide_basicにすれば /dev/*mem を使えそうなので, /usr/local/etc/ezjail/[name] を以下のように変更する.

  • /usr/local/etc/ezjail/[name]
    • i.e. /usr/local/etc/ezjail/cattleya02_osaka_remu
export jail_cattleya02_osaka_remu_devfs_ruleset="devfsrules_unhide_basic"

解決策(3つ目):jail 環境が起動した後に epair インターフェースを渡して,IP アドレスの設定,ルーティングの設定を行うようにする

/usr/local/etc/ezjail/[name] の jail_[name]_exec_xxx に設定することで jail 起動時にホスト OS 側,ゲスト OS 側で任意のコマンドを実行出来る.コレを使って epair インターフェースを渡したり,ゲスト OS 側の IP アドレス設定等を行う.
ゲスト OS 側で実行してくれる afterstart がうまく動かなかったので,ホスト OS 側から jexec コマンドを使ってゲスト OS 側の設定をする.以下の内容を /usr/local/etc/ezjail/[name] に追加した.

  • /usr/local/etc/ezjail/[name]
    • i.e. /usr/local/etc/ezjail/cattleya02_osaka_remu
export jail_cattleya02_osaka_remu_exec_prestart0="ifconfig epair0 create"
export jail_cattleya02_osaka_remu_exec_prestart1="ifconfig epair0a up"
export jail_cattleya02_osaka_remu_exec_prestart2="ifconfig bridge0 addm epair0a"
export jail_cattleya02_osaka_remu_exec_poststart0="ifconfig epair0b vnet cattleya02"
export jail_cattleya02_osaka_remu_exec_poststart1="jexec cattleya02 /sbin/ifconfig epair0b 10.3.2.21/24"
export jail_cattleya02_osaka_remu_exec_poststart2="jexec cattleya02 /sbin/route add default 10.3.2.1"
export jail_cattleya02_osaka_remu_exec_poststart3="jexec cattleya02 /etc/rc.d/sshd start"
export jail_cattleya02_osaka_remu_exec_poststop0="ifconfig bridge0 deletem epair0a"
export jail_cattleya02_osaka_remu_exec_poststop1="ifconfig epair0a destroy"

参考

hasegaw blog: FreeBSD VIMAGEを使ったTCP/IPのルーティング デモンストレーション

  • VIMAGE 概要と設定例について参考にしました

http://www.jp.freebsd.org/cgi/mroff.cgi?subdir=man&lc=1&cmd=&man=jail&dir=jpman-5.2.0%2Fman§=8

  • NFS サーバ上で jail 環境で構築するのがなぜよろしく無いのかを参考にしました

First stab at a new jail(8)

  • prestart, poststart の実行環境/タイミングを参考にしました

*1:jail に -c オプションを使うとそれ以外の引数が変わるため,ezjailのスクリプトを書き換える必要がある