RHEL 上級 #1 ブートプロセス — GRUB2、dracut、レスキューモード

読了 11分

RHEL 上級シリーズをブートプロセスで始めます。普段は電源を押せば自動でログインプロンプトが出るので、ブート段階はほとんど意識しません。ところがいったんブートが止まったり、カーネルオプションを変えなければならなかったり、root パスワードを忘れたマシンを救出する場面では、「電源 → ログイン」の間に何が起きているかを知らないと手が出ません。この記事ではその間を段階別に分解し、各段階で何かが壊れたときにどう入り込んで直すかまで整理します。

RHEL 上級 シリーズでこの記事の位置:

  • #1 ブートプロセス — GRUB2、dracut、レスキューモード ← この記事
  • #2 カーネルチューニング — sysctl、tuned、kdump
  • #3 パフォーマンス分析 — sar、top/htop、iostat、vmstat、perf
  • #4 SELinux 上級 — ポリシー作成、audit2allow
  • #5 セキュリティ強化 — auditd、OpenSCAP、FIPS
  • #6 Subscription / Satellite / Insights
  • #7 Cockpit による GUI 管理と Web Console

RHEL 9 ブートの全体フロー #

電源が入ると、次の 5 段階が順番に進行します。

ブート 5 段階
1. ファームウェア (UEFI または BIOS)
       │  ESP (EFI System Partition) からブートローダー実行
2. GRUB2 (ブートローダー)
       │  /boot のカーネル + initramfs をメモリに読み込み
3. カーネル (vmlinuz) + initramfs
       │  必須ドライバで本物の root ファイルシステムを mount
4. systemd (PID 1)
       │  default.target まで unit 依存関係ツリーを辿って起動
5. ログインプロンプト (multi-user.target / graphical.target)

各段階は次の段階に たった 1 つだけ渡す 構造です。ファームウェアはブートローダーの場所さえ知っていればよく、GRUB2 はカーネルと initramfs をメモリに載せた時点で仕事が終わります。カーネルは systemd を PID 1 として起動すれば自分の役目は終わりで、その先は systemd の領域です。段階を分けて見ると、どこで止まったかの診断がずっと楽になります。

段階 1 — UEFI/BIOS #

最近の RHEL 9 マシンはほぼすべて UEFI ブートです。UEFI ファームウェアはシステムディスクの EFI System Partition (ESP) を見つけて、その中のブートローダー実行ファイルを直接呼び出します。

UEFI ブートか確認
$ ls /sys/firmware/efi
config_table  efivars  fw_platform_size  fw_vendor  ...

# ディレクトリが存在すれば UEFI、なければレガシー BIOS

ESP は通常 /boot/efi に mount されます。

ESP マウントと GRUB2 EFI バイナリ
$ mount | grep efi
/dev/nvme0n1p1 on /boot/efi type vfat (...)

$ ls /boot/efi/EFI/redhat/
BOOTX64.CSV  fonts  grub.cfg  grubx64.efi  shimx64.efi

shimx64.efi がファームウェアが最初に呼び出すファイルです。Secure Boot 環境では shim が Microsoft の署名による信頼チェーンの中で GRUB2 (grubx64.efi) の署名を検証し、その後 GRUB2 を起動する役割を担います。RHEL の Secure Boot がきれいに動く理由がここにあります。

UEFI ファームウェアがどのブートローダーを起動するかは、NVRAM のブートエントリに記録されています。efibootmgr で確認します。

UEFI ブートエントリ確認
$ sudo efibootmgr -v
BootCurrent: 0001
Timeout: 0 seconds
BootOrder: 0001,0002,0000
Boot0001* Red Hat Enterprise Linux  HD(1,GPT,...)/File(\EFI\redhat\shimx64.efi)
Boot0002* UEFI: USB ...

順序を変えたり項目を追加・削除するときも同じコマンドを使います。Live USB から入って efibootmgr -c でブート項目を直接作るのが、GRUB2 が壊れたときの最後の手段になります。

段階 2 — GRUB2 #

GRUB2 (GRand Unified Bootloader 2) がやることは 1 行に要約できます。/boot からカーネルイメージ (vmlinuz) と initramfs をメモリに載せ、カーネルに引数を渡して制御を移す。

/boot のブート資産
$ ls /boot
config-5.14.0-...el9.x86_64
initramfs-5.14.0-...el9.x86_64.img
vmlinuz-5.14.0-...el9.x86_64
loader/
grub2/

grub.cfg は直接編集しない #

GRUB2 のメニューとブートオプションを保持するファイルには 2 種類あります。UEFI 環境なら /boot/efi/EFI/redhat/grub.cfg、レガシー BIOS なら /boot/grub2/grub.cfg。どちらも 自動生成ファイル なので直接触りません。

