ディスクレスクライアントを作ろう

最終目標は?

24時間電源を入れてFreeBSD(98)を動かしています。 動かしているのは PC-9801FA で、とっても静かなマシンです。 そのため、ハードディスクの回転音が目立ちます。 また容量も200MBと小さいものを使っています。 SS2も動かしているので、FreeBSD(98)がディスクレスクライアントになれば 静かになるはずです。ついでに、PCで動くFreeBSDとディスクが共有できれば 便利になります。

したがって、最終目標はPCとPC-98x1で出来るだけディスクを共有する ディスクレスクライアントを作ることです。

OSは何にするか?

FreeBSDを使うことまでは決めていますが、バージョンをどうするかという問題が あります。 まず、2.2.8で試し、つぎに3.1を試すことにします。

どうやって起動するか?

起動するには、 といった方法があります。特徴はそれぞれ、 となります。ここでは、フロッピーにBOOTPをサポートしたカーネルを用意し、 そこから起動することにします。

ブートフロッピーを作る

必要なネットワークドライバを組み込んだカーネルを用意します。 BOOTPをサポートするために、configファイルには、
options         BOOTP           # Use BOOTP to obtain IP address/hostname
options         BOOTP_NFSROOT   # NFS mount root filesystem using BOOTP info
options         "BOOTP_NFSV3"   # Use NFS v3 to NFS mount root
options         BOOTP_COMPAT    # Workaround for broken bootp daemons.
を書いておきます。 またフロッピーから起動した時、userconfigの情報は覚えてくれない (NFSマウントしたrootディレクトリに情報を書いてくれても起動時には読めない) ので、I/Oアドレスなどは使用するデバイスに合わせておきます。 カーネルが出来たらkzipで圧縮しておきます。
	kzip kernel
フロッピーへの書き込みは次のようにして行ないました。
	disklabel -w -r fd0 fd1440
	disklabel -B -b /usr/mdec/fdboot fd0
	newfs /dev/rfd0c
	mount /dev/fd0c /mnt
	cp kernel.kz /mnt/kernel
	umount /mnt
コピー時に名前を変えているのは、bootが見るのが kernel であるためです。 同じカーネル(kzipしていないもの)をNFSサーバに用意するrootディレクトリに コピーしておきます。フロッピーのkernelと、rootディレクトリのものが 一致していないと、ps -aux, dmesgなどが使えなくなるので注意が必要です。

サーバを用意する

BOOTPサーバ

ここでは、NetBSD/sparc 1.3.2を使いました。man bootpd を見るとbootpdは デーモンとして常に動かしておく方法と、inetdから起動する方法があるようです。 ここではinetdから起動することにしました。/etc/services, /etc/inetd.confに 必要な記述があることを確認します。つぎに、BOOTP でディスクレスクライアントに 渡す情報を記述します。/etc/bootptabに、
oliv:\
        :ht=ether:\
        :ha=000006001b1a:\
        :sm=255.255.255.0:\
        :hn:\
        :ds=10.93.63.2:\
        :ip=10.93.63.97:\
        :gw=10.93.63.2:\
        :rp="10.93.63.51:/disk2/diskless/oliv-root":\
        :vm=rfc1048:
