Debian Linux の OneCD(ライブDVD)化手順

Last-modified: 2014-12-23 (火) 03:50:25
初出 2007-8-2
最終更新 2009-03-29
開発時期 2007-8, 2009-3(squeeze, lenny対応)
このページの手順でUSBメモリ・ブートのルータなんかも作成できますが、その前に、
単なるUSBブートについての情報は→ こちら
インストーラをUSBメモリ(ディスク)からブート可能にするなら→ こちら
 

久しぶりに新規にOneCD Linuxを作ってみました。今回の素材はDebian etch(2009-3にlenny,squeezeに対応)です。
以前のFedora用のものと技術的には大して変わっていないのですが、DebianにはOneCD化し易い条件がかなり揃っていたこともあり、以前のよりかなり作業が簡素化できました。キット形式にしなければならないほどの複雑さはないので、本ページで手順という形で紹介します。

 

本ページで作成するCD(DVD)は、元となるLinux環境をそのままCD(DVD)に焼いたバックアップ・ディスクとほぼ同じです。/sys,/proc,/tmpを省いていること、CDブートすること、特製のinitrd(イニシャルラムディスク)が追加されていることが単なるバックアップ・ディスクとの違いです。

 

ライブCD(DVD)として、元となるLinux環境と(早さを除いては)同様にアプリケーションを使用することも出来ますし、自力でブートして元となったDebian etch環境をレストアできるリカバリー・ディスクとして用いることも出来ます。

 

また、本ページ掲載のinitrd(イニシャルラムディスク)用パッチを用いれば、OneCD化やOneDVD化はもちろんですが、CF等のフラッシュメモリー系ディスクや、ごく普通のハードディスクをリードオンリーでマウントして運用する用途にも使用できます(※)。実は私の今回の製作目的はCFのリードオンリー運用です。→超格安CF-IDE変換アダプタの製作、CF-IDE変換アダプタの活用Tips
私はこの機能を使ってスピンドルレスの静穏なルーターを実運用しています。

※: カーネルオプションのrootデバイスが/dev/?d?の場合は光学ドライブと見なし、/dev/?d??の場合は、パーティションテーブルを持つディスクと見なす仕様です。

 

特製のinitrdを作るためのパッチはaufs(etchではunionfs)を用いる版と用いない版の二種類を作成しました。aufs(unionfs)の説明は省略しますが、aufs(unionfs)を用いると見かけ上/以下どこにでも書き込みが可能となるのでデスクトップ用途としては優れていると思います。ただ私としてはルータやサーバ用途で長期連続稼動中にランダムなファイル名のファイルが大量に生成・消去された場合にはaufs(unionfs)を用いない方が安定感があるのではと思っています。用いる状態と用いない状態の切換はCF運用の場合、リモートからおこなえるので、今後ルータ兼簡易サーバとして運用しながら優劣を見極めたいと思っています。

 

通常はaufs(unionfs)を用いる版がおすすめです。aufs(unionfs)を用いない版も書き込みが必要な部分はラムディスクになっていて、基本的に全てのアプリケーションが普通に使用できます。しかしこちらの版はデスクトップ用途の場合、メモリ節約の為の小細工によってgnome、anthyの環境に特化してしまっています。使用する環境が大きく異なる場合は修正が必要です。こちらにも追加説明があります。

 

ライブCD(DVD)としての特徴を持っていますから、滅茶な使い方をしても再起動すれば初期状態に戻ります。またSATA-HDDやUSBブートにも対応しています。

 

(2007-12-27追記) パッチ中にコメントがないのは、出来るだけ「パッチの容量が小さいな」と言う印象を持ってもらいたいからです。これくらいの容量でコメントがない場合、わりと全てに目を通してくれる確率が高くなるような気がするからです。また、同じく容量を小さくするために、あまり至れり尽せりな内容にはしていません。例えば/bootをHDD上で元々別パーティションにしていた場合の扱いなんかは明らかに簡略化しています。

 

OneCD(ライブDVD)化、もしくはリードオンリー化の手順

構築済みの Debian squeeze もしくは lenny もしくは etch 環境上で作業をおこないます。

1.追加パッケージのインストール

# apt get install syslinux

