16
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

カーネルを一時的に変更できるgrub2-rebootが便利。結局GRUB2のことをいろいろ調べたよ

Last updated at Posted at 2020-09-05

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では、新しくインストールしたカーネルがデフォルトになる)
  • 手でカーネルをビルドして変更した
  • カーネルに渡すブートオプションを変更した
  • デバイスドライバなど新しいカーネルモジュールをインストールした
  • ハードウェア障害

このようなトラブルが発生したときは、以下の手順のトラブルシュートが必要だ。

起動しないときのトラブルシュート方法

  1. サーバーを再起動してコンソール画面を表示する
  2. 起動メニューから、起動実績のあるカーネルを選択する
  3. 正常起動したら、デフォルトカーネルを変更する

サーバー本体が手元にあるときは問題ないが、クラウドやデータセンターなど遠隔地にあるときは、コンソール画面を表示できず、古いカーネルを選択できないことがある。

パブリック・クラウド: コンソールログイン機能を提供していないベンダがある
データセンター: 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を使用する。

  1. カーネル一覧を表示。
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モードだった。すべてのインスタンスタイプを調べていないので参考程度に。

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
BIOSモードのとき
$ 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が追加されている。

BIOSモード
$ ls -l /etc/grub2*
lrwxrwxrwx. 1 root root 22 Sep  3 13:31 /etc/grub2.cfg -> ../boot/grub2/grub.cfg
UEFIモード
$ 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+カーネルバージョン」が設定されている。

7系
# GRUB Environment Block
saved_entry=CentOS Linux (3.10.0-1062.el7.x86_64) 7 (Core)
8/9系
# 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)という仕様である。

RHEL8/9系のみ
# 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の設定ファイルで除外設定したほうがいいだろう。

/etc/yum.conf(/etc/dnf/dnf.conf)
exclude=kernel

DEFAULTKERNELには、デフォルトカーネルの種類を設定する。通常のカーネルはkernelやkernel-coreだが、デバックカーネルではkernel-debugになる。またOracle LinuxのUEKではkernel-uekになっている。

4. 起動するカーネルを変更する

grub2-rebootの使い方を説明するつもりが、だいぶ遠回りをしてしまった。デフォルトカーネルを変更する方法もほとんど同じなので一緒に説明する。おもな手順は次のとおり。

  1. デフォルトカーネルを調べる
  2. カーネルのリストを表示する
  3. 次回起動するカーネルを設定する(永続or一時的)
  4. 再起動する

カーネルを変更するコマンドは、バージョンによって異なるので、それぞれについて説明する。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. デフォルトカーネルをエントリ番号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コマンドを使うと、次回起動時だけカーネルを一時的に変更できる。このような特性はコンソールアクセスが難しいクラウドやテストのときに便利。

6. 参考

16
15
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?