grub.cfg 生成
# UEFI
$ sudo grub2-mkconfig -o /boot/efi/EFI/redhat/grub.cfg

# レガシー BIOS
$ sudo grub2-mkconfig -o /boot/grub2/grub.cfg

grub2-mkconfig が読み取って組み立てる入力は次の 2 か所です。

  • /etc/default/grub — グローバルデフォルト (timeout、デフォルトカーネル引数など)
  • /etc/grub.d/ — メニュー項目を生成するシェルスクリプト群 (普段は触らない)

/etc/default/grub の主要キー #

/etc/default/grub 例
GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)"
GRUB_DEFAULT=saved
GRUB_DISABLE_SUBMENU=true
GRUB_TERMINAL_OUTPUT="console"
GRUB_CMDLINE_LINUX="resume=/dev/mapper/rhel-swap rd.lvm.lv=rhel/root rd.lvm.lv=rhel/swap rhgb quiet"
GRUB_DISABLE_RECOVERY="true"
GRUB_ENABLE_BLSCFG=true
キー意味
GRUB_TIMEOUTメニュー待ち時間 (秒)。0 ならメニューを表示しない
GRUB_DEFAULTsaved なら最後に起動した項目がデフォルト。数字ならそのインデックス
GRUB_CMDLINE_LINUXカーネルが受け取る引数 — すべてのブート項目に共通で適用
GRUB_ENABLE_BLSCFGBLS (Boot Loader Spec) を使うか。RHEL 9 デフォルト true

GRUB_CMDLINE_LINUX から quiet を外して console=tty0 のようなオプションを追加すると、ブートメッセージが画面にそのまま流れます。ブートがどこで止まるかを追うときに最初に出すカードです。

修正後は必ず grub2-mkconfig で再生成しないと反映されません。

grubby — カーネル引数を 1 行で追加 #

GRUB_CMDLINE_LINUX を直接編集して grub2-mkconfig を回す流れが面倒なら、grubby 1 行で済みます。すべての BLS 項目に同一に適用 されます。

grubby 使用
# すべてのカーネルに引数を追加
$ sudo grubby --update-kernel=ALL --args="audit=1"

# 引数を削除
$ sudo grubby --update-kernel=ALL --remove-args="quiet"

# 現在ブート中のカーネルの引数確認
$ sudo grubby --info=$(grubby --default-kernel)

運用で推奨される流れです。/etc/default/grub の直接編集は本当にグローバルなオプションを変えるときだけにして、普段のカーネルパラメータ調整は grubby で。

BLS — Boot Loader Specification #

RHEL 9 がデフォルトで採用しているメニュー管理方式です。ブート項目 1 つにつき /boot/loader/entries/ の中にファイルを 1 つずつ置きます。

BLS 項目
$ ls /boot/loader/entries/
6a5f...77a-5.14.0-...el9.x86_64.conf
6a5f...77a-0-rescue.conf

$ cat /boot/loader/entries/6a5f...77a-5.14.0-...el9.x86_64.conf
title Red Hat Enterprise Linux (5.14.0-...el9.x86_64) 9.4 (Plow)
version 5.14.0-...el9.x86_64
linux /vmlinuz-5.14.0-...el9.x86_64
initrd /initramfs-5.14.0-...el9.x86_64.img
options root=/dev/mapper/rhel-root ro resume=... rhgb quiet
grub_users $grub_users
grub_arg --unrestricted
grub_class rhel

新しいカーネルをインストールすると kernel-install フックが自動で BLS 項目を追加します。grubby が修正対象とするのも、結局このファイル群です。

段階 3 — カーネルと initramfs (dracut) #

GRUB2 がメモリに載せた後、次の 2 つが組として動きます。

  • vmlinuz — 実際のカーネルイメージ
  • initramfs — カーネルが本物の root ファイルシステムを mount できるまで一時的に使う RAM ディスク

initramfs はなぜ必要か #

本物の root ファイルシステムが LVM 上に乗っていたり、RAID 上にあったり、NFS 上にあったり、LUKS で暗号化されているとします。カーネル自体がそれらすべてのケースに対応するドライバを内蔵すると巨大化し、セキュリティパッチサイクルも重くなります。そこで RHEL は 現在のマシンが root を mount するのに必要なドライバだけを抜き出して、一時 RAM ディスクとして束ねます。それが initramfs です。

ブートの流れ:

  1. カーネルがメモリで目を覚ます
  2. initramfs を RAM に展開して一時 root (/) として使う
  3. その中のツールで LVM 有効化 → 実際の root ファイルシステムを一時 mount
  4. pivot_root で本物の root へ切り替え
  5. systemd (/sbin/init) を実行

initramfs が壊れていたり必要なドライバが抜けていると、ブートが段階 3 で止まります。「Cannot find root device」 のようなエラーがそこです。