今回もブートローダはgrubではなく、私がCDやDVDのブートではいつも使っているsyslinux(isolinux)を選びました。
普通は入っていないものなのでインストールして下さい。
また、aufs(unionfs)を用いる場合はaufs(unionfs)のコンパイル済みモジュールをインストールして下さい。

# apt-get install aufs(unionfs)-modules-2.6.hoge-fuga
 

ルータ用にbaseだけインストールした場合はmkisofsも入っていないのでインストールして下さい。

2.initrdの解凍

作業ディレクトリをここでは仮に/boot/workとします。(以下も同じ)

# cd /boot
# cp -p initrd.img-2.6.hoge-fuga initrd.img-2.6.hoge-fuga.orig
# mkdir work
# cd work
# gzip -dc ../initrd.img-2.6.hoge-fuga.orig | cpio -idmv

3.initrd内に追加ファイルをコピー

元々etchのinitrd内には他のディストリと比べると驚くほど多くのコマンドが揃っています。
しかし、aufs(unionfs)を用いる場合はaufs(unionfs)のモジュール、aufs(unionfs)を用いない場合はstatコマンドを/以下からコピーする必要があります。

3-a) aufs(unionfs)を用いる場合

# cd /boot/work/lib/modules/2.6.hoge-fuga/kernel/fs
# cp -p /lib/modules/2.6.hoge-fuga/kernel/fs/exportfs/exportfs.ko . # squeeze, lennyの場合
# cp -p /lib/modules/2.6.hoge-fuga/extra/aufs/aufs.ko .       # squeeze, lennyの場合
# cp -p /lib/modules/2.6.hoge-fuga/extra/unionfs/unionfs.ko .    # etchの場合

3-b) aufs(unionfs)を用いない場合

# cd /boot/work/bin
# cp -p /usr/bin/stat .

4.initスクリプトにパッチを当てる

4-a) aufs(unionfs)を用いる場合

# cd /boot/work
# patch -p0 < ../squeeze-initrd-init-onecd-aufs.patch  # squeeze, lennyの場合
# patch -p0 < ../etch-initrd-init-onecd-unionfs.patch  # etchの場合

ダウンロード filesqueeze-initrd-init-onecd-aufs.patch (squeeze, lenny用)
    ダウンロード fileetch-initrd-init-onecd-unionfs.patch (etch用)
※:ramdisk_size認識の部分を除き、元々mountrootと記述してある部分の前後にまとめて追加しただけなので将来Debianオリジナルのinitスクリプトが変化してこのパッチがそのまま当たらなくなった場合でも簡単に手パッチで対応できると思います。また他ディストリへの展開も難しくないと思います。
[squeeze-initrd-init-onecd-aufs.patch]

--- init	2009-02-18 02:06:31.000000000 +0900
+++ init.new	2009-03-29 02:46:45.000000000 +0900
@@ -168,6 +168,9 @@
 	blacklist=*)
 		blacklist=${x#blacklist=}
 		;;
+	ramdisk_size=*)
+		RAMDISKSIZE="${x#ramdisk_size=}"
+		;;
 	esac
 done
@@ -197,8 +200,52 @@
 log_begin_msg "Mounting root file system"
 . /scripts/${BOOT}
 parse_numeric ${ROOT}