といったように書きます。各フィールドの意味は書きませんが、 root partitionのフィールドにはNFSマウントのため`:'が含まれています。 これがフィールドの区切りと重なるので、" "で囲んでいます。

NFSサーバ

ここでは、NetBSD/sparc 1.3.2を使いました。 面倒だったので少し乱暴ですが、動いている FreeBSD 2.2.8-RELEASE マシンから コピーして用意しました。/etc/exportsにはディスクレスクライアントからアクセス 出来るようにしておきます。またrootアクセスも許しておきます。 ディスクレス用の、etc/fstab, etc/rc.confなどを適切に直しておきます。 swap領域については、とりあえずなしにしてみます。

試してみる

試してみると、486DX2-66 + 16MB + 3c589 で、swapなしだととっても遅い。 XFree86のVGAサーバを使うとさらに遅い。Xは、SVGAサーバを 使うように設定し直すと速くなりましたが、 ps aux で見ると、pagedamonがCPUをかなり使っています。swapを
mkdir /swap
dd if=/dev/zero of=swapfile bs=1k count=16000
chmod 600 swapfile
vnconfig -e /dev/vn0c /swap/swapfile
swapon /dev/vn0c
と追加してみると、だいぶ速くなりました。/etc/rc.confに追加しておきます (あとで、この部分は変えました)。
swapfile="/swap/swapfile"
同様にして、 Pentium 133MHz + 64MB + PCI NE2000コンパチで、SWAP 32MBにすると、 結構使えます。

/ディレクトリについては、/etc/fstabを見ない(再マウントしない)ようです。

ところで、試している時に間違って rootディレクトリ2.2.8, usrディレクトリ3.1に してしまったのですが(一見)問題なく動いていました。2.2.8でもELFをサポート しているというのは、こういうことだったのかと思ってしまいました。

共有できるディレクトリできないディレクトリ

おおまかにわけると、/usr, /usr/local, /homeは共有でき、/, /varは 共有できません。またswapファイルも別にする必要があります。

同一アーキテクチャ(i386)

/のうち共有できるディレクトリは、 /, /bin, /dev, /lkm, /proc, /root, /sbin, /stand と /usrなどのマウントポイント です。ただし /kernel は共有できるように工夫する必要があります。

/のうち共有できないのは、 /etc, /tmp, /var です。

/varのうち共有できるディレクトリは、 /var/db/pkg です。

ディスクレスクライアント間でほぼ同じ設定で動かすことを考えます。 /etcのうち問題になりそうなのは、 rc.conf, fstab, XF86Config です。fstabは、/は再マウントされないようなので、共有したくない /varが問題です。

rc.confはhostname, ifconfigが問題になりますが、hostnameは BOOTPで設定されているので、rc.confを見ないようです。 ifconfigは、rc.confで記述しないのが一つの手です。2つ目のインターフェース がある場合には問題になります。 keyboardの配列やマウスの接続先も問題になりそうです。 そこで、rc.confの最後に、

if [ "`hostname -s`" ] ; then
	if [ -f "/etc/rc.conf.`hostname -s`" ]; then
		. /etc/rc.conf.`hostname -s`
	fi
fi
を追加して、/etc/rc.conf.hostnameを使えるようにしました。これで rc.confは共通の設定(ディスクのついているサーバ用)、 ディスクレスクライアント1用の設定は rc.conf.client1 で上書きと することが出来ます。

NFS上のswapは元々の/etc/rcの順序(NFS mountする前にswaponする)では、 /varにおけません。そこで、/etc/rcに

--- rc.ORG      Tue Oct 21 23:21:08 1997
+++ rc  Mon Mar 22 17:43:14 1999
@@ -122,7 +122,16 @@
        network_pass1
 fi
 
+mount 10.93.63.51:/disk2/diskless/`hostname -s`-var /var
+mount 10.93.63.51:/disk2/diskless/`hostname -s`-tmp /tmp
+
 mount -a -t nfs
+
+# Add additional swapfile, if configured.
+if [ "x$nfsswapfile" != "xNO" -a -w "$nfsswapfile" -a -b /dev/vn0b ]; then
+       echo "Adding $nfsswapfile as additional swap."
+       vnconfig /dev/vn0b $nfsswapfile && swapon /dev/vn0b
+fi
 
 # Whack the pty perms back into shape.
 chmod 666 /dev/tty[pqrsPQRS]*
という変更を加えました(ディスクのついたマシンのことは考えていません)。 これで、/varに swapファイルを置くことが出来ます。 NFS上のswapファイルの位置は、nfsswapfileに書くことにしています。

XF86Configは、/etc/XF86Configではなく、 /usr/X11R6/lib/X11/XF86Config.hostname にすればマシン毎に分けられます。

/usrのうち共有できないのは、/usr/X11R6/bin/X, /usr/local/etc/rc.d です。どちらも、すべてのクライアントで同一とする(できる)なら そのままで大丈夫です。

異アーキテクチャ(i386とpc98)

i386とpc98で違う(98binを展開すると追加、入れ換えとなる)のは、
/boot
/kernel,
/dev/MAKEDEV(/dev),
/etc/mtree, /etc/make.conf, /etc/disktab,
/lkm(msdos_mod.o, blank_saver_mod.o, fade_saver_mod.o, green_saver_mod.o,
snake_saver_mod.o, star_saver_mod.o)
/modules
/sbin/fdisk
ここまでは、/ パーティションです。/ はi386, pc98で共有しないことにします。 /usr の下は、
/usr/bin/(col, more, talk)
/usr/sbin/(config, fdformat, pnpinfo, tzsetup, vidcontrol)
/usr/include/libdisk.h
/usr/share/misc/more.help
/usr/mdec/
/usr/lib/(kztail.o, kzhead.o, libdisk.a)
とXのサーバです(3.3R では /usr/sbin/config は共通, /usr/mdec も共通 ?, /usr/lib/kztail.o kzhead.o はなし)。 ソースについても98用の追加変更がありますが、 基本的には #ifdef で括られているはずなのと、 /usr/src98/sys などをつくればよいので、とりあえず考えません。

col, more, talkは8ビット透過になっているだけのはずなので 入れ換えてしまってもいいはずですが、 /usr/bin, /usr/sbin はwrapperをつくることにします。/usr/bin/pc98, /usr/bin/i386にバイナリを入れ、アーキテクチャによってよびだすものを 変えるスクリプトを用意します。 /usr/sbinも同様にします。/usr/bin用は、このようにしました。

#!/bin/sh

BASENAME=`basename "$0"`
BASEDIR=/usr/bin
SYSCTL=/sbin/sysctl

if [ "`sysctl -n hw.machine`" = "pc-98" ]; then
	exec $BASEDIR/pc98/$BASENAME $@
else
	exec $BASEDIR/i386/$BASENAME $@
fi

/usr/share/misc/more.help は実は同じもの(2.2.8では)なので問題ありません。 /usr/include/libdisk.h は、#ifdef で括られているので 98bin に 含まれているものに入れ換えてしまいます。

/usr/mdecは、98用のバイナリを入れたディレクトリを上からmountします。

kztail.o, kzhead.oは、kzipに必要です(kzhead.oは同じようです)。 libdisk.aは関係するものを コンパイルしなければいいはずです。/usr/libは結構大きいので できれば共有したいところです。

/usr/share/{98readme, FAQ.98, handbook.98}もコピーしておくと便利です。