はじめに
2007年7月、余剰パーツの寄せ集めで作っていた旧サーバを一新。そこで新サーバは、寄せ集めじゃなくホームサーバに適した機能を持たせて運用させることに。テーマは「小型・静音・省電力」。
サーバに求める要件
業務用途ではないので、ハードウェアスペックにはさほどこだわらないことにする。その代わり家庭向けに適した要件を重視。
- 小型: 大きさはノートサイズ程度。横置き、縦置きにはこだわらないが、省スペース化を目指すなら縦置き。
- 静音: ファン類は全て撤去。放熱はヒートシンクで賄う。ファンをなくすには、廃熱のためのケース構造も考える必要がある。
- 省電力: 高速動作等の性能は求めず、消費電力の少ないパーツで構成する。電源もACアダプタにし、同時に電源ファンをなくす。
また、HDDの駆動音や耐久性能を考えるとホームサーバにはあまり適していない。現在、フラッシュメモリからの起動手順、BIOS周りの対応も進んでおり、大容量化と低価格化も進んでいるので今回は1CFブートLinuxを取り入れる。
パーツの選定
色々とパーツを見て回ったが、要件を満たせそうな組み合わせとしては以下の通り。付記している価格は2007年6月に購入した際の参考価格。
- Mini-box M300-enclosure 11,500円(OLIO.SPEC)
- Mini-box picoPSU-80 6,780円(OLIO.SPEC)
- FranMar ACADAPTER 12V-7A 84W 6,780円(OLIO.SPEC)
- VIA EPIA LN-10000EG 15,900円(OLIO.SPEC)
- DDR2 512MB PC2-4200 1,680円(じゃんぱら)
- A-DATA CFカード 120x 4GB 4,980円(じゃんぱら)
- 玄人志向 GIU2V-PCI 3,980円(地元店舗)
マザーボードはmini-ITXタイプを選ぶ。CPUは低発熱かつ省電力で定評のあるVIA製を選択。さらにPCケースとしてMini-boxのM300-enclosureにする。M300は同社製のpicoPSUというDC-DC変換アダプタを使用しなければならないという制約があるが、それによる小型化を評価し、無駄なパーツも省いたシンプルスタイルも好印象だったので決定。加えて、標準でCF->IDE変換アダプタが付属しており、HDDレスなCFブートLinuxを作るには最適だと判断。しかし、この変換アダプタは後々の転送速度問題にも絡んでくる。詳細は後述。
当初、VIAのマザーボードはCNシリーズにしようかと考えていたが、M300-enclosureには規格上適合しにくい模様(同社の製品ページに「Not recommanded for EPIA 800, EPIA 5000 and EPIA CN series because of the different USB header pinout.」とある)。 またルータとしても使用することを考えているので、ギガビットEthernetが付属したPCIカードを装着することにした。
Linuxをインストール
実際の作業風景として記録した写真。
内部にあるケースファンはマザーボードに接続しない(完全ファンレスのため)。インストールには、手持ちのIDE型CDドライブを強引に接続。Vine Linux 4.1を最小構成でインストール。後述するRead-onlyで運用するならばスワップファイル領域は作成しないようにしておく。この際、IDEの転送モードが問題になってくる。
CFカードでのブートにはまだ色々と問題がある。A-DATAの4GB・120倍速CFカードはMultiword-DMA Mode2まで対応しているようだが、M300に付属されている変換アダプタはDMA転送モードに対応していないようである。このため、インストール後にCFカードからブートさせようとするとブートローダの読み込みに数十分ほどの時間がかかる。また今回CFカードからのブートに問題はなかったが、CFメーカーによってはCFブートさせようとするとエラーを起こしてしまうようなカードも存在する模様。詳しい内容は参考文献を参照。
この問題を回避するために、CF->IDE変換アダプタを接続しているIDEコネクタの転送モードとして、DMA転送を使用しないように設定する。BIOSの設定から変更することが出来る。これでブートローダの読み込みが通常通り行われるようになる。
さらにカーネル起動後、OS側で転送モードが再設定される。このときにもDMA転送転送させないようにするために、カーネルのブートパラメータとして「ide=nodma」を渡す。ブートローダがGRUBであれば、正常起動後に/boot/grub/grub.confを修正しておく。
### /boot/grub/grub.conf title Vine Linux (Current kernel) root (hd0,0) kernel /vmlinuz ro root=LABEL=/ ide=nodma initrd /initrd.img
これらの設定によって、CFカードにはPIOモードで転送されることになる。PIO転送はDMA転送と比較すると低速であることは否めない。もしPIOの速度が気にくわないのであれば、266倍速以上のフラッシュメモリとUltraDMA転送に対応した変換アダプタを購入すればよい。ここでは目的が家庭用サーバの構築であるから、性能を求めていないということで了承してもらいたい。
Read-only Linuxの構築
以上まででCFカードからブートするLinuxを手に入れることが出来る。あとは必要なサービスをインストールして使用していけばいい。しかし、CFカードはその特性から「書き込み回数上限」というものが存在する。これはフラッシュメモリの構造的な問題であるので、完全な回避手段は存在しない。その上限値は個体差もあるが、およそ10〜30万回程度であると言われている。OSの処理においては自動で書き込みが発生する(スワップやログのような)こともある。10分に1回程度の割合で書き込みが発生すると仮定すると、単純計算でおよそ1年で5万回強の書き込みが行われる。最短で2年以内に書き込み回数上限に達する可能性があるということだ。勿論それ以上の頻度で書き込みが発生することも考えられるし、問題が発生するかどうかも不明である。
しかし、この問題の要点は「いつどのように壊れるか」ではなく「壊れる可能性を明らかに内包する」ことである。不安定化することが決定づけられているシステムを長期間運用しようなどというのは心臓に悪い冗談にしかならない。そこでCFカードへの書き込みは極力減らし、RAMDiskで対応するという方法でこの問題点を解決する。基本的にはファイルシステムをRead-onlyにしておけば、CFカードには余計な書き込みが為されず、寿命を延ばすことが出来る。一方、OSが書き込まなければならないデータはRAMDisk上に退避させておき、そちらとのやりとりで済ませることにする。RAMDiskを利用するには十分な容量のメモリが必要だが、昨今のメモリの低価格化を考えればマザーボードに限界まで積んでおけば問題ない。現在のところ、512MBの大きさでも十分運用できている。またRAMDiskからCFカードへのフィードバックは行わない。
Linuxをリードオンリー化するには次のような手順を踏む。
- ルートファイルシステムをRead-onlyでマウントし、その状態を維持する。
- メモリ上にRAMDiskを作成し、/etc、/tmp、/varをRAMDiskにコピーしてマウントさせる。
- depmodの起動禁止、autofsck書き込み禁止等の修正。
- 終了時のHaltを修正する。
起動時の設定は、Vine Linuxではrc.sysinitを修正することになる。他のOSではrc.sysinitまたはそれと同一の働きをするサービスが、Vineとは若干異なる記述になっているかもしれない。実際の修正を行うには、起動しなくなった時のことも考えてバックアップをとる他、必要であればrc.sysinitの動作も理解しなければならない。
ルートファイルシステムをRead-onlyのまま維持するにはカーネルにブートパラメータを渡すだけで良い。「no_remount_rootfs」をgrub.confに記述しておく。
### /boot/grub/grub.conf title Vine Linux (Current kernel) root (hd0,0) kernel /vmlinuz ro root=LABEL=/ ide=nodma no_remount_rootfs initrd /initrd.img
またスワップ領域がCF上に存在し、それをマウントするようになっているなら/etc/fstabから該当行を削除しておく。可能ならLinuxインストール時からスワップ領域は作成しないようにしておく。
次にRAMDiskを作成するスクリプトを記述する。/etc/rc.ramdiskとして、以下のように記述。
### /etc/rc.ramdisk #!/bin/bash # Make RAM-Disk for read-only system # Initialized Values INODE=2048 MNG="apt" PKG="rpm" # /etc mke2fs -q -i $INODE /dev/ram0 mount -n -o rw /dev/ram0 /mnt cp -a /etc/* /mnt umount -n /mnt mount -n -o rw /dev/ram0 /etc # /var mke2fs -q -i $INODE /dev/ram2 mount -n -o rw /dev/ram2 /mnt for i in `ls -1 /var | grep -v "cache" | grep -v "lib" | grep -v "log"` do cp -a /var/$i /mnt done mkdir /mnt/cache for i in `ls -1 /var/cache | grep -v $MNG` do cp -a /var/cache/$i /mnt/cache done mkdir /mnt/lib for i in `ls -1 /var/lib | grep -v $MNG | grep -v $PKG` do cp -a /var/lib/$i /mnt/lib done mkdir /mnt/log umount -n /mnt mount -n -o rw /dev/ram2 /var exit 0
記述は長いが、やっていることは単純。mke2fsでRAMDiskを初期化し、ターゲットから全てのファイルをコピー後、ターゲットとしてマウントしているだけである。このファイルに実行属性を付加しておく。
次に/etc/rc.sysinitを修正する。念のため、オリジナルファイルをバックアップしておくとよい。diffで差分を表示。
123a124,126 > # Make RAM-Disk for read-only system > action $"Making RAM-Disk for read-only system: " /etc/rc.ramdisk 564,587c567,590 < rc_splash "start depmod" < < # Our modutils don't support it anymore, so we might as well remove < # the preferred link. < rm -f /lib/modules/preferred < rm -f /lib/modules/default < if [ -x /sbin/depmod -a -n "$USEMODULES" ]; then < # If they aren't using a recent sane kernel, make a link for them < if [ ! -n "`uname -r | grep -- "-"`" ]; then < ktag="`cat /proc/version`" < mtag=`grep -l "$ktag" /lib/modules/*/.rhkmvtag 2> /dev/null` < if [ -n "$mtag" ]; then < mver=`echo $mtag | sed -e 's,/lib/modules/,,' -e 's,/.rhkmvtag,,' -e 's,[ ].*$,,'` < fi < if [ -n "$mver" ]; then < ln -sf /lib/modules/$mver /lib/modules/default < fi < fi < if [ -L /lib/modules/default ]; then < INITLOG_ARGS= action $"Finding module dependencies: " depmod -A default < else < INITLOG_ARGS= action $"Finding module dependencies: " depmod -A < fi < fi --- > #rc_splash "start depmod" > # > ## Our modutils don't support it anymore, so we might as well remove > ## the preferred link. > #rm -f /lib/modules/preferred > #rm -f /lib/modules/default > #if [ -x /sbin/depmod -a -n "$USEMODULES" ]; then > # # If they aren't using a recent sane kernel, make a link for them > # if [ ! -n "`uname -r | grep -- "-"`" ]; then > # ktag="`cat /proc/version`" > # mtag=`grep -l "$ktag" /lib/modules/*/.rhkmvtag 2> /dev/null` > # if [ -n "$mtag" ]; then > # mver=`echo $mtag | sed -e 's,/lib/modules/,,' -e 's,/.rhkmvtag,,' -e 's,[ ].*$,,'` > # fi > # if [ -n "$mver" ]; then > # ln -sf /lib/modules/$mver /lib/modules/default > # fi > # fi > # if [ -L /lib/modules/default ]; then > # INITLOG_ARGS= action $"Finding module dependencies: " depmod -A default > # else > # INITLOG_ARGS= action $"Finding module dependencies: " depmod -A > # fi > #fi 1002c1005 < touch /.autofsck --- > #touch /.autofsck
udevでデバイスを初期化した直後にRAMDisk作成スクリプトを実行するようにしている。加えてdepmodに関する部分を全てコメントアウト。他、fsck用の/.autofsckを作成する部分をコメントアウトしておく(この時点で既にRead-onlyとなっているため、書き込めずにエラーとなる)。リードオンリー運用ではfsckを行う必要はない。
実際のrc.sysinit → rc.sysinit(32KB)
/tmpだけは特別に、tmpfsとしてRAMDiskを使う。/etc/fstabを以下のように修正する。
### /etc/fstab LABEL=/ / ext3 defaults,ro 0 0 LABEL=/boot /boot ext3 defaults,ro 0 0 none /dev/pts devpts gid=5,mode=620 0 0 #none /dev/shm tmpfs defaults 0 0 none /proc proc defaults 0 0 none /sys sysfs defaults 0 0 none /tmp tmpfs defaults,mode=1777 0 0
もとの/etc/fstabに/dev/shmに関する記述があればそれをコメントアウトし、/tmp用にtmpfsを記述し直す。ついでに/と/bootのオプションにroを加えてリードオンリーにしてある。ちなみに/dev/ptsは端末エミュレータの起動時に必要なデバイス。これをコメントアウトすると端末が起動できなくなる可能性がある。
haltを修正する。以下、diffの差分表示。
178c178 < rm -f /.autofsck --- > #rm -f /.autofsck
rc.sysinit同様、/.autofsckの削除をコメントアウト。
実際のhalt → halt(5KB)
以上でリードオンリーなLinuxが構築できる。実際に動作させてみて、起動・終了ともに問題ないことを確認する。また、一時的に書き込みを行いたい場合(システムのアップデート等)はルートを再マウントすればOK。-wで書き込み可能、-rで読み込みのみとなる。
# mount -wn -o remount / # mount -rn -o remount /
再マウント後、/etc等もアンマウントしないとRAMDiskに書き込まれるだけで破棄されてしまうので注意が必要。
Comment