+
+[ ! -n "${RAMDISKSIZE}" ] && RAMDISKSIZE=64M
+mntpoint=DISK
+diskmnt=${rootmnt}/${mntpoint}
+echo "Mounting RAMDISK..."
+mount -t tmpfs -o size=${RAMDISKSIZE} tmpfs ${rootmnt}
+echo "Creating directories..."
+mkdir ${diskmnt}
+for i in boot initrd sys proc dev tmp; do
+mkdir ${rootmnt}/${i}
+done
+chmod 1777 ${rootmnt}/tmp
+echo > ${rootmnt}/fastboot
+
+echo "Mounting root disk..."
+export rootmnt=${diskmnt}
+if [ $(str=${ROOT##*/} && num=${str##*d} && echo ${#num}) -lt 2 ]; then
+cdboot=1
+mount -o ro -t iso9660 ${ROOT} ${diskmnt}
+else
 maybe_break mountroot
 mountroot
+fi
+export rootmnt=/root
+mount --bind /dev ${rootmnt}/dev
+
+echo "Mounting aufs..."
+mkdir ${rootmnt}/UNIONW ${rootmnt}/UNIONFS
+chmod 1777 ${rootmnt}/UNIONW
+modprobe -q aufs
+mount -t aufs -o dirs=${rootmnt}/UNIONW=rw:${diskmnt}=ro unionfs ${rootmnt}/UNIONFS
+
+echo "Creating symbolic links..."
+for i in bin sbin lib selinux srv usr opt var etc media mnt root home; do
+ln -s UNIONFS/${i} ${rootmnt}/${i}
+done
+
+if [ -n "${cdboot}" ]; then
+cp -p ${rootmnt}/etc/fstab ${rootmnt}/etc/fstab.onecdbkup
+grep -v "/boot"<${rootmnt}/etc/fstab.onecdbkup>${rootmnt}/etc/fstab
+rm -rf ${rootmnt}/boot
+ln -s UNIONFS/boot ${rootmnt}/boot
+fi
+
+dmesg>${rootmnt}/var/log/dmesg
+
 log_end_msg
 maybe_break bottom

このsqueezeおよびlenny用のパッチのetch用からの実質的な違いは、etch用のものではunionfsと記述している個所をaufsと書き換えたことだけです。

4-b) aufs(unionfs)を用いない場合

# cd /boot/work
# patch -p0 < ../etch-initrd-init-onecd-normal.patch

ダウンロード filesqueeze-initrd-init-onecd-normal.patch (squeeze, lenny用)
    ダウンロード fileetch-initrd-init-onecd-normal.patch (etch用)
※:ramdisk_size認識の部分を除き、元々mountrootと記述してある部分の前後にまとめて追加しただけなので将来Debianオリジナルのinitスクリプトが変化してこのパッチがそのまま当たらなくなった場合でも簡単に手パッチで対応できると思います。また他ディストリへの展開も難しくないと思います。
[(squeeze-initrd-init-onecd-normal.patch]

--- init	2009-02-18 02:06:31.000000000 +0900
+++ init.new	2009-03-29 11:28:38.000000000 +0900
@@ -168,6 +168,9 @@
 	blacklist=*)
 		blacklist=${x#blacklist=}
 		;;
+	ramdisk_size=*)
+		RAMDISKSIZE="${x#ramdisk_size=}"
+		;;
 	esac
 done
@@ -197,8 +200,110 @@
 log_begin_msg "Mounting root file system"
 . /scripts/${BOOT}
 parse_numeric ${ROOT}
+
+dircp ()
+{
+SRCEDIR=$1
+DESTDIR=$2
+BASEDIR=`pwd`
+mkdir -p "${DESTDIR}"
+cd "${SRCEDIR}"
+find . -type d|while read i
+do
+	cd "${BASEDIR}"
+	cd "${SRCEDIR}"
+	DIRMODE=`stat -c %a "${i}"`
+	DIRUSER=`stat -c %u "${i}"`
+	DIRGROP=`stat -c %g "${i}"`
+	cd "${BASEDIR}"
+	cd "${DESTDIR}"
+	mkdir -p "${i}"
+	chmod $DIRMODE "${i}"
+	chown $DIRUSER "${i}"
+	chgrp $DIRGROP "${i}"
+done
+}
+[ ! -n "${RAMDISKSIZE}" ] && RAMDISKSIZE=64M
+mntpoint=DISK
+diskmnt=${rootmnt}/${mntpoint}
+echo "Mounting RAMDISK..."
+mount -t tmpfs -o size=${RAMDISKSIZE} tmpfs ${rootmnt}
+echo "Creating directories..."
+mkdir ${diskmnt}
+for i in boot initrd sys proc dev tmp; do
+mkdir ${rootmnt}/${i}
+done
+chmod 1777 ${rootmnt}/tmp
+echo > ${rootmnt}/fastboot
+
+echo "Mounting root disk..."
+export rootmnt=${diskmnt}
+if [ $(str=${ROOT##*/} && num=${str##*d} && echo ${#num}) -lt 2 ]; then
+cdboot=1
+mount -o ro -t iso9660 ${ROOT} ${diskmnt}
+else
 maybe_break mountroot
 mountroot
+fi
+export rootmnt=/root
+mount --bind /dev ${rootmnt}/dev
+
+echo "Copying files, directories..."
+for i in etc media mnt root home; do
+cp -a ${diskmnt}/${i} ${rootmnt}
+done
+dircp ${diskmnt}/var ${rootmnt}/var
+
+echo "Creating symbolic links..."
+for i in bin sbin lib selinux srv usr opt; do
+ln -s ${mntpoint}/${i} ${rootmnt}/${i}
+done
+
+echo "Adding /var/lib..."
+rm -rf ${rootmnt}/var/lib
+for i in ${diskmnt}/var/lib/*; do
+	case $i in
+	*/gconf|*/dpkg|*/apt|*/aptitude|*/anthy)
+	;;
+	*)
+	cp -a ${i} ${rootmnt}/var/lib
+	;;
+	esac
+done
+for i in gconf dpkg apt anthy; do
+if [ ! -e ${diskmnt}/var/lib/${i} ]; then continue; fi
+ln -s ../../${mntpoint}/var/lib/${i} ${rootmnt}/var/lib/${i}
+done
+
+echo "Adding /var/cache..."
+rm -rf ${rootmnt}/var/cache
+for i in ${diskmnt}/var/cache/*; do
+	case $i in
+	*/apt|*/debconf|*/app-install|*/man)
+	;;
+	*)
+	cp -a ${i} ${rootmnt}/var/cache
+	;;
+	esac
+done
+for i in apt debconf app-install man; do
+if [ ! -e ${diskmnt}/var/cache/${i} ]; then continue; fi
+ln -s ../../${mntpoint}/var/cache/${i} ${rootmnt}/var/cache/${i}
+done
+
+if [ -n "${cdboot}" ]; then
+cp -p ${rootmnt}/etc/fstab ${rootmnt}/etc/fstab.onecdbkup
+grep -v "/boot"<${rootmnt}/etc/fstab.onecdbkup>${rootmnt}/etc/fstab
+rm -rf ${rootmnt}/boot
+ln -s ${mntpoint}/boot ${rootmnt}/boot
+fi
+
+echo "Copying crontabs..."
+rm -rf  ${rootmnt}/var/spool/cron/crontabs
+cp -a ${diskmnt}/var/spool/cron/crontabs ${rootmnt}/var/spool/cron
+
+dmesg>${rootmnt}/var/log/dmesg
+
 log_end_msg
 maybe_break bottom

このsqueezeおよびlenny用のパッチのetch用からの実質的な違いは、etch用のものに/var/cacheと/var/spool/cron/crontabsのコピー部を追加したことだけです。


※: こちらの版では/varをラムディスク上にコピーしています。ただラムディスク容量節約の為、一部シンボリックリンク化した部分を除き、各サブディレクトリの中身を空っぽにしてコピーしています。どうしても必要なファイルは自らコピーする命令を追加記述する必要があります。例えば私の場合、cron用のファイルは必要です。でもこのパッチに書いてしまうとキリがなくなるので省いています。もちろん実際に私が運用している環境では記述しています。このような問題があるので通常はaufs(unionfs)を用いる版がお薦めです。

5.initrdの圧縮

# cd /boot/work
# find | cpio -H newc -o | gzip -9 > ../initrd.img-2.6.hoge-fuga # 元のファイル名

6.ハードディスクからのブートでCD、DVDブートの予行演習(動作確認)

# cd /boot/grub

menu.lstのデフォルトに指定されているセクションをコピーし、

title           Debian GNU/Linux, kernel 2.6.hoge-fuga(original)
root            (hd0,○)
kernel          /vmlinuz-2.6.hoge-fuga root=/dev/hda○ ro
initrd          /initrd.img-2.6.hoge-fuga.orig
savedefault

みたいなセクションを追加しておきます。
また、デフォルトに指定されているセクションのカーネルオプションに適宜、

ramdisk_size=192M

のような指定を追加しておきます。指定しなければラムディスクサイズは64Mになります。
ルータ専用の場合は32Mもあれば十分だと思います。
加えて/etc/fstabの/の行とswapの行をコメントアウトしておきます。
そして、再起動します。
ルートデバイスをリードオンリーマウントした状態でシステムが正常に起動すれば成功です。
ここまで成功すればCD化、DVD化もまず成功するでしょう。

 

ハードディスクやCFからブートする場合、fstabに/bootを専用パーティションとしてマウントする記述があれば、それに従って/bootをマウントする仕様となっています。この場合、ルートデバイスをリードオンリーマウントした状態でもmenu.lst等の書き換えが可能です。

7.CDやDVDの作成

7-a) isoファイル作成スクリプト の実行

