2011/06/24(金)Linuxブートシーケンスまとめ。GRUBと起動トラブルのメモ
mdadmで作ったソフトウェアRAIDに障害があってRAIDを組みなおしてたのですが、まともに起動しないトラブルに見舞われて格闘してました。
その時に調べたGRUB2と起動の仕組みについてのメモ。
環境
- Ubuntu 10.04 / Debian 6
- ブートローダー GRUB2 (Ver1.98)
- 起動ディスク RAID1(ミラー)
- ファイルシステム構成 / から起動(/bootは / ファイルシステム内)*1
ここで説明するGRUBはGRUB2です。GRUB(Ver0.98)とは異なりますのでご注意ください。
GRUBが呼ばれるまで
BIOSからブートローダーに制御が移ります。GRUBには stage1 と stage2 の2つのブート段階があり、BIOSからstage1が呼び出されると別セクタにあるsatage2以降がロードされます。細かいことはともかく、GRUBはBIOSから直接起動されます。
GRUBにはext3/ext4等のファイルシステムを解読する能力があります。
/boot/grub/grub.cfg
をGRUBはファイルシステムを解釈して直接読み込みます。逆に言えば、/boot の存在するパーティションにはきちんとファイルシステムがあって、このファイルが置かれている必要があります。
GRUBはRAIDされたディスクでもここまで辿ることができますが、RAID構成情報が変わるとそれを認識することができないようで、RAID1構成でディスク1台構成から2台構成に変った時、grub-install を再度やり直す必要がありました。
きちんとパーティーションタイプを識別するようで、fdiskのトルグで一時的に違うものに変更してもロード失敗してました。
GRUBメニューとブートオプション
GRUBが grub.cfg をきちんと認識できると、GRUBメニューが立ち上がります。
実際に稼働中の Ubuntu 10.04 では次のようなエントリが構成されています。
menuentry 'Ubuntu, with Linux 2.6.32-32-generic' --class ubuntu --class gnu-linux --class gnu --class os { recordfail insmod raid insmod mdraid insmod ext2 set root='(md1)' search --no-floppy --fs-uuid --set dbba431a-7cc1 linux /boot/vmlinuz-2.6.32-32-generic root=UUID=dbba431a-7cc1 ro video=800x600-16 initrd /boot/initrd.img-2.6.32-32-generic } ※UUIDは長いだけなので一部省略しました。
この中で重要な情報は、当然 linux と書かれたカーネル指定ですが、同じぐらい重要なのが initrd です。これについては後で述べます。
- recordfail は grub に対するオプションです。debian 6(squeeze)のgrubでは無効でした。
- insmod raid, insmod mdraid は grub に対するオプションです。モジュールを読み込めという指定ですが、このモジュールは /boot/grub/*.mod に実際に存在します。
set root='(md1)' はルート(/)ファイルシステムの存在するパーティションを指定します*2。指定できるデバイスの一覧は、GRUBコンソールモードで「ls」を入力すると出てきます。これはGRUBに対する指定です。
HDDのパーティションを指定するときは、set root='(hd0,msdos2)'という風に指定します。これは /dev/sda2 を指定するようなものです。ですが、2番目以降のHDDから起動した場合、そのHDDが例え /dev/sdb に相当するものでも、GRUBを起動しているHDDは常に hd0 になります。注意してください。hd1以降は、GRUBを起動しているHDDを除いてデバイス順に並べたものです。また、(hd0,1)(/dev/sda2相当)といった旧来のGRUB方式の指定は無効です。これも注意してください。
search という以前のGRUBには無かった行は「set root=」の代替手段を示しています。「set root=」が指定されないとき、または「set root=」で指定されたデバイスが無効であるとき、GRUBは「--set」以降で指定されたUUIDを持つファイルシステムを探索し、それをGRUBに対するルート(/)ファイルシステムだと解釈します。
linux 行にある「root=UUID=dbba431a-7cc1-a56e」はlinux kernelに対するルート(/)ファイルシステムの指定でまた意味が違ってきます。
GRUBのルート(/)ファイルシステムとはなんぞや?
GRUBの重要な役割はカーネルをメモリに展開(ロード)して、起動シーケンスをカーネルに渡すことです。ロードするカーネルはどこに書かれているかというと、HDDのパーティション内に書きこまれています。
GRUBはファイルシステムを(insmodすればRAIDすらも)理解することができますが、膨大なファイルの中からカーネルを自動で探すことはできません。カーネルがどこに存在するのかきちんとGRUBに教える必要があります。
GRUBのルート(/)ファイルシステムとは、Linuxカーネルをロードするときのファイルシステムの基準(/)を指定するものです。もし、/boot パーティションが /dev/sda1 に分かれていて、/dev/sda3 がLinuxのルート(/)ファイルシステムならばGRUBは次のように書かれます。
set root='(hd0,msdos1)' linux /vmlinuz-2.6.32-32-generic root=/dev/sda3 ro initrd /initrd.img-2.6.32-32-generic
もしsearch行を書くなら、そこでは /dev/sda1のUUID なりを記述します。
また見て分かるとおりlinux, initrd で書かれたパスが異なっています。これはroot='(hd0,msdos1)'で指定した位置を基準(/)としたパスで記述する必要があるからです。
Linuxカーネルとカーネルモジュール
GRUBオプションにより正しくLinuxカーネルを発見できたら、GRUBはそれをファイルシステムからロードし、また同時にinitrdが指定されていればinitramfsを展開します。
Linuxが起動しないというトラブルにも何段階かありますが、カーネルがとりあえずロードされるなら、GRUBの設定には(linux行のroot指定やinitrdが合っていれば)問題はないと言えます。
昔のLinuxカーネルはモノリシックな作りになっていて、カーネルが標準で対応していないデバイスを使いたい場合、多くはカーネルを全部つくり直す必要がありました*3。かといって予めすべてのデバイスに対応するようなカーネルを作っておくとカーネルが大きくなってそれはそれでメモリを圧迫しましたし起動もロードも遅かったのでした*4。
今はカーネルモジュール(*.ko)と言って、デバイスドライバの部分を切り離して別ファイルとして置いておき、その特定のデバイスを見つけ必要になったとき自動ロードするようになりました。
しかし、これには大きな問題があります。カーネルモジュールは当然ファイルシステムのどこかに置かれているわけですが、ファイルシステムを認識する前に必要なモジュールや設定情報はどうやって知ればいいのでしょうか?
例えば、標準のディスク(ATAやSATAのext3やext4)なら当然カーネルに組み込まれていますが、ソフトウェアRAIDや専用のRAIDカードから起動されたとき、モジュールを読み込もうにもモジュールを読み込むのに必要なデバイスドライバをカーネルが持っていないので起動できません。*5
GRUBの「root=」で正しくLinuxルート(/)ファイルシステムを指定しているのに、ルート(/)ファイルシステムのマウントに失敗して起動できない原因はここにあります。
Begin: Mounting root file system...
と表示される当たりで起動シーケンスが止まってしまうのです。
しかしだからと言って、起動時に必要になるかもしれないすべてのデバイスをカーネルに組み込むのも考えものです。メモリとリソースのムダでしかありません。これを解決するためLinux2.6から導入されたのがinitramfsです。
initramfs
カーネル起動時にあらかじめメモリに展開することでファイルシステムを構成する仕組みです。CDブートするディスクトップ環境とかでも使われてるメモリファイルシステムです。GRUBで指定する initrd行 がそれです。
メモリなのでカーネル起動時でもファイルを参照することができ、必要に応じてモジュールをロードできます。そして、本来のルート(/)ファイルシステムをマウントする段階で破棄されますのでメモリを圧迫することもありません。
いいこと尽くめなのですが、1つだけ難点がありあらかじめinitrd.imgとしてRAMファイルシステム全体を固めておく必要があります。UbuntuやDebianでは次のコマンドを使用します。
# update-initramfs -u
設定等は /etc/initramfs にありますが特にいじる必要はなさそうです。
update-initramfsは次のような場合に実行する必要があります。
- カーネルモジュールを手動追加したり、blacklist登録したとき。
- ソフトウェアRAIDを組んでいて、RAID構成情報(/etc/mdadm/mdadm.conf)を変更した場合。→詳細はこちら
Begin: Mounting root file system...
が表示されるまでが、initramfsでの動作になります。
復旧時に大事なchrootする方法
/dev/md1 が復旧対象のLinuxのルートファイルシステムです。
#!/bin/sh mount /dev/md1 /main-root mount --bind /dev /main-root/dev mount --bind /dev/pts /main-root/dev/pts mount --bind /proc /main-root/proc mount --bind /sys /main-root/sys chroot /main-root
mount --bindで、chroot後もデバイスを参照できるようにしてあげるのがコツらしい。