dracut — initramfs を作るツール #

dracut がモジュール式に initramfs を生成します。

dracut 基本
# 現在のカーネル用 initramfs を再生成 (上書き)
$ sudo dracut --force

# 特定のカーネルバージョン用
$ sudo dracut --force /boot/initramfs-$(uname -r).img $(uname -r)

# インストール済みのすべてのカーネル用
$ sudo dracut --force --regenerate-all

いつ作り直すかというと、

  • root が乗っているディスク/ストレージ構成を変えたとき (例: LVM → 通常パーティション)
  • カーネルモジュールを追加し、ブート段階から必要なとき
  • LUKS 暗号化を新たに有効/無効化したとき
  • /etc/dracut.conf.d/ の設定を変えたとき

initramfs の中を覗く #

入らずに確認
$ lsinitrd /boot/initramfs-$(uname -r).img | less

どのモジュールが入っているか、どのフックスクリプトがどの順で動くかが全部見えます。ブートが initramfs 段階で止まったときの最初の診断ツールです。

dracut モジュール設定 #

/etc/dracut.conf.d/*.conf に断片的な設定を書きます。

/etc/dracut.conf.d/network.conf 例
add_dracutmodules+=" network "
omit_dracutmodules+=" plymouth "

add_dracutmodules は強制包含、omit_dracutmodules は強制除外です。両方とも 両側に空白 がある点に注意 — 抜けると他のモジュール名と繋がって意図と異なる結果になります。

段階 4 — systemd が PID 1 に #

initramfs が本物の root に pivot_root した後、/sbin/init (実は /usr/lib/systemd/systemd へのシンボリックリンク) が PID 1 として起動します。その後は 基礎 #4 で扱った systemd の領域です。

default.target #

systemd は default.target まで unit 依存関係を辿って起動します。RHEL サーバでは通常 multi-user.target がデフォルトです。

default ターゲット確認/変更
$ systemctl get-default
multi-user.target

$ sudo systemctl set-default graphical.target
$ sudo systemctl set-default multi-user.target

ブート時間分析 #

systemd 段階で時間がどこで漏れているかを確認するツールがあります。

systemd-analyze
$ systemd-analyze
Startup finished in 2.5s (firmware) + 4.1s (loader) + 3.2s (kernel) + 8.7s (initrd) + 12.4s (userspace) = 30.9s
multi-user.target reached after 12.4s in userspace.

$ systemd-analyze blame
4.512s NetworkManager-wait-online.service
2.103s plymouth-quit-wait.service
1.876s dnf-makecache.service
...

$ systemd-analyze critical-chain
multi-user.target @12.4s
└─sshd.service @11.8s +0.6s
  └─network-online.target @11.7s
    └─NetworkManager-wait-online.service @7.2s +4.5s

blame は単に各サービスの起動時間を大きい順に並べ、critical-chaindefault ターゲットに到達するまでの直列依存パス を見せます。ブートが遅いときに手を入れる候補は critical-chain の上にいます。

段階 5 — レスキューモードとトラブルシューティング #

ブートがどこかで止まったら、次の問いはどうやって入るかです。止まった段階によって入り口のカードが違います。

rescue.target と emergency.target #

構造の違い
multi-user.target  ← 平常
rescue.target      ← root シェル。ローカルファイルシステム mount 済み、ネットワーク無し
emergency.target   ← root シェル。root のみ read-only mount、それ以外ほぼ何も上がらない

GRUB メニューで e を押してブート項目を編集し、linux ... 行の末尾に systemd.unit 引数を追加します。

GRUB 編集 — rescue に入る
linux ($root)/vmlinuz-... root=/dev/mapper/rhel-root ro ... systemd.unit=rescue.target

Ctrl+X でブートすると root シェルが立ち上がります。このモードはパスワードを要求します — root パスワードを知らない場合は次の手順へ進みます。

root パスワード復旧 — rd.break #

GRUB ブート項目の編集画面で linux 行に次を追加します。

GRUB 編集 — rd.break
linux ($root)/vmlinuz-... ... rd.break enforcing=0
引数意味
rd.breakinitramfs 段階で止めてシェルを立ち上げる。root パスワードを要求しない
enforcing=0SELinux Permissive でブート。パスワード変更後にラベルが崩れる問題を回避

Ctrl+X でブートすると switch_root:/# プロンプトが出ます。本物の root はまだ mount だけされて pivot_root 直前の状態です。

root パスワード変更手順
switch_root:/# mount -o remount,rw /sysroot
switch_root:/# chroot /sysroot
sh-5.1# passwd
New password: ...
sh-5.1# touch /.autorelabel       # 次回ブート時に SELinux ラベルを再設定
sh-5.1# exit
switch_root:/# exit               # 通常ブート再開

/.autorelabel を作っておくと、次回ブート時に systemd がすべてのファイルの SELinux ラベルを付け直します。少し時間はかかります (数分 ~ 十数分) が、enforcing=0 で変更した /etc/shadow のラベルがずれる事故を防げます。作らないと次回ブートで SSH ログインなどが塞がる可能性があります。

initramfs 段階で止まったとき #

「Warning: dracut-initqueue timeout」 のようなメッセージで止まると、root ファイルシステムが見つからない状態です。

チェック順:

dracut シェルから
# emergency シェルが出たら
dracut:/# cat /proc/cmdline       # カーネル引数確認 — root=... が正しいか
dracut:/# ls /dev/mapper/         # LVM デバイスが見えるか
dracut:/# vgchange -ay            # VG を手動有効化
dracut:/# blkid                   # ディスクシグネチャ確認

/etc/fstab に間違った UUID やデバイスを書いた直後に再起動すると、最もよく見る光景です。rescue ISO から入って chroot し、/etc/fstab を直すのが標準復旧手順です。

systemd 段階で止まったとき #

multi-user.target の直前で、あるサービスが上がらずブートが終わらないケースです。

次回ブートで
# emergency モードで起動 (GRUB 編集 → systemd.unit=emergency.target)
# あるいは通常ブート後に分析
$ systemctl list-jobs           # 進行中/止まっている job
$ systemctl status              # 全体状態ツリー
$ journalctl -b -1              # 直前ブートのログ
$ journalctl -b -1 -p err       # 直前ブートのエラーのみ

journalctl -b -1-1 は 「1 つ前のブート」 です。今回ブートしたログは -b 0 または単に -b。ブートが止まって強制再起動した直後の診断で最もよく使うオプションです。

よくある落とし穴 #

  • grub.cfg を直接編集 — 次に grub2-mkconfig が回ると変更分が丸ごと飛びます。常に /etc/default/grub または grubby 経由で。
  • カーネル引数変更後に grub2-mkconfig 漏れ/etc/default/grub だけ直して終わると、次回ブートに反映されません。UEFI と BIOS で出力先のパスが違う点もよく忘れます。
  • dracut 再生成忘れ — root ディスク構成を変えたのに initramfs をそのまま放置するとブートが止まります。ディスク作業直後に dracut --force を習慣化。
  • rd.break 後に /.autorelabel 漏れ — root パスワードを変えてそのままブートすると SELinux ラベルがずれて SSH ログインなどが塞がる場合があります。
  • enforcing=0 忘れrd.break だけで enforcing=0 を抜くと、SELinux がパスワードファイル変更を拒否することがあります。
  • GRUB timeout 0GRUB_TIMEOUT=0 にしておくとメニュー自体が出ず、GRUB 編集に入る機会を失います。本番マシンは最低 3 ~ 5 秒推奨。
  • multi-user ではなく graphical デフォルト — サーバに X が入って起動が GUI まで待たされます。サーバは multi-user.target で明示。

覚えておくコマンド #

作業コマンド
UEFI ブートエントリ確認sudo efibootmgr -v
GRUB メニュー再生成 (UEFI)sudo grub2-mkconfig -o /boot/efi/EFI/redhat/grub.cfg
カーネル引数追加/削除sudo grubby --update-kernel=ALL --args="..."
現在のカーネル引数確認sudo grubby --info=$(grubby --default-kernel)
initramfs 再生成sudo dracut --force --regenerate-all
initramfs の中を見るlsinitrd /boot/initramfs-$(uname -r).img
default ターゲット確認/変更systemctl get-default / set-default
ブート時間分析systemd-analyze blame / critical-chain
直前ブートのログjournalctl -b -1 -p err

まとめ #

  • ブートは 5 段階 — UEFI/BIOS → GRUB2 → カーネル+initramfs → systemd → デフォルトターゲット。どこで止まったかを段階別に押さえると診断が容易になります。
  • GRUB2 設定/etc/default/grub + grubby で管理。grub.cfg は自動生成なので直接編集しません。
  • initramfs — 本物の root を mount する前の一時 RAM ディスク。ディスク構成を変えたら dracut --force
  • レスキューモードrescue.target (ネットワーク無しのローカルシェル)、emergency.target (read-only root シェル)。GRUB 編集で進入。
  • root パスワード復旧rd.break enforcing=0 で initramfs シェル → chroot /sysrootpasswd/.autorelabel

次回はブート直後にカーネルがどう振る舞うかを決める カーネルチューニング です。sysctl でランタイムパラメータを調整し、tuned プロファイルでワークロード別最適化を適用し、kdump でカーネルパニック時点のダンプを取る流れまで 1 サイクルで扱います。

X