make-onecd-iso.sh
このスクリプトでisolinuxによるCD(DVD)ブート環境の構築からisoファイルの作成までをおこないます。

ダウンロード filemake-onecd-iso.sh

#!/bin/sh -ex
# make-onecd-iso.sh
sysname=OneCDDebian
outputpath=/iso-output
mkdir -p ${outputpath}
mkdir -p /isolinux
cp -pf /vmlinuz /isolinux
cp -pf /initrd.img /isolinux
cp -pf /usr/lib/syslinux/isolinux.bin /isolinux
cat>/isolinux/isolinux.cfg<<EOT
default vmlinuz
append initrd=initrd.img root=/dev/hdc vga=0x317
prompt 1
say Welcome to ${sysname} linux.
say Select menu or override default boot option or wait 5 seconds.
say eg.) boot: vmlinuz root=/dev/hdb ramdisk_size=192M
say |
say Default boot option: "vmlinuz root=/dev/hdc 0x317"
say Default ramdisk size: 64M
say |
timeout 50
label hdc
  say hdc) Boot from /dev/hdc
  kernel vmlinuz
  append initrd=initrd.img root=/dev/hdc vga=0x317
label hda
  say hda) Boot from /dev/hda
  kernel vmlinuz
  append initrd=initrd.img root=/dev/hda vga=0x317
