BIOSのPOSTだけで3分かかるサーバー再起動——もっと速い方法はないのか?
本番サーバーでカーネルを更新するたびに、あの長々とした起動プロセスを待たされる:BIOSがRAM、CPU、デバイスをチェックし、ブートローダーに制御を渡し、ようやくカーネルを読み込む。4GBのRAMを搭載した古い物理サーバーでは、POSTフェーズだけで90秒かかる。10台のサーバーに掛け算すると、カーネルパッチのたびに約15分のダウンタイムになる。
問題はLinuxの起動が遅いわけではない——カーネルの交換とは無関係なBIOS/UEFIハードウェアが、システム全体をその手順に通すことを強要しているのだ。kexecはまさにこの問題を解決するために生まれた。
kexecとは何か、なぜ機能するのか
kexec(kernel executeの略)はLinuxカーネルのシステムコールで、実行中のカーネルが別のカーネルを直接メモリに読み込み、制御を引き渡すことを可能にします——BIOS/UEFIとブートローダー(GRUB)を完全にスキップして。
動作メカニズムは2つの独立したフェーズから構成されます:
- ロードフェーズ:
kexec -lコマンドを使って、カーネルイメージを予約メモリ領域に読み込む。このステップはシステムが通常稼働中に実行され、サービスを中断しない。 - 実行フェーズ:
kexec -e(またはsystemctl kexec)コマンドで実際に新しいカーネルへ移行する。ここで現在のカーネルはファイルシステムをアンマウントし、プロセスを停止してから、新しいカーネルのエントリポイントへ直接ジャンプする。
POSTなし。メモリテストなし。GRUBのカウントダウンなし。新しいカーネルは、まるでブートローダーから制御を受け取ったかのようにstart_kernel()から実行を開始する。
私が管理しているRAM 4GBのUbuntu 22.04サーバーでは、通常の再起動の約3分からkexecを使うことで30秒未満へと、処理時間が大幅に短縮されることを確認している。
実践:kexecのインストールと使い方
ステップ1:kexec-toolsのインストール
kexec-toolsはkexec(2)カーネルシステムコールと対話するためのユーザースペースツールを提供します。
# Ubuntu / Debian
sudo apt install kexec-tools -y
# RHEL / AlmaLinux / Rocky
sudo dnf install kexec-tools -y
# Arch Linux
sudo pacman -S kexec-tools
インストール後、Ubuntu/Debianではkexec-load.serviceというsystemdユニットが追加されます——このユニットはシャットダウン時に現在のカーネルを自動的にメモリに読み込み、kexec再起動の準備をします。
ステップ2:使用中のカーネルとinitrdを確認する
実行中のカーネルイメージとinitramfsの正確なパスを確認する必要があります:
# 実行中のカーネルバージョンを確認
uname -r
# 出力例: 6.8.0-45-generic
# Ubuntuでの通常のパス
ls /boot/vmlinuz-$(uname -r)
ls /boot/initrd.img-$(uname -r)
# 使用中のcmdlineを確認(ブートパラメータ)
cat /proc/cmdline
ステップ3:カーネルをメモリに読み込む
このステップは安全です——実行中のシステムを中断しません:
KERNEL_VER=$(uname -r)
sudo kexec -l /boot/vmlinuz-${KERNEL_VER} \
--initrd=/boot/initrd.img-${KERNEL_VER} \
--reuse-cmdline
# --reuse-cmdline: /proc/cmdlineの全パラメータを再利用する
# "root=UUID=... quiet splash"を再入力する必要がない
カーネルが読み込まれたか確認:
cat /sys/kernel/kexec_loaded
# 出力: 1 = 読み込み済み、0 = 未読み込み
ステップ4:kexec再起動を実行する
kexecをトリガーする方法は3つあります。制御レベルに応じて選択できます:
# 方法1: systemctl(推奨——グレースフルシャットダウンを先に実行)
sudo systemctl kexec
# 方法2: kexecを直接実行(グレースフル、先にサービスを停止)
sudo kexec -e
# 方法3: 通常のrebootに統合(Ubuntu/Debianのみ)
# kexec-toolsがインストールされていると、カーネルが読み込まれている場合は通常のrebootコマンドが自動的にkexecを使用する
sudo reboot
新しいカーネルを読み込む(apt upgrade後)
これが最も実践的なユースケースです——ダウンタイムを最小限に抑えたカーネルアップデート:
# カーネルをアップデート
sudo apt update && sudo apt upgrade -y
# 最新のインストール済みカーネルを検索
NEW_KERNEL=$(ls /boot/vmlinuz-* | sort -V | tail -1 | sed 's|/boot/vmlinuz-||')
echo "新しいカーネル: ${NEW_KERNEL}"
# 新しいカーネルをメモリに読み込む(まだ再起動しない)
sudo kexec -l /boot/vmlinuz-${NEW_KERNEL} \
--initrd=/boot/initrd.img-${NEW_KERNEL} \
--reuse-cmdline
# 準備:不要なサービスを停止し、データを同期
sudo sync
# kexec再起動——BIOS/UEFIを完全にスキップ
sudo systemctl kexec
# 起動後に確認
uname -r
複数サーバーへの自動化スクリプト
複数のノードにカーネルアップデートを展開する際に使用するスクリプトです:
#!/bin/bash
# fast-reboot.sh — 最新カーネルでkexec再起動
set -e
NEW_KERNEL=$(ls /boot/vmlinuz-* | sort -V | tail -1 | sed 's|/boot/vmlinuz-||')
CURR_KERNEL=$(uname -r)
if [ "${NEW_KERNEL}" = "${CURR_KERNEL}" ]; then
echo "新しいカーネルはありません。現在使用中: ${CURR_KERNEL}"
# 現在のカーネルを再読み込み(コールドリブートより速い)
fi
echo "カーネルを読み込み中: ${NEW_KERNEL}"
kexec -l "/boot/vmlinuz-${NEW_KERNEL}" \
--initrd="/boot/initrd.img-${NEW_KERNEL}" \
--reuse-cmdline
echo "5秒後にkexec再起動を開始します..."
sleep 5
systemctl kexec
kexecを使う際の注意点
kexecがすべての状況に適しているわけではない
通常の再起動が適切な場面もあります:
- ファームウェア/BIOSのアップデート:新しいファームウェアをフラッシュするにはPOSTを完全に通過する必要があります。
- カーネルパニックやメモリ破損:kexecは古いカーネルから壊れたハードウェア状態を引き継ぐ可能性があります。
- ハードウェアの変更(RAM追加、ディスク追加):BIOSが新しいデバイスを再検出する必要があります。
- EFI Secure Bootのシステム:一部のディストリビューションはSecure Bootが有効な状態でkexecをサポートしていません(カーネル設定に依存)。
カーネルがkexecをサポートしているか確認する
# CONFIG_KEXECを確認
grep CONFIG_KEXEC /boot/config-$(uname -r)
# 確認が必要: CONFIG_KEXEC=y
# CONFIG_KEXEC_FILEを確認(kexec -lはこれを使用)
grep CONFIG_KEXEC_FILE /boot/config-$(uname -r)
# CONFIG_KEXEC_FILE=y
Ubuntu、Debian、RHELのデフォルトカーネルはほとんどがどちらも有効になっています。自分でビルドしたカーネルの場合は、make menuconfigのProcessor type and featuresセクションで有効にしてください。
kdumpとkexec:どんな関係があるのか
kdump——カーネルクラッシュダンプをキャプチャするツール——も内部でkexecを使用しています。kdumpは小さなキャプチャカーネルを予約メモリ領域に読み込み、メインカーネルがパニックすると、コアダンプを書き込むためにキャプチャカーネルへ自動的にkexecします。kdumpを使用している場合でも心配不要です——独立したメモリ領域を使用するため、通常のkexec再起動と競合しません。
まとめ
kexecはLinuxの知られざる機能の中でも、極めて実用的なものの一つです。カーネルパッチ適用時のダウンタイムを最小化したい本番サーバーには、kpatchやlivepatchのような複雑なライブパッチングよりも、はるかにシンプルで効果的な解決策です。
典型的なフロー:apt upgrade → 新しいカーネルをkexec -l → サービスのグレースフルシャットダウン → systemctl kexec。再起動プロセス全体が3分ではなく30秒未満で完了します。多数のノードを運用するシステムでは、この差が積み重なると非常に大きなものになります。
Linuxサーバーを運用していてまだkexecを知らなかったなら、次回のカーネルアップデート時にぜひ試してみてください——初めて使ったときから違いは一目瞭然です。
