第1章 システムのブートプロセスとGRUB
LPIC-1 101.2 / 101.3 相当
パソコンの電源を入れてからログイン画面が出るまでに、内部では何段階もの処理が行われています。この流れを理解しておくと、起動トラブルの切り分けがしやすくなります。
ブートプロセスの流れ
- BIOS / UEFI: 電源投入後、ハードウェアの初期チェック(POST)を行い、起動デバイスを探す。
- ブートローダー(GRUB2など): 起動するカーネルを選択し、メモリに読み込む。
- カーネルの起動: ハードウェアを初期化し、ルートファイルシステムをマウントする。
- init / systemd: 最初に起動するプロセス(PID 1)。以降のサービスを順に起動していく。
GRUB2(GRand Unified Bootloader)
多くのLinuxディストリビューションで使われるブートローダーです。設定ファイルは /etc/default/grub で、これを編集した後は反映のために設定を再生成するコマンドを実行します。
$ sudo nano /etc/default/grub
$ sudo update-grub # Debian/Ubuntu系(Ubuntu 24.04 LTSの例)
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-6.8.0-49-generic
Found initrd image: /boot/initrd.img-6.8.0-49-generic
Found linux image: /boot/vmlinuz-6.8.0-48-generic
Found initrd image: /boot/initrd.img-6.8.0-48-generic
done
$ sudo grub2-mkconfig -o /boot/grub2/grub.cfg # RHEL系(Rocky Linux 9の例)
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-5.14.0-427.13.1.el9_4.x86_64
Found initrd image: /boot/initramfs-5.14.0-427.13.1.el9_4.x86_64.img
done
この行のこの値に注目: どちらのディストリビューションでも、Found linux imageの行に列挙されたカーネルのバージョン分だけ、GRUBの起動メニューに項目が追加されます。カーネル更新後にこの一覧が増えていることを確認すれば、新しいカーネルが正しく認識されていることが分かります。
ランレベルとsystemdターゲット
従来のSysV initでは「ランレベル」という番号でシステムの状態(マルチユーザーか、GUIありかなど)を管理していました。現在主流のsystemdでは、これに近い概念を「ターゲット」という単位で扱います。
| ランレベル(旧式) | systemdターゲット | 意味 |
|---|---|---|
| 0 | poweroff.target | シャットダウン |
| 1 | rescue.target | シングルユーザー(レスキュー)モード |
| 3 | multi-user.target | CUIのマルチユーザーモード |
| 5 | graphical.target | GUIありのマルチユーザーモード |
| 6 | reboot.target | 再起動 |
$ systemctl get-default # 既定のターゲットを確認
multi-user.target
$ sudo systemctl set-default multi-user.target
Removed "/etc/systemd/system/default.target".
Created symlink /etc/systemd/system/default.target → /usr/lib/systemd/system/multi-user.target.
$ sudo systemctl isolate rescue.target # 一時的に切り替え
(実行するとその場でrescue.targetへ切り替わり、多くのサービスが停止するため、通常はコマンドの応答がそのまま画面に表示される前に接続が切れる)
この行のこの値に注目: set-defaultは既定のターゲットを恒久的に変更するだけで、実行してもシステムの状態そのものはすぐには変わりません。一方isolateは「今すぐそのターゲットの状態に切り替える」即時実行のコマンドで、rescue.targetのように多くのサービスを止めるターゲットを指定すると、SSH接続をしている場合は接続自体が切断されることがあるため注意が必要です。
SysV initの仕組み(systemd以前の管理方式)
systemdが主流になる以前、多くのディストリビューションではSysV initという方式でサービスの起動を管理していました。今でも一部の古い環境や、LPIC試験の出題範囲としては現役の知識であるため、systemdとの対比で仕組みを押さえておきます。
SysV initでは、起動時にどのランレベルへ入るかを/etc/inittabというファイルで定義していました。代表的な行の書式は次のとおりです。
/etc/inittab(書式の例)
id:3:initdefault:
l3:3:wait:/etc/rc.d/rc 3
id:3:initdefault:は「既定のランレベルは3」という意味で、systemdのsystemctl set-defaultに相当する設定です。2行目のようなl<N>:<N>:wait:...という行が、各ランレベルに入ったときに何を実行するかを指定します。
各ランレベルで実際にどのサービスを起動・停止するかは、/etc/rc<N>.d/(ランレベルNに対応するディレクトリ、例:/etc/rc3.d/)以下に置かれた、S(Start)またはK(Kill)から始まるシンボリックリンクの一覧で決まります。
$ ls /etc/rc3.d/
S10network S20sshd S50cron K20apache2 K80nfs
Sから始まるリンクはそのランレベルに入るときに起動するサービス、Kから始まるリンクは停止するサービスを表し、リンク名の数字部分(S10・S20など)が実行順序を決めます。これらのリンクの実体は/etc/init.d/以下にある実際の起動スクリプトです。
Wants=/After=のような明示的な依存関係ではなく、単純な「番号順」で起動・停止するだけの仕組みです。この違い(明示的な依存関係グラフか、番号による直列実行か)は、systemdへの移行理由としてもよく説明されるポイントです。
これらのシンボリックリンクを手作業で管理するのは煩雑なため、update-rc.d(Debian系)やchkconfig(RHEL系)というコマンドで、サービスの有効化・無効化を一括管理していました。
| 操作 | Debian系(update-rc.d) | RHEL系(chkconfig) |
|---|---|---|
| サービスを有効化 | update-rc.d apache2 defaults | chkconfig httpd on |
| サービスを無効化 | update-rc.d apache2 remove | chkconfig httpd off |
| 現在の設定を確認 | (rc*.d内のリンクを直接確認) | chkconfig --list |
systemdでは、これらに相当する操作はsystemctl enable / systemctl disableで行います。内部的にも、有効化とはユニットファイルへのシンボリックリンクを所定のディレクトリに作成することであり、「シンボリックリンクの有無でサービスの自動起動を管理する」という発想自体はSysV initから受け継がれています。
telinitとinitの関係、acpidによる電源イベント処理
telinitは、稼働中のシステムに対して「ランレベルを変更してほしい」と指示するコマンドで、実体としてはinitプロセス(PID 1)にシグナルを送っているだけです。例えばtelinit 1はシングルユーザーモードへの切り替えを、telinit 6は再起動を意味していました。systemd環境では、PID 1がsystemdに置き換わっているため、telinitコマンド自体も互換性のために残されており、内部的には対応するsystemdターゲットへのsystemctl isolate相当の処理に変換されます。
$ sudo telinit 3 # systemd環境でも動作し、multi-user.targetへの切り替えとして扱われる
電源ボタンが押されたときのようなハードウェアイベントの処理には、伝統的にacpidというデーモンが使われてきました。ACPI(Advanced Configuration and Power Interface)が通知するイベント(電源ボタン押下、蓋を閉じたなど)を受け取り、あらかじめ設定したスクリプト(安全なシャットダウン処理など)を実行する役割を持ちます。systemdを採用する環境の多くでは、systemd-logind がこの役割の大部分を肩代わりしていますが、より細かいACPIイベントへの対応が必要な場合は、今でもacpidが使われることがあります。
initramfs(初期RAMディスク)の役割
カーネルが起動した直後は、まだルートファイルシステムをマウントするために必要なドライバ(RAIDやLVM、暗号化ディスクのモジュールなど)を読み込めていないことがあります。この問題を解決するのがinitramfs(初期RAMディスク)です。ブートローダーはカーネルと一緒にinitramfsをメモリに読み込み、そこに含まれる最小限のドライバとツールでルートファイルシステムをマウントできる状態にしてから、本来の起動処理を引き継ぎます。
$ ls -lh /boot/initramfs-*.img # RHEL系(Debian系は initrd.img という名前)
-rw-------. 1 root root 89M Aug 15 03:12 /boot/initramfs-5.14.0-427.13.1.el9_4.x86_64.img
-rw-------. 1 root root 71M Jul 2 09:44 /boot/initramfs-5.14.0-427.9.1.el9_4.x86_64.img
$ sudo dracut --force # initramfsを再生成(RHEL系)
dracut: Executing: /usr/bin/dracut --force
dracut: *** Creating image file '/boot/initramfs-5.14.0-427.13.1.el9_4.x86_64.img' ***
dracut: *** Creating initramfs image file '/boot/initramfs-5.14.0-427.13.1.el9_4.x86_64.img' done ***
$ sudo update-initramfs -u # initramfsを再生成(Debian系)
update-initramfs: Generating /boot/initrd.img-6.8.0-49-generic
この行のこの値に注目: ls -lhの出力にあるファイルサイズ(89M・71M)は、そのカーネル向けに組み込まれたドライバやモジュールの量を反映しており、更新のたびに新しいバージョンのファイルが増えていきます。dracut・update-initramfsのどちらも、最後に「done」や生成先のパスが表示されれば正常に再生成が完了したサインです。
起動ログの確認とsystemd-analyze
起動時に何が起きたかを後から調べるには、journalctlのブートログ表示や、起動時間を分析するsystemd-analyzeが役立ちます。
$ journalctl -b # 今回起動分のログのみ表示
-- Boot 3f1a9c7e2b4d4e3f9a1b2c3d4e5f6a7b --
Aug 21 07:02:11 web01 kernel: Linux version 6.8.0-49-generic ...
Aug 21 07:02:12 web01 systemd[1]: Starting Journal Service...
Aug 21 07:02:13 web01 systemd[1]: Started Journal Service.
...(この後も起動時のログが続く)
$ journalctl -b -1 # 1つ前の起動分のログ
-- Boot 1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d --
Aug 20 21:15:04 web01 kernel: Linux version 6.8.0-48-generic ...
...(1つ前の起動分のログが続く)
$ systemd-analyze # 起動全体・カーネル・ユーザー空間それぞれの所要時間
Startup finished in 4.021s (kernel) + 8.512s (userspace) = 12.533s
graphical.target reached after 8.498s in userspace.
$ systemd-analyze blame # 起動を遅くしているサービスの特定に使う
5.203s NetworkManager-wait-online.service
1.812s dev-sda1.device
0.891s snapd.service
0.622s systemd-udev-settle.service
...(以下、時間の短いサービスが続く)
この行のこの値に注目: journalctl -bの先頭にある-- Boot xxxx --という行は起動ごとに異なるIDで、これを目印に「どの起動時のログか」を区別できます。systemd-analyze blameの出力は所要時間の降順に並ぶため、一覧の一番上(この例ではNetworkManager-wait-online.service)が起動時間短縮の調査で最初に疑うべき対象になります。
また、GRUBのメニュー画面でカーネル行を選択してeキーを押すと、起動パラメータを一時的に編集できます。ルートパスワードを忘れた場合などにinit=/bin/bashを追加してシングルユーザーモード相当で起動し、パスワードをリセットするといった復旧作業に使われます(この変更は次回起動時には元に戻ります)。
GRUBの設定は「2段構え」になっている
GRUB2の実際の設定ファイルは /boot/grub/grub.cfg(または /boot/grub2/grub.cfg)ですが、このファイルは自動生成されるものであり、直接編集すべきではありません。管理者が編集するのはあくまで /etc/default/grub(起動時のデフォルト項目や待機時間などの設定)と /etc/grub.d/以下のスクリプト(メニュー項目を生成する仕組み)で、そこから update-grub や grub2-mkconfig が最終的な grub.cfg を組み立てます。
grub.cfg を直接手で書き換えてしまうと、次に update-grub が実行されたタイミング(カーネル更新時など)で変更が上書きされて消えてしまいます。恒久的な変更は必ず /etc/default/grub 側に対して行いましょう。
ブートローダー自体の再インストール
ディスクの先頭領域(MBR)やEFIシステムパーティションに書き込まれているブートローダーそのものが壊れてしまった場合は、設定ファイルの再生成だけでは直りません。その場合は grub-install でブートローダー本体を書き直します。
$ sudo grub-install /dev/sda # BIOS/MBR環境: ディスク全体を指定(パーティションではない)
Installing for i386-pc platform.
Installation finished. No error reported.
$ sudo grub-install --target=x86_64-efi --efi-directory=/boot/efi # UEFI環境
Installing for x86_64-efi platform.
Installation finished. No error reported.
$ sudo update-grub # 本体書き込み後に設定ファイルも再生成しておく
Generating grub configuration file ...
done
この行のこの値に注目: どちらの環境でも最後の行にInstallation finished. No error reported.と表示されれば、ブートローダー本体の書き込みが成功したサインです。この行が出ずにエラーメッセージで終わった場合は、指定したディスク名やEFIディレクトリのパスを再確認してください。
レスキューモードで起動したLive USBなどから chrootして実行することが多く、実機のトラブルシューティングでは定番の復旧手順です。
systemdのユニットと依存関係
systemdはサービスやマウント、ターゲットなどをすべて「ユニット」という単位で管理します。ユニットファイルには、他のユニットとの依存関係や起動順序を指定するディレクティブがあります。
| ディレクティブ | 意味 |
|---|---|
Wants= | そのユニットが起動するときに一緒に起動してほしいユニット(失敗しても本体には影響しない) |
Requires= | 必須の依存関係(依存先が失敗すると自身も失敗扱いになる) |
After= | 指定したユニットの後に起動する(順序のみを指定し、依存関係そのものは作らない) |
Before= | 指定したユニットより前に起動する |
たとえば graphical.target は内部的に multi-user.target を Requires= かつ After= で参照しており、「CUIのマルチユーザー環境が整った後にGUI関連のサービスを起動する」という積み重ねの構造になっています。
$ systemctl list-dependencies graphical.target # そのターゲットが何に依存しているかをツリー表示
graphical.target
● ├─multi-user.target
● │ ├─basic.target
● │ ├─getty.target
● │ ├─NetworkManager.service
● │ └─sshd.service
● └─display-manager.service
$ systemctl cat sshd.service # ユニットファイルの内容を確認
# /usr/lib/systemd/system/sshd.service
[Unit]
Description=OpenSSH server daemon
After=network.target sshd-keygen.target
Wants=sshd-keygen.target
[Service]
ExecStart=/usr/sbin/sshd -D $OPTIONS
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
[Install]
WantedBy=multi-user.target
この行のこの値に注目: list-dependenciesのツリー表示では、インデントの深さが依存関係の階層を表し、●の色(実際の端末では緑や赤)でそのユニットが起動済みかどうかも分かります。systemctl catは実際に読み込まれているユニットファイルの中身をそのまま表示するため、[Service]セクションのExecStartを見れば、そのサービスが実際にどのコマンドで起動しているかを確認できます。
豆知識: UEFIとSecure Boot
近年のPCの多くはBIOSではなくUEFIを採用しています。UEFIにはSecure Bootという機能があり、デジタル署名されたブートローダー・カーネルしか起動を許可しない設定にできます。自作のカーネルモジュールを使う場合などにSecure Bootが原因で読み込みに失敗することがあるため、実機のトラブルシューティングでは「Secure Bootが有効になっていないか」も確認ポイントの1つになります。
クラウド環境でのブートプロセス
AWSやGoogle Cloudなどのクラウド上の仮想マシンでは、物理的なBIOS/UEFI画面を直接見ることはできませんが、内部的なブートの流れ(ブートローダー→カーネル→initramfs→systemd)は物理サーバーと変わりません。違いとして、クラウドのインスタンスではcloud-initという仕組みが起動の初期段階で動作し、インスタンス作成時に指定したホスト名の設定やSSH鍵の配置、初期スクリプトの実行などを自動的に行います。「起動直後なのに、指定した設定が反映されていない」という場合は、systemdのログに加えてcloud-init statusや/var/log/cloud-init.logも確認すると原因が見つかることがあります。
ディスクレイアウト設計の考え方
OSをインストールする際、ディスクをどのようにパーティション分割するかは、起動の安定性やトラブル発生時の被害範囲に直結する設計判断です。ここでは、代表的な分割ポイントごとの考え方を整理します。
| 分割ポイント | 分ける理由・考え方 |
|---|---|
/bootを分離する | ルートファイルシステムがLVMや暗号化など複雑な構成の場合でも、ブートローダーが確実に読み込める単純な構成にしておくため。ルート側で問題が起きても、起動に必要な最小限の領域は独立して守られる。 |
| ESP(EFI System Partition) | UEFI環境で必須のパーティション。ブートローダーや各OSのブートエントリを格納するだけの用途なので、一般的には数百MB程度(多くの実務では512MB前後)を確保すれば十分とされる。 |
/homeを分離する | OS本体を再インストールする際にも、ユーザーのデータを別パーティションとして温存できる。ユーザーデータの肥大化がOS領域を圧迫することも防げる。 |
/varを分離する | ログやメールキューが溜まり続けてディスクを圧迫しても、ルートファイルシステム全体を巻き込まずに済む。特にログが際限なく増える可能性のあるサーバー用途では重要な分割ポイント。 |
| swap領域のサイズ | 通常はメモリ不足時の退避用として、物理メモリの一部〜同量程度を目安にすることが多い。ハイバネーション(メモリの内容を丸ごとディスクに退避して電源を切る機能)を使う場合は、メモリの内容をすべて書き出せるだけの容量(物理メモリと同量以上)が必要になる。 |
/varを独立させずにルート直下に置いていると、ログの肥大化やメールキューの滞留だけでルートファイルシステムの空き容量が尽き、システム全体が不安定になることがあります。特に予期しないエラーで大量のログを吐き続けるアプリケーションが動くサーバーでは、/var(あるいは/var/logだけでも)を独立したパーティションにしておくと、被害をその領域だけに閉じ込められます。
クラウド環境の仮想マシンでは、物理サーバーほど細かくパーティションを分割しない構成が一般的です。ルートボリューム1つだけをアタッチし、必要に応じて追加のデータボリュームをアプリケーションのデータ用に後から追加する、という運用が多く見られます。これは、クラウドではボリューム単位でのスナップショットやサイズ変更が容易なため、OSインストール時に厳密な区分けをしなくても、後から柔軟に対応しやすいという事情によるものです。
起動しなくなったサーバーへの向き合い方
本章の内容は、平常時よりもむしろ「サーバーが起動しなくなった」という緊急時にこそ真価を発揮します。画面に何も表示されない場合はまずBIOS/UEFIレベルの問題(ハードウェア故障やブート順序の設定ミス)を疑い、GRUBのメニューは出るがそこから先に進まない場合はカーネルやinitramfsの問題を疑う、というように、どの段階で止まっているかを見極めることが第一歩です。慌てて再インストールに走る前に、レスキューモードや外部メディアからの起動で原因を特定できないか試すことで、データを保ったまま復旧できる可能性が大きく広がります。