say |
EOT
mkisofs \
	-V "${sysname}" \
	-R \
	-x ${outputpath} -x /sys -x /proc -x /tmp  \
	-b isolinux/isolinux.bin  -o ${outputpath}/${sysname}.iso \
	-c isolinux/boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table /

このスクリプトでは簡潔に記述する為にisolinuxのメニューにsayを使用しています。ほぼ言うまでもないことですが、メッセージファイルやグラフィカルメニューを使用するのが一般的です。必要であれば適宜変更して下さい。
ここに紹介している手順ですと、稼動中のLinux上でシステム全体のバックアップをとっているのと実質的に同じことになります。ここに関係記事があります。gnome上で作業しても殆ど問題は発生したりしませんが、gnomeの設定を変更した直後の場合は一旦ログアウトしてから作業を開始して下さい。気になる人はシングルユーザモードで作業したり、別のOS上から作業してください。厳密に言えば/devの扱いを考慮すると別のOS上からの作業が最も好ましいですが、この点でここに紹介している手順に問題が発生することはありません。

7-b) オンザフライ焼きする場合

make-onecd-on-the-fly.sh
このスクリプトではDVDのオンザフライ焼きをおこないます。

  • ここで使用しているgrowisofsコマンドはDVD専用だと思います。CDの場合でもmkisofsとcdrecordをパイプでつなげばオンザフライ焼きが可能です。
  • 当該PCにライタブルな光学ドライブがなく、且つ空きディスク容量がない場合でも、mkisofsの出力をsshで他のマシンに送る手法が使えます。

ダウンロード filemake-onecd-on-the-fly.sh

#!/bin/sh -ex
# make-onecd-on-the-fly.sh
sysname=OneCDDebian
mkdir -p /isolinux
cp -pf /vmlinuz /isolinux
cp -pf /initrd.img /isolinux
cp -pf /usr/lib/syslinux/isolinux.bin /isolinux
cat>/isolinux/isolinux.cfg<<EOT
default vmlinuz
append initrd=initrd.img root=/dev/hdc vga=0x317
prompt 1
say Welcome to ${sysname} linux.
say Select menu or override default boot option or wait 5 seconds.
say eg.) boot: vmlinuz root=/dev/hdb ramdisk_size=192M
say |
say Default boot option: "vmlinuz root=/dev/hdc 0x317"
say Default ramdisk size: 64M
say |
timeout 50
label hdc
  say hdc) Boot from /dev/hdc
  kernel vmlinuz
  append initrd=initrd.img root=/dev/hdc vga=0x317
