3台の FreeBSD 8.4 サーバーを更新中ですが、大きなトラブルもなく最初の1台を完了できたことに気をよくして、本日2台目の更新に取りかかりました。
2台目は、ディスク残量が少し気になってはいたのですが、容量不足ならワーニングメッセージが出るだろうという見込みで始めてしまいました。
この甘い判断がドツボの始まり。
ディスク容量不足
新しいカーネルを書き込んでいるところで、ディスク容量不足になってしまいました。ワーニングは出てきませんでした。ガックリ。
# freebsd-update install Installing updates... /: write failed, filesystem is full install: ///boot/kernel/INS@MH1V: No space left on device rmdir: ///boot/kernel: Directory not empty Kernel updates have been installed. Please reboot and run "/usr/sbin/freebsd-update install" again to finish installing updates.
間違った作業
ここで、リブートしたら絶対に立ち上がらないと分かっているので、再度「freebsd-update install」を行わざるを得ませんでした。本来、これは再起動後に行うべきコマンドなんですが、この時点では他に行うべきコマンドが思いつかなかったので、やってしまいました。
あとから落ち着いて考えれば、/boot/kernel.old1 を /boot/kernel としてコピーしたり、freebsd-update rollback コマンドを使用すれば、再び 8.4 で再起動できたかもしれません。
# freebsd-update install Installing updates... Completing this upgrade requires removing old shared object files. Please rebuild all installed 3rd party software (e.g., programs installed from the ports tree) and then run "/usr/sbin/freebsd-update install" again to finish installing updates.
上記のように明らかにステージが違うメッセージが表示されています。
このあとリブートしたところ。永遠の沈黙が・・・・・・・・・。
マシン設置場所の友人にメールでコンタクトして画面を見てもらったところ、次のメッセージが出ているとのこと。このメッセージは、カーネル起動途中のディスクマウントに失敗したときに発生する現象で、以前 ATOM D510 を freebsd-update で8.2から 8.3 に更新した後に SATA のデバイスファイル名が勝手に代わってしまいOSが起動しなくなった時に、似たような状況になったことがあります。
uhub0: 2 ports with 2removable, self powered
Trying to mount root from ufs:/dev/ad0s1a [rw]...
mountroot: waiting for device /dev/ad0s1a ...
Mounting from ufs:/dev/ad0s1a failed with error 19.
Loader variables:
vfs.root.mountfrom-ufs:/dev/ad0s1a
vfs.root.mountfrom.options=rw
Manual root filesystem specification:
: options
Mount using filesystem
add with the specified (optional) option list.
eg. ufs:/dev/da0s1a
zfs:tank
cd9660:dev/acd0 ro
(whick is equivalent to : mount -t cd 9660 -o ro /dev/acd0 /)
? List valid disk boot devices
. Yield 1 second (for background tasks)
Abort manual input
mountroot>
やっぱり、/boot/kernel 以下の一部のファイルがダメージを受けているようです。はぁ。
この時点で、コンソールからコマンドを一行入力するだけで、再び FreeBSD 8.4 の古いカーネルで起動する方法はあったのですが、(FreeBSD 9.x のプリブート画面を触る機会がなく、いつも素通りしていましたので、)この時は気づきませんでした。
オフライン回復作業
結局、現場に依頼して、HDD を取り外して送ってもらうように手配し、ローカルにてデータの吸い出しと、再インストールを行うことにしました。
目的は、
- HDD 上のユーザーデータを保持する
- FreeBSD 8.4 のディレクトリ構造を保ったまま OSを更新する。
の2点。可能であれば、OSを復旧させた後、容量が大きいHDDに移行させたい。
そして、本日、依頼していたHDDが到着。
旧カーネルからの起動
FreeBSD の場合(というか殆どのOSで可能ですけど)、更新後のカーネルが何らかの不具合で起動できない場合、旧カーネル(が残っているらな)から起動することが可能です。FreeBSDの場合は、デーモンロゴのところで10秒間のポーズがありますが、この時、スペースキーでポーズを延長し、’ESC’ キーでコマンドラインに、
boot kernel.old
と起動したいカーネル名をタイプすれば、旧カーネルから起動できるかもしれません。(起動できてもネットワークが開通するかどうかは分からないので、運が良ければという条件付きですが。)コンソールの前にいれば、回復可能でしょう。
どのカーネルが何に対応するかは、事前に /boot/kernel.???/kernel を what コマンドでチェックしてみればわかります。
# ll -d kernel.* drwxr-xr-x 2 root wheel 28672 8月 28 13:37 kernel.old drwxr-xr-x 2 root wheel 14336 8月 28 13:37 kernel.old1 drwxr-xr-x 2 root wheel 41472 8月 28 13:36 kernel # what kernel.*/kernel kernel.old/kernel: FreeBSD 7.3-RELEASE #0: Thu Apr 15 18:15:34 JST 2010 kernel.old1/kernel: FreeBSD 8.4-RELEASE-p34 #0: Tue Jul 21 21:11:16 UTC 2015 kernel/kernel: FreeBSD 9.3-RELEASE-p21 #0: Tue Jul 28 08:57:41 UTC 2015
この例では、起動しない 9.3 の他、8.4 と 7.3 が保存されていました。(HDD 送付を依頼する前に、これに気がついていれば・・・・)
ちなみに、7.3 では起動できたものの、ログインできませんでした。9.3 は起動できません。
kernel.old1(FreeBSD 8.4)で起動したところ、OSが起動し、ログインも出来たので、まずはデータのバックアップ。/ と /usr を dump コマンドでパーティションごとバックアップしておきました。これで、このあとの失敗も怖くない!
ロールバックを試みる
Windows のレストア機能と似たようなもので、freebsd-update コマンドにも巻き戻し機能があります。ただし、これは更新後のOSを前のバージョンに戻すもので、今回のように新しいカーネル自体がクラッシュしている場合には使えるかどうか分かりません。それに、私、/boot/kernel を消してしまいましたし。kernel.old1 の FreeBSD 8.4 で起動できれば良くて、その他カーネル以外が元に戻ってくれればありがたいということで、freebsd-update rollback を使ってみました。
# freebsd-update rollback Uninstalling updates...rm: ///usr/src/share/zoneinfo/zone1970.tab: No such file or directory rm: ///usr/src/share/zoneinfo/leap-seconds.list: No such file or directory rm: ///usr/src/secure/lib/libssl/man/SSL_CTX_set_tlsext_ticket_key_cb.3: No such file or directory rm: ///usr/src/crypto/openssl/test/constant_time_test.c: No such file or directory rm: ///usr/src/crypto/openssl/doc/ssl/SSL_CTX_set_tlsext_ticket_key_cb.pod: No such file or directory rm: ///usr/src/crypto/openssl/crypto/constant_time_test.c: No such file or directory rm: ///usr/src/crypto/openssl/crypto/constant_time_locl.h: No such file or directory rm: ///usr/src/contrib/bind9/lib/isc/include/isc/counter.h: No such file or directory rm: ///usr/src/contrib/bind9/lib/isc/counter.c: No such file or directory install: ///boot/GENERIC/kernel: No such file or directory install: ///boot/GENERIC/kernel.symbols: No such file or directory install: ///boot/GENERIC/linker.hints: No such file or directory install: ///boot/kernel/kernel: No such file or directory install: ///boot/kernel/kernel.symbols: No such file or directory install: ///boot/kernel/linker.hints: No such file or directory done.
まあ、正常時には戻らないでしょうね。
ここから再び、「freebsd-update -r 9.3-RELEASE upgrade」 を試そうとしたのですが、OS部分がボロボロ状態。ネットワークは何とか動いているものの、
setloginclass(root): Function not implemented
なんてメッセージがコンソールにガンガン出ていて、これはダメだという感じ。
ネットワーク経由の修復は諦めて、FreeBSD ftp サイトから、FreeBSD 9.3 ISO イメージをダウンロードし、これをCD-RWに焼いて、CDからブートして、HDD 上の中途半端なFreeBSD 8.4 を 9.3 に更新しようと考えました。
FreeBSD 9.3 ブートCDから起動しない!
対象のHDDは、i810チップセットの sockt370 マザーボードにつないでいます。理由は、ATOM 330 ボードのFreeBSD 9.3 につないだら、FreeBSDパーティションを(なぜか)ufs パーティションとして認識されなかったため。
# fsck -y /dev/ada2s1d fsck: Could not determine filesystem type
i810 ボードにつないで旧カーネルから起動したら FreeBSDパーティションとして認識されるようになりました。
このPCはCD しかつながっておらず、USBブートは出来ません。仕方なく、ftpサイトから、
- FreeBSD-9.3-RELEASE-i386-bootonly.iso
- FreeBSD-9.3-RELEASE-i386-disc1.iso
の両方をCD-RとCD-RWに焼いて起動させたのですが、どちらもCDファイルシステムをマウントする時点で、mountroot プロンプトに落ちてしまいます。2枚焼いたのは、メデイアかドライブが悪いのかと思ったため。
BIOSの 2nd IDE PIO モードやDMAモードをOFFしてみましたけど、結果は同じ。CDドライブを取り替えてみても現象は変わらず。
昔作った、FreeBSD 8.2 のISO CD-R を試してみたところ、問題なくインストーラが起動。どう考えてもftpサイトにアップされているISOイメージがおかしいとしか思えない。しかし、Check Sum は問題なし。i810チップセットボードを使っているほかに人の所では問題ないのかな?時間が無くて、他のPCでどうなるのか試していませんが、この作業が終わったら試さないといけなさそうです。
FreeBSD 8.4 に戻す
9.3を直接インストールするのは諦めて、まず、
- FreeBSD 8.4 が動くようにし(この状態では恐らく 9.3 への freebsd-update が動かない)
- FreeBSD 8.4 の最終パッチバージョンへアップデート(これで freebsd-update コマンドが使用可能になるはず)
- FreeBSD 9.3 へアップデート
というルートを経由することに決めましたが、このルートはイバラの道でした。
FreeBSD 8.4 のインストールCDがどこかにないかと探してみたものの、見つかりません。ISOイメージは見つからなかったものの、Past Release ftp サイトへのリンクは見つかり、8.4 のインストールファイルは今でも入手可能だということが分かりました。sysinstall コマンドは http や ftp でのダウンロードの他、事前にダウンロードしておいたファイルシステム上のファイルからインストールする方法をサポートしています。sysinstall コマンドを使う前に、wget で Past Release の 8.4-RELASE ディレクトリごと HDD 上にダウンロードすることにしました。
wget -rc "http://ftp-archive.freebsd.org/pub/FreeBSD-Archive/old-releases/i386/8.4-RELEASE/"
で、/home 上に再帰ダウンロードを行いました。
このftpサイトのファイル構造を見て、9.x 以降と8.x以前の配布ファイルの構造が大きく違っていることに気づきました。8.x 以前は 1.4MB FD で配布可能なように1.4MB毎にファイル分割されてされていましたが、9 以降は一つの圧縮ファイルになっていました。もうフロッピーは見捨てられていた訳ね。
sysinstall では、インストールイメージをダウンロードしてあるファイルシステム上のパスを入力しなくてはいけないのですが、これの指定がどの階層までなのか分からず一苦労。
たとえば、/home の下で wget を発行したとすると、
/home/ftp-archive.freebsd.org/pub/FreeBSD-Archive/old-releases/i386/8.4-RELEASE/
という深いディレクトリ構造の下にファイルが置かれます。この場合、インストーラには、
「/home/ftp-archive.freebsd.org/pub/FreeBSD-Archive/old-releases/i386/」まで指定することになります。
ここまで来たら、結構楽になりました。sysinstall の Upgrade メニューでminimal と(カーネルが消えているので構築することも視野に入れて)kernel-developer の二つを選択して、最低限のコマンドとカーネル構築が出来るパッケージをインストール。GENERIC カーネルが入って、リブート準備完了。これで、毎回ブートコマンドラインからタイプしてカーネルを選択するという作業から解放される〜と喜んでリブート。
しかし、ブートラインで止まりました・・・・・・・。
kernel.old1 で起動してみると、8.4 パッチレベル0 では、/boot/kernel は存在していなくて、/boot/GENERIC でした。GENERIC を kernel にリネームしてみても起動しない!いつから カーネル名が /boot/kernel になったんでしょう?調べれば分かるかもしれませんが、今はそんな気になれない。あとで再構築することにします。
コマンドラインから、
boot /boot/GENERIC/kernel
とタイプして FreeBSD 8.4-p0 を起動しました。
/boot の下に自動起動できるカーネルがないのでカーネルを構築してインストールすることにします。
/sys/i386/conf の下で、
config GENERIC
/sys/i386/compile/GENERIC に移動して、
make cleandepend && make depend make install
これで、/boot/kernel が出来上がりました。
FeeBSD8.4-p35 に上げる
次に、freebsd-update fetch ; freebsd-update install で FreeBSD 8.4-p35 に更新しようとしたところ、またまたトラブル。
# freebsd-update fetch Looking up update.FreeBSD.org mirrors... 5 mirrors found. Fetching metadata signature for 8.4-RELEASE from update2.freebsd.org... done. Fetching metadata index... done. The update metadata is correctly signed, but failed an integrity check. Cowardly refusing to proceed any further.
このエラーは、OS内のライブラリーや設定ファイルが古すぎる場合に出てきます。通常は、freebsd-update fetch コマンドで新しいファイルをダウンロードすれば解決するのですが、その freebsd-update コマンド自体がエラーを出しているので、困ってしまいます。
検索してみたら、
https://www.freebsd.org/security/advisories/FreeBSD-EN-12:01.freebsd-update.asc
に情報があり、パッチを当てれば良いみたいです。
仕方がないので、freebsd-update コマンドにパッチを当てることにしました。やってみたら簡単。
- sysinstall → configure → distribution → src → usbin で /usr/sbin のソースをインストール
- # fetch http://security.FreeBSD.org/patches/EN-12:01/freebsd-update.patch でパッチをダウンロード。
- # cd /usr/src
# patch < /path/to/patch
# cd /usr/src/usr.sbin/freebsd-update
# make obj && make && make install
で、freebsd-update コマンドを更新。
これで、freebsd-update コマンドが更新され、freebsd-update fetch が使えるようになりました。
# freebsd-update fetch Looking up update.FreeBSD.org mirrors... none found. Fetching metadata signature for 8.4-RELEASE from update.FreeBSD.org... done. Fetching metadata index... done. Fetching 1 metadata files... done. Inspecting system... done. Preparing to download files... done. Fetching 1361 patches.....10....20....30..略
そして、最終版 FreeBSD 8.4 が完成。
# uname -a FreeBSD pc50.lifewithunix.jp 8.4-RELEASE-p35 FreeBSD 8.4-RELEASE-p35 #0: Tue Jul 28 10:38:20 UTC 2015 root@amd64-builder.daemonology.net:/usr/obj/usr/src/sys/GENERIC i386
ようやく、振り出しに戻ることが出来ました。
FreeBSD 9.3 へ上げる
最後に、目的の 9.3 に上げるため、「freebsd-update -r 9.3-RELEASE upgrade」 を発行。今度はディスののこり容量に注意して作業しました。
/usr/src の下に、カーネルと user/sbin 用ソースファイルが置いてあるため、fetch に時間が掛かりました〜。
そして、freebsd-update install を行ったところkernel とその付属物のアップデートが終了。
Kernel updates have been installed. Please reboot and run "/usr/sbin/freebsd-update install" again to finish installing updates.
リブートして、再び freebsd-update install を発行。
# freebsd-update install Installing updates... Completing this upgrade requires removing old shared object files. Please rebuild all installed 3rd party software (e.g., programs installed from the ports tree) and then run "/usr/sbin/freebsd-update install" again to finish installing updates.
この作業も結構時間が掛かりました。
このあとパッケージの更新を行って、freebsd-update install コマンドを発行すれば 9.3 への更新は完了よてい。パッケージが依存関係の都合で550個以上もインストールされているため、バイナリーインストールで終了させます。
まとめ
もし、OSバージョンアップのリモートメンテナンス中に /boot/kernel がダメージを受けて再起動しないかもしれないと思った場合、
- メンテナンスは諦めて、rollback で元に戻す。
- 戻せないなら、/sys/src から新しいカーネルを作る。
- リブートしてしまった場合は、現場のエンジニアに旧カーネルからの起動を依頼する。
9.x 以降はブートメニューの中に、別のカーネルから起動するためのメニュー 5 が加わっているので、もう一つのカーネルを選択して、 Enter を押してくれるように依頼する。ネットワーク経由で入れれば、その後は何とかなります。