1. はじめに
Linuxを使っていて「新しいカーネルモジュールをインストールしたら起動しなくなった」「カーネルに渡すブートオプションを変更したら起動しなくなった」という経験をしたことがあるだろうか。
このような状況で便利なのが、次回起動時だけカーネルを変更する grub2-reboot コマンドだ。続けて再起動すると元のカーネルに戻るので、コンソールアクセスが制限されているクラウドでとくに便利だ。またテスト目的で複数カーネルを切り替えるときにも役に立つ。
意外に知られていないコマンドなので紹介しようと考えていたが、調べていくうちに結局以下のことを書くことになってしまった。
- GRUB2の設定ファイルと仕組み
- BIOSとUEFIにおける設定ファイルの違い
- RHEL7系とRHEL8/RHEL9系の設定方法の違い
- デフォルトカーネルを変更する方法
- 一時的にカーネルを変更する方法
1-1. 対象環境
- RHEL7/RHEL8/RHEL9などGRUB2を使用しているLinuxディストリビューション。CentOS StreamやOracle Linux、AlmaLinux、Rocky Linuxなど互換ディストリビューションを含む
2. どんなときに便利?
冒頭でも説明しているが、もう少し詳しく説明する。今まで正常起動していたサーバーが急に起動しなくなるとき、以下のような原因/きっかけが考えられる。
- カーネルをアップデートした(RPM/Yum/DNFでは、新しくインストールしたカーネルがデフォルトになる)
- 手でカーネルをビルドして変更した
- カーネルに渡すブートオプションを変更した
- デバイスドライバなど新しいカーネルモジュールをインストールした
- ハードウェア障害
このようなトラブルが発生したときは、以下の手順のトラブルシュートが必要だ。
起動しないときのトラブルシュート方法
- サーバーを再起動してコンソール画面を表示する
- 起動メニューから、起動実績のあるカーネルを選択する
- 正常起動したら、デフォルトカーネルを変更する
サーバー本体が手元にあるときは問題ないが、クラウドやデータセンターなど遠隔地にあるときは、コンソール画面を表示できず、古いカーネルを選択できないことがある。
パブリック・クラウド: コンソールログイン機能を提供していないベンダがある
データセンター: BMC/IPMIボードやKVM装置(PC切替器)を設定していないサーバーでは、データセンターに行く必要がある
grub2-rebootコマンドは起動カーネルを一時的に変更するだけなので、リブートさえできればリカバリーが可能だ。ただし、Yum/DNFでインストールしたときはデフォルトカーネルも更新されるので注意が必要だ。
3. GRUB2
GRUBの設定ファイルは、6系までのGRUBならば/etc/grub.conf
もしくは/boot/grub/grub.conf
、7系以降のGRUB2ならば/etc/grub2.cfg
が基本だ。ところがBIOSモードとUEFIモードでは異なる。とくに物理サーバーの場合、UEFIで使っていることがほとんどだろう。そのあたりを説明する。
3-1. GRUB2の設定ファイル
GRUB2には多くのファイルがあり、代表的なのは次のファイルだ。
-
/etc/grub2.cfg
/boot/grub2/grub.cfgへのシンボリックリンク -
/boot/grub2/grub.cfg
カーネルエントリの一覧など。8系にも存在するが内容は違う -
/boot/loader/entries/*
8系では、このディレクトリにエントリの一覧がある -
/boot/grub2/grubenv
デフォルトエントリの情報など -
/etc/default/grub
GRUB2全体の設定ファイル -
/etc/sysconfig/kernel
カーネルのアップデート方法など
この中でもデフォルトカーネルの変更などで参照するのは/etc/grub2.cfg
だ。7系でデフォルトカーネルの一般的な変更手順は次のとおり。8系ではgrubbyを使用する。
- カーネル一覧を表示。
awk -F\' '$1=="menuentry " {print i++ " : " $2}' /etc/grub2.cfg
2.デフォルトカーネルのエントリ番号を設定。
grub2-set-default エントリ番号
3./boot/grub2.cfg
はシンボリックリンクなので、信頼性確保のため実体の/boot/grub2/grub.cfg
を指定して再構築。
grub2-mkconfig -o /boot/grub2/grub.cfg
ヒント
8系ではgrubbyでの操作が必須だが、7系でもgrubbyを使用できる。ただし古いバージョンは使用できないかもしれない。
注意
6系までのGRUBでは、/boot/grub/grub.confをエディタで修正してデフォルトカーネルを変更した。しかし、GRUB2では/boot/grub2/grub.cfgを手で変更してはいけない。必ずgrub2-mkconfigやgrubbyなどのコマンドを使って変更する。
3-2. UEFIモードに気をつけろ
BIOSモードのときには、これまでの説明でよいのだが、UEFIモードでは異なる。一般には次のように説明されているが、実体に加えてシンボリックリンクも異なる。詳しく説明する。
BIOSブート:/boot/grub2/grub.cfg
UEFIブート:/boot/efi/EFI/*/grub.cfg
3-2-1. UEFIモードを判別する
現在使用しているサーバーや仮想マシンがUEFIモードかどうかは次のコマンドで判別できる。クラウドを調べたところ、AWSやAzureはBIOSモード、Oracle CloudはUEFIモードだった。すべてのインスタンスタイプを調べていないので参考程度に。
$ ls -l /sys/firmware/efi
total 0
-r--r--r--. 1 root root 4096 Aug 13 10:00 config_table
drwxr-xr-x. 2 root root 0 Aug 13 09:06 efivars
-r--r--r--. 1 root root 4096 Aug 13 10:00 fw_platform_size
-r--r--r--. 1 root root 4096 Aug 13 10:00 fw_vendor
-r--r--r--. 1 root root 4096 Aug 13 10:00 runtime
drwxr-xr-x. 9 root root 0 Aug 13 10:00 runtime-map
-r--------. 1 root root 4096 Aug 13 10:00 systab
drwxr-xr-x. 29 root root 0 Aug 13 10:00 vars
$ ls -l /sys/firmware/efi
ls: cannot access /sys/firmware/efi: No such file or directory
3-2-2. UEFIモードの実体ファイル
UEFIモードにおける設定ファイルは/boot/efi/EFI/*/grub.cfg
と紹介した。アスタリスクの部分はディストリビューションによって異なる。
ディストリビューション | ファイル名 |
---|---|
RHEL | /boot/efi/EFI/redhat/grub.cfg |
CentOS | /boot/efi/EFI/centos/grub.cfg |
Oracle Linux | /boot/efi/EFI/redhat/grub.cfg |
Amazon Linux | /boot/efi/EFI/amzn/grub.cfg |
Ubuntu | /boot/grub/grub.cfg(UEFI/BIOS共通) |
3-2-3. シンボリックリンクと実体の関係
おそらく戸惑うのはシンボリックリンクだろう。UEFIモードでは/etc/grub2-efi.cfg
が追加されている。
$ ls -l /etc/grub2*
lrwxrwxrwx. 1 root root 22 Sep 3 13:31 /etc/grub2.cfg -> ../boot/grub2/grub.cfg
$ ls -l /etc/grub2*
lrwxrwxrwx. 1 root root 22 Sep 3 13:31 /etc/grub2.cfg -> ../boot/grub2/grub.cfg
lrwxrwxrwx. 1 root root 31 Sep 3 13:31 /etc/grub2-efi.cfg -> ../boot/efi/EFI/
centos/grub.cfg
シンボリックリンクを使っていれば、よしなにやってくれそうだ。しかし/etc/grub2.cfg
は/boot/grub2/grub.cfg
を参照しているが実体は存在しない。
$ ls -l /boot/efi/EFI/centos/grub.cfg
-rwx------. 1 root root 10223 Sep 3 13:35 /boot/efi/EFI/centos/grub.cfg
$ ls -l /boot/grub2/grub.cfg ★存在しない
ls: cannot access /boot/grub2/grub.cfg: No such file or directory
そのためUEFIモードで次のコマンドは失敗する。
awk -F\' '$1=="menuentry " {print i++ " : " $2}' /etc/grub2.cfg
UEFIであることを認識し、/etc/grub2-efi.cfg
もしくは/boot/efi/EFI/*/grub.cfg
を指定する必要がある。
awk -F\' '$1=="menuentry " {print i++ " : " $2}' /etc/grub2-efi.cfg
3-3. その他のファイル
これまで説明していない、次のファイルを説明する。今回はCentOS 8の内容を表示しているが、7系でもほとんど同じだ。
- /etc/default/grub
- /boot/grub2/grubenv
- /etc/sysconfig/kernel
3-3-1. /etc/default/grub
このファイルにはGRUB2全体の設定が含まれている。デフォルトカーネルに関係するのはGRUB_DEFAULTディレクティブだ。キーが「saved」になっている。
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/cl-swap rd.lvm.lv=cl/root rd.lvm.lv=cl/swap rhgb quiet"
GRUB_DISABLE_RECOVERY="true"
GRUB_ENABLE_BLSCFG=true
3-3-2. /boot/grub2/grubenv
このファイルにはデフォルトカーネルの設定が含まれる。先ほど「saved」だったため、saved_entryディレクティブで指定したカーネルがデフォルトカーネルになる。
RHEL7系では「メニュー番号」もしくは「メニュー名」を設定されているが、RHEL8系では「ID+カーネルバージョン」が設定されている。
# GRUB Environment Block
saved_entry=CentOS Linux (3.10.0-1062.el7.x86_64) 7 (Core)
# GRUB Environment Block
saved_entry=0aa24a7da6284581821b2215d0757580-4.18.0-147.el8.x86_64
kernelopts=root=/dev/mapper/cl-root ro resume=/dev/mapper/cl-swap rd.lvm.lv=cl/root rd.lvm.lv=cl/swap rhgb quiet
boot_success=0
boot_indeterminate=0
RHEL8/9系では、一致する設定ファイルがこちらに存在する。これはBootLoaderSpecByDefault(BLS)という仕様である。
# ls /boot/loader/entries/
0aa24a7da6284581821b2215d0757580-0-rescue.conf
0aa24a7da6284581821b2215d0757580-4.18.0-147.el8.x86_64.conf
0aa24a7da6284581821b2215d0757580-4.18.0-193.14.2.el8_2.x86_64.conf
3-3-3. /etc/sysconfig/kernel
このファイルにはカーネルのアップデート方法が含まれる。
# UPDATEDEFAULT specifies if new-kernel-pkg should make
# new kernels the default
UPDATEDEFAULT=yes
# DEFAULTKERNEL specifies the default kernel package type
DEFAULTKERNEL=kernel-core
UPDATEDEFAULTは、カーネルをアップデートしたときデフォルトカーネルにするか否かを指定する。yesのときはデフォルトカーネルになるが、noにするとデフォルトカーネルにはならない。
たとえばyum update
でカーネルを追加インストールしても、新しいカーネルを使いたくないときにnoを指定する。
そもそもカーネルのインストール自体を抑制したいときは、次のようにYum/DNFの設定ファイルで除外設定したほうがいいだろう。
exclude=kernel
DEFAULTKERNELには、デフォルトカーネルの種類を設定する。通常のカーネルはkernelやkernel-coreだが、デバックカーネルではkernel-debugになる。またOracle LinuxのUEKではkernel-uekになっている。
4. 起動するカーネルを変更する
grub2-rebootの使い方を説明するつもりが、だいぶ遠回りをしてしまった。デフォルトカーネルを変更する方法もほとんど同じなので一緒に説明する。おもな手順は次のとおり。
- デフォルトカーネルを調べる
- カーネルのリストを表示する
- 次回起動するカーネルを設定する(永続or一時的)
- 再起動する
カーネルを変更するコマンドは、バージョンによって異なるので、それぞれについて説明する。7系でもgrubbyを使うときは、8/9系の説明を見てほしい。
7系: grub2-mkconfig/grub2-set-defaultもしくはgrubby
8/9系: grubby
注意
システム起動時のカーネル選択メニューを表示できないクラウド環境では、デフォルトカーネルを変更する前にバックアップすることを推奨。筆者はUbuntuでやからしました(笑)。RHEL系と似ているようで似ていないので別途執筆予定。
4-1. RHEL7系(非grubby)
4-1-1. デフォルトカーネルを調べる
現在のデフォルトはgrub2-editenv
コマンドで表示できる。saved_entryディレクティブの値がデフォルトカーネルだ。
# grub2-editenv list
saved_entry=CentOS Linux (3.10.0-1127.19.1.el7.x86_64) 7 (Core)
# uname -a
Linux hostname 3.10.0-1127.19.1.el7.x86_64 #1 SMP Tue Mar 17 23:49:17 UTC 2020
x86_64 x86_64 x86_64 GNU/Linux
4-1-2. カーネルのリストを表示する
インストールされているカーネルの一覧を表示する。先ほどの結果と合わせると、インデックス番号0がデフォルトカーネルだ。
BIOSモード
# awk -F\' '$1=="menuentry " {print i++ " : " $2}' /etc/grub2.cfg
UEFIモード
# awk -F\' '$1=="menuentry " {print i++ " : " $2}' /etc/grub2-efi.cfg
0 : CentOS Linux (3.10.0-1127.19.1.el7.x86_64) 7 (Core)
1 : CentOS Linux (3.10.0-1062.18.1.el7.x86_64) 7 (Core)
2 : CentOS Linux (3.10.0-1062.el7.x86_64) 7 (Core)
3 : CentOS Linux (0-rescue-b5a029229fd6464ca40d2e93351d5c5f) 7 (Core)
4-1-3. デフォルトカーネルを変更する
次の例では、一つバージョンが古いエントリ番号1のカーネルを指定している。
- デフォルトカーネルをエントリ番号1に変更する。
# grub2-set-default 1
2.デフォルトカーネルが変わっていることがわかる。
# grub2-editenv list
saved_entry=1
4-1-4. 再起動する
変更したカーネルを有効にするために再起動する。これで終了だ。
# systemctl reboot
4-2. RHEL7/RHEL8/RHEL9系(grubby)
4-2-1. デフォルトカーネルを調べる
grubbyではカーネルのファイル名やインデックスを表示できる。
# grubby --default-kernel
/boot/vmlinuz-4.18.0-193.14.2.el8_2.x86_64
# grubby --default-index
0
4-2-2. カーネルのリストを表示する
インストールされているカーネルの一覧を表示する。この例では-A 2
オプションを付けて、grepに一致する次の行も表示している。
# grubby --info=ALL | grep -A 2 index
index=0
kernel="/boot/vmlinuz-4.18.0-193.14.2.el8_2.x86_64"
args="ro crashkernel=auto resume=/dev/mapper/cl-swap rd.lvm.lv=cl/root rd.lvm.lv=cl/swap rhgb quiet $tuned_params"
--
index=1
kernel="/boot/vmlinuz-4.18.0-147.el8.x86_64"
args="ro crashkernel=auto resume=/dev/mapper/cl-swap rd.lvm.lv=cl/root rd.lvm.lv=cl/swap rhgb quiet $tuned_params"
--
index=2
kernel="/boot/vmlinuz-0-rescue-0aa24a7da6284581821b2215d0757580"
args="ro crashkernel=auto resume=/dev/mapper/cl-swap rd.lvm.lv=cl/root rd.lvm.lv=cl/swap rhgb quiet"
4-2-3. デフォルトカーネルを変更する
デフォルトカーネルを永続的に変更するには、次のようにgrubbyコマンドを使用する。今回の例では--set-default
オプションと値の間にイコール(=)を指定しているが、空白スペースでもよい。
# grubby --set-default=/boot/vmlinuz-4.18.0-147.el8.x86_64
次のようにインデックス番号も指定できる。
# grubby --set-default-index=1
変更されたことを確認する。
# grubby --default-kernel
/boot/vmlinuz-4.18.0-147.el8.x86_64
--info
を使うと、指定したカーネルのエントリ全体を表示できる。この例ではカーネルファイル名を指定しているが、インデックスも指定できる。
# grubby --info=/boot/vmlinuz-4.18.0-147.el8.x86_64
index=1
kernel="/boot/vmlinuz-4.18.0-147.el8.x86_64"
args="ro crashkernel=auto resume=/dev/mapper/cl-swap rd.lvm.lv=cl/root rd.lvm.lv
=cl/swap rhgb quiet $tuned_params"
root="/dev/mapper/cl-root"
initrd="/boot/initramfs-4.18.0-147.el8.x86_64.img $tuned_initrd"
title="CentOS Linux (4.18.0-147.el8.x86_64) 8 (Core)"
id="0aa24a7da6284581821b2215d0757580-4.18.0-147.el8.x86_64"
4-2-4. 再起動する
変更したカーネルを有効にするために再起動する。これで終了だ。
# systemctl reboot
4-3. 一時的にカーネルを変更する
先ほどはカーネルを永続的に変更したが、次回起動時だけカーネルを変更することもできる。もう一度、再起動すると元のカーネルに戻るので、次ような目的のときに便利だ。
- テスト目的で別のカーネルに変更したい
- デバックカーネルを使いたい
- カーネルに渡すブートオプションをテストしたい
変更にはgrub2-rebootを使う。次のようにメニューエントリ番号(インデックス番号)を指定する。
grub2-reboot 1
動作を確認するために、一連の流れで説明する。
1.現在のカーネルバージョンは「4.18.0-147」。
# uname -r
4.18.0-147.el8.x86_64
2.デフォルトカーネルも当然同じ。
# grubby --default-kernel
/boot/vmlinuz-4.18.0-147.el8.x86_64
# grubby --default-index
1
3.grub2-rebootを使い、次回再起動時だけインデックス"0"に変更する。
# grub2-reboot 0
4.デフォルトカーネルは変更していないのでインデックスは変わっていない。
# grubby --default-index
1
5.再起動する。
# systemctl reboot
6.再起動後にログインすると、インデックス"0"のカーネルに変わっている。
# uname -r
4.18.0-193.14.2.el8_2.x86_64
# grubby --info=/boot/vmlinuz-4.18.0-193.14.2.el8_2.x86_64
index=0
kernel="/boot/vmlinuz-4.18.0-193.14.2.el8_2.x86_64"
args="ro crashkernel=auto resume=/dev/mapper/cl-swap rd.lvm.lv=cl/root rd.lvm.lv=cl/swap rhgb quiet $tuned_params"
root="/dev/mapper/cl-root"
initrd="/boot/initramfs-4.18.0-193.14.2.el8_2.x86_64.img $tuned_initrd"
title="CentOS Linux (4.18.0-193.14.2.el8_2.x86_64) 8 (Core)"
id="0aa24a7da6284581821b2215d0757580-4.18.0-193.14.2.el8_2.x86_64"
7.さらに再起動する。
# systemctl reboot
8.デフォルトカーネルに戻っている。grub2-rebootの効力は、次回再起動時だけということが確認できた。
# uname -r
4.18.0-147.el8.x86_64
5. まとめ
- RHEL7以降のGRUB2ではコマンドを使ってデフォルトカーネルを変更する。手でファイルを変更してはいけない。
- BIOSモードとUEFIモードではGRUBの設定ファイル名が異なる。
- デフォルトカーネルを変更するには、RHEL7系ではgrub2-mkconfig/grub2-set-defaultもしくはgrubbyを使用する。RHEL8/9系ではgrubbyを使用する。
- grub2-rebootコマンドを使うと、次回起動時だけカーネルを一時的に変更できる。このような特性はコンソールアクセスが難しいクラウドやテストのときに便利。