label hda
  say hda) Boot from /dev/hda
  kernel vmlinuz
  append initrd=initrd.img root=/dev/hda vga=0x317
say |
EOT
growisofs \
	-V "${sysname}" \
	-R \
	-x /sys -x /proc -x /tmp  \
	-b isolinux/isolinux.bin \
	-dvd-compat -Z /dev/dvd \
	-c isolinux/boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table /

このスクリプトでは簡潔に記述する為にisolinuxのメニューにsayを使用しています。ほぼ言うまでもないことですが、メッセージファイルやグラフィカルメニューを使用するのが一般的です。必要であれば適宜変更して下さい。

ルータやサーバとして使用する場合の常套Tips

  • 大量のログを吐くアプリケーションのsyslogは他のマシンに飛ばす
  • logrotateのrotate間隔を短くする

作成したディスクをリカバリー・ディスクとして用いる場合の注意点

mkisofsコマンドに与えるオプションの影響で、以下の作業を付け加える必要があります。

# cd リカバリーしたroot
# mkdir sys proc tmp
# chmod 1777 tmp

その後の状況

(2008-8-31記述)私はnormalの方を常用しています。7月に9ヶ月ぶりに停止させ、普通に起動してapt-get dist-upgradeをおこないました。今後も特に変更予定はありません。
pppoeのルータとして使用していますが、他にもVPNルータ、内部用samba,ftp,プリンタサーバとしても使用しています。他の工夫としてはグローバルipアドレスを書き込み可な場所に記録するようにしています。
現在は1GバイトのCFをシステム用に500Mバイト弱、残りを内部用共有フォルダに充てています。わざとシステム用の領域を小さくしてあるのですが、不要パッケージの削除をapt-get removeのみでおこなうというルールではこの大きさはほぼ限界です。カーネルアップデートは一部をNFSに逃さないとおこなえないほどです。次回からはシステム用領域に1Gくらいは確保しようと思っています。
(2009-01-18)後継として使用予定のものを開発しました。
Minimum Ubuntu (ミニマムUbuntu:レスキュー機能を持ち、限界までサイズを絞ったUbuntu Linuxの派生)

自作ルータを長期運用してきた私が得た教訓

(2008-10-29記述)今までいくつかのOne CD LinuxやCFを使ったスピンドルレスなルータを長期間、常時稼動させてきました。それによって得た細かい教訓を挙げてみたいと思います。

  • One CD Linuxの重大な欠点 ― 長期運用でCDに埃が付着しての読み取り不良が起きやすい
    • 利用しているPCによってはCDドライブが空気の通り道になっている場合があります。そういった環境で長期運用しているとCD-Rの裏面に汚れが付着し、いつの間にかディスクアクセスが出来なくなっていることがあります。そうなってしまってもネットワーク・ルータとしての動作に支障はきたさないのですが、ルータにログインして何か他の作業をやろうとした際に「あれ、出来ない」という状況に気づくなんてことになります。One CD LinuxよりはスピンドルレスLinuxの方が好ましいと思います。
  • ACアダプタが好ましい
    • 断っておきますが、ハードディスクレスなPCは電源が非常に長持ちします(※)。ですから内蔵電源だと駄目ってことはないです。今までに私が運用した自作ルータは全て内蔵電源の省スペースPCです。しかし電源が故障した場合の復旧のし易さを考慮すると、ルータ用途などで長期に渡って常時稼動させるPCはできる限りACアダプタを使用すべきだと思います。さらに言えば、デスクトップ用途のPCもACアダプタを積極的に利用すべきだと思います。

※:電源にかかる負荷が低いので、劣化した電源でも障害が発生しにくいという意味で

 

ご要望、ご意見、質問を下のフォームにどうぞ
(でもここより、掲示板書き込みフォームのページに書いて頂いた方が気づき易いと思います。)

  • cdに埃が付く場合や、フロピーにカビが生えるなどのときは、cat /dev/xxx >/dev/nullを3回ぐらいして、モーターを回すようにするといいですよ。それをcronで定期的にするなど。 -- ? 2014-12-23 (火) 03:50:24