| ../ |
VPSサーバー(CentOS8.1)の構築中に yum update
を実行すると再起動できなくなるという洗礼を受けた。yum update中に /boot/grub2/grub.cfg の変更が行われ、その影響のようだ。回避するには、サーバー構築の最初の段階で /boot/grub2/grub.cfg を退避しておき、yum update 後に元に戻してやるといい。
##どういった症状なのか
GMO VPSで利用可能となったサーバー(CentOS8.1)を VPSポータルから通常モードで起動して「ON」状態になったら、TeraTermで接続してrootでログインする。VPSポータルのコンソールからログインしても同様である。そして、yum updateを実行し、サーバーを再起動する。
login: root
Password: ***
$ yum update -y
$ reboot
すると、TeraTermからは接続できなくなり、VPSポータルのコンソールからだとGRUB選択画面が開いて、進めなくなる。
##なぜ起きるのか
GRUBは、ブートローダーであり、どこからOSイメージを読み込んで起動するかを選択するためのもの。古いGRUB(GRUB Legacy
)と新しいGRUB2
がある。GRUB2はCentOS7以降からだ。GRUB LegacyとGRUB2のどちらを使うか、どのOSイメージを使うかを選択して、そのOSイメージをロードして、OSが起動される。通常、どのOSイメージを使うかは初期設定されたものや初回にユーザーが選択したものを覚えていて、自動で一連の動作を行ってくれる。それが、yum updateの実行によって、OSイメージの選択が自動で行えなくなってしまうようだ。そのため、TeraTermからは接続時にタイムアウトし、VPSポータルのコンソールからはGRUB LegacyとGRUB2の選択画面で止まってしまう。
VPSポータルのコンソールでは、GRUB選択画面からGRUBのコマンドで対処できるのかもしれない。WindowsPCからはキーバインドも異なり、コピペもできず、GRUBコマンドの知見もないので、私は早々にあきらめた。
CentOS8.1では、GRUB2が使われる。起動時の制御は、/etc/default/grub
ファイルで指定できる。GRUB_TIMEOUT
でタイムアウト秒数、GRUB_DEFAULT
でOSイメージを指定する。タイムアウト秒数を経過したら指定のOSイメージが自動で起動される。正確には、事前にgrub2-mkconfig
コマンドを実行して、grubファイルと/etc/grub.d/
配下のファイルの記述にもとづいて、/boot/grub2/grub.cfg
ファイルを生成しておくのである。起動時にはgrub.cfgファイルが実行されるのであって、起動時にgrubファイルを解釈する訳ではない。GRUB2の詳細は、https://documentation.suse.com/ja-jp/sles/12-SP4/html/SLES-all/cha-grub2.html などを参照のこと。
つまり、yum updateによって、grub.cfgファイルが書き換わっているか、grub.cfgファイルの記述どおりに実行できない状況(例えば、必要なファイルがないなど)に陥っているのだ。
また、起動時には /boot/grub2/grubenv
も参照する。これは前回の起動時の設定を保存し、次回の起動時に参照するものである。GRUB_DEFAULT=saved の指定の時に利用される。起動後に grubenv ファイルに修正を加えることで、次回の起動を制御できる。例えば、grub2-set-default
コマンドを実行することで、/etc/default/grub を介さずにダイレクトにsaved_entry
を変更することができる。
##回避する手順
初回のログイン後に、grub.cfgファイルを退避しておき、yum update後に戻してやれば、なんとかなりそうだ。以下、手順を説明する。
(1) VPSポータルからサーバーを通常モードで起動し、TeraTermからrootでログインする。そして、初期状態でバージョンを確認する。CentOS8.1である。
$ cat /etc/redhat-release
CentOS Linux release 8.1.1911 (Core)
(2) yum update後にブートできなくなるので、事前に以下のファイルを退避しておく。実際には、grub.cfgファイルだけでもいいが、他のファイルも合わせて退避しておくとよい。
$ cp /etc/default/grub /etc/default/grub.original
$ cp /boot/grub2/grub.cfg /boot/grub2/grub.cfg.original
$ cp /boot/grub2/grubenv /boot/grub2/grubenv.original
実は、grubファイルは既にgrub.rpmsave
としてバックアップされており、grub.cfgファイルもgrub.cfg.rpmsave
としてバックアップされている。それらのファイルで十分かもしれないが、上記手順では明示的に退避するようにした。ちなみに/etc/default/grubは、以下のようになっている。
$ cat /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="crashkernel=auto biosdevname=0 net.ifnames=0 rhgb quiet"
GRUB_DISABLE_RECOVERY="true"
GRUB_ENABLE_BLSCFG=true
GRUB_DISABLE_UUID=true
GRUB_DISABLE_LINUX_UUID=true
GRUB_TIMEOUTとGRUB_DEFAULTは、5秒間待った後で、前回使用したOSイメージを起動することを示している。GRUB_DEFAULTは0,1,2,...のような番号で指定したり、OSイメージの名前で指定したり、"saved"
で指定する。"saved"の場合には、/boot/grub2/grubenvファイルのsaved_entryで前回使用したOSイメージを識別する。
$ cat /boot/grub2/grubenv
saved_entry=20db05bac820110f5800ebe2e7262b58-4.18.0-193.28.1.el8_2.x86_64
kernelopts=root=/dev/vda1 ro crashkernel=auto biosdevname=0 net.ifnames=0 rhgb quiet
boot_success=1
(3) yum updateを実行する。問題のyum updateの実行である。200個くらいのモジュールが更新される。
$ yum update -y
(4) yum update後にバージョンを確認する。CentOS8.2に上がっている。
$ cat /etc/redhat-release
CentOS Linux release 8.2.2004 (Core)
(5) 今後はyum updateでカーネルが更新されないように設定しておく。
$ vi /etc/yum.conf
exclude=kernel* grub2*
※kernel*
だけでなく、kernel* grub2*
にしておいた方が無難である。
※ただし、これらを除外しておくと、今後に行うinstallやupdateによって「依存性の欠如」の問題が生じる場合がある。そのときは、excludeの行をコメントアウトして、yum updateを行い、grub2の問題に手動で対処する必要がある。
(6) 退避しておいた grub.cfg を元に戻す。/etc/default/grub は変更されないので、回復は不要。grubenvは、起動時に更新されるが、通常、内容は前回と同じである。grub2-set-default
コマンドを使うとgrubenvが書き換わるので、その場合には回復すること。
$ cp /boot/grub2/grub.cfg.original /boot/grub2/grub.cfg
$ cp /boot/grub2/grubenv.original /boot/grub2/grubenv
$ awk -F\' '$1=="menuentry " {print $2}' /boot/grub2/grub.cfg
CentOS Linux (4.18.0-147.5.1.el8_1.x86_64) 8 (Core)
(7) この時点で再起動して、TeraTermからログインできることを確認する。
(8) 再起動後にログインできたら、改めてgrubファイルを編集し、grub2-mkconfigコマンドで反映させる。grubファイルでは、GRUB_CMDLINE_LINUX
のみ変更している。そして、grubenvファイルのsaved_entryで識別されたものが"Found linux"
のメッセージ内に見当たるかを確認する。awkの結果でも確認できる。先頭(0番目)が該当することが分かる。
# grubファイルを編集する
$ vi /etc/default/grub
GRUB_TIMEOUT=3
GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)"
GRUB_DEFAULT=saved
GRUB_DISABLE_SUBMENU=true
GRUB_TERMINAL_OUTPUT="console"
GRUB_CMDLINE_LINUX="rd.lvm.lv=centos/root rd.lvm.lv=centos/swap
vconsole.font=latarcyrheb-sun16 vconsole.keymap=jp106
biosdevname=0 net.ifnames=0 rhgb quiet "
GRUB_DISABLE_RECOVERY="true"
GRUB_ENABLE_BLSCFG=true
GRUB_DISABLE_UUID=true
GRUB_DISABLE_LINUX_UUID=true
# grub.cfgファイルに反映させる
$ grub2-mkconfig -o /boot/grub2/grub.cfg
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-4.18.0-193.28.1.el8_2.x86_64
Found initrd image: /boot/initramfs-4.18.0-193.28.1.el8_2.x86_64.img
Found linux image: /boot/vmlinuz-4.18.0-147.5.1.el8_1.x86_64
Found initrd image: /boot/initramfs-4.18.0-147.5.1.el8_1.x86_64.img
Found linux image: /boot/vmlinuz-0-rescue-20db05bac820110f5800ebe2e7262b58
Found initrd image: /boot/initramfs-0-rescue-20db05bac820110f5800ebe2e7262b58.img
done
$ awk -F\' '$1=="menuentry " {print $2}' /boot/grub2/grub.cfg
CentOS Linux (4.18.0-193.28.1.el8_2.x86_64) 8 (Core)
CentOS Linux (4.18.0-147.5.1.el8_1.x86_64) 8 (Core)
CentOS Linux (0-rescue-20db05bac820110f5800ebe2e7262b58) 8 (Core)
# grubenvファイルを確認する
$ cat /boot/grub2/grubenv
saved_entry=20db05bac820110f5800ebe2e7262b58-4.18.0-193.28.1.el8_2.x86_64
kernelopts=root=/dev/vda1 ro crashkernel=auto biosdevname=0 net.ifnames=0 rhgb quiet
boot_success=1
(9) 再度、再起動して、TeraTermからログインできれば問題ない。
##補足1:grub2-mkconfigコマンド
上記手順のyum update直後にgrub2-mkconfigコマンドを実行してgrub.cfgを生成するとどうか試してみた。結果はNGだった。TeraTermからログインできなくなり、VPSポータルのコンソールからはGRUB選択画面で停止することになった。grub.cfg内のmenuentry
の記述が欠落した状態になった。原因は分からない。
上記(8)(9)の手順のように、再起動した後にgrub2-mkconfigを実行するとうまくいくようだ。
##補足2:grub2-set-defaultコマンド
/boot/grub2/grubenvを見ると、saved_entryには番号(0,1,2,...)ではなく、20db05bac820110f5800ebe2e7262b58-4.18.0-193.28.1.el8_2.x86_64の名前が入っている。後半の「4.18.0-193.28.1.el8_2.x86_64」の部分でマッチングされるようだ。
$ cat /boot/grub2/grubenv
saved_entry=20db05bac820110f5800ebe2e7262b58-4.18.0-193.28.1.el8_2.x86_64
kernelopts=root=/dev/vda1 ro crashkernel=auto biosdevname=0 net.ifnames=0 rhgb quiet
boot_success=1
ちなみに、grub2-set-defaultを実行することで、/etc/default/grubを介さずにダイレクトにsaved_entryを変更することができる。
$ grub2-set-default 0
$ cat /boot/grub2/grubenv
saved_entry=0
kernelopts=root=/dev/vda1 ro crashkernel=auto biosdevname=0 net.ifnames=0 rhgb quiet
boot_success=1
##補足3:リカバリモードからの修復
起動できなくなったら、VPSポータルからリカバリモードでサーバー起動して、TeraTermからrootでログインして、以下の手順で修復できる。パスワードは"recovery"
ではなく、初期パスワードである。VPSポータルのメッセージは間違っているようだ。本体ディスクを識別してマウントしてから操作する手順になる。
$ fdisk -l | more
/dev/vda1
$ mount /dev/vda1 /mnt
$ cp /mnt/boot/grub2/grub.cfg.original /mnt/boot/grub2/grub.cfg
試していないが、grub.cfg.rpmsaveが既存だったので、そのファイルで回復できたかもしれない。以下のようにコピーしてサーバーを再起動すれば、上記(8)(9)の手順で回復できたかもしれない。
$ cp /mnt/boot/grub2/grub.cfg.rpmsave /mnt/boot/grub2/grub.cfg
GMOサービスセンターからのアドバイス
GMOサービスセンターにアドバイスをお願いしていたのですが、その回答をいただきました。ありがとうございます。
(1) CentOS 8においては、Kernelのアップグレード前には、yum.confに下記を追加してくださいとのことだった。grub2*も除外した方がいいそうだ。
$ vi /etc/yum.conf
exclude=kernel* grub2*
(2) また、grub2をアップデートした場合には、/etc/default/grubを編集して、GRUB_ENABLE_BLSCFGの行をコメントアウトして grub2-mkconfigを実行するとよいということだった。ただ、私もまだGRUB_ENABLE_BLSCFG=falseの挙動は未確認だ。
$ yum update grub2*
$ vi /etc/default/grub
GRUB_ENABLE_BLSCFG=true
↓
# GRUB_ENABLE_BLSCFG=true # コメントアウトする。falseをセットしてもいい。
$ grub2-mkconfig -o /boot/grub2/grub.cfg
以上