Azure Linux VMのシステム起動不能障害にシリアルコンソールで対処する

**2022/06/10 追記:** この記事で紹介した Ubuntu 20.04 LTS カーネルのコンテナ起動パニック問題は、すでに修正版のパッケージが作成され、各クラウドベンダでも利用可能になりました。 APT で最新のパッケージに更新していればシステム起動不能障害が発生することはもうありません。

https://bugs.launchpad.net/ubuntu/+source/linux-aws-5.13/+bug/1977919/comments/66

## 新しい Linux カーネルの不具合で Azure VM が起動不能

2022年6月9日の夜、Azure VM で新しい Ubuntu 20.04 LTS をセットアップしたところ、Docker でコンテナを起動すると `kernel BUG at include/linux/fs.h:3103!` で panic を起こしてシステムがリブートするようになってしまいました。

https://twitter.com/hogegashi/status/1534914076672806913

直前でリリースされていた Ubuntu 20.04 LTS の新しいカーネル 5.13.0-1028 でまずいバグが混入したようです。この問題は Azure だけでなく Amazon、Google、Oracle など様々なクラウドベンダでも同時に発生しているようで、最近では珍しく影響範囲の大きな不具合になりそうです。

https://bugs.launchpad.net/ubuntu/+source/linux-aws-5.13/+bug/1977919

原因がカーネルにあることは検索してすぐ判明したのですが、自分の Azure VM はいろいろいじっているうちにシステム起動直後にコンテナを自動起動してリブートを繰り返すというまずい状況に陥ってしまいました。

https://twitter.com/hogegashi/status/1534917734722441216

こういうときはいわゆるシングルユーザーモード (Ubuntu では recovery mode) でシステムを起動してから Docker の自動起動を止めますが、そのためには Linux カーネル起動前のブートローダを操作する必要があります。

幸い Azure VM ではほとんどの Linux ディストリビューションにおいてブートローダの段階からシリアルコンソールが有効になっており、 Azure Portal から web ブラウザでアクセスできます。これを使えば Ubuntu のブートローダである GRUB を操作して問題に対処できます。

## Azure VM のシリアルコンソール

https://docs.microsoft.com/ja-jp/troubleshoot/azure/virtual-machines/serial-console-linux

Azure VM でシリアルコンソールを使うには、まず VM のブート診断情報の保存設定が必要です。これは VM を止める必要はなく、稼働中でも設定できます。

シリアルコンソールを使う場合、マネージドなストレージアカウントではなく、自分で作ったカスタムのストレージアカウントを割り当てる必要があります。既存のストレージアカウントがなければ、その場で新しく作ることができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61759/9adccb24-36a1-d705-274d-f60d7cee3990.png)
設定が完了すれば、次のようにAzure Portal 内で VM のシリアルコンソールを開くことができます。
![hoge.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61759/305bc0e6-f0a7-bd92-5817-cf9b436253a7.gif)
VM が起動・リブートしたところを見計らってキーボードの ESC キーやカーソル↓キー を連打していると、GRUB ブートローダのメニューで止めることができます。そこから「Advanced options for Ubuntu」を選びます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61759/16e43553-c26e-451f-f3ca-4545db297c48.png)
次に不具合がなかった古いカーネル Linux 5.13.0-1025-azure を選べば正常起動できるのですが、せっかくなので Ubuntu の recovery mode を選んで Docker の自動起動を止めてみることにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61759/d1228c02-5fd0-934c-3d92-1c79beb6585e.png)
適当なカーネルの (recovery mode) を選んで起動すると Recovery Menu というものが現れて入力待ちになります。ここでバックグラウンドのログ出力で画面が乱れたりしますが <kbd>Ctrl</kbd>+<kbd>L</kbd> を押すと再描画することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61759/5fef6e34-4116-b176-db35-6725a28f514c.png)
Recovery Menu から「root」を選ぶと、 root 権限の Bash シェルが起動します。そこで `systemctl disable docker` を実行することで Docker の自動起動を無効化できます。後で元に戻すには `systemctl enable docker` を実行してください。

exit でシェルを抜けると Recovery Menu に戻りますので、次は「resume」を選ぶとシステムが通常起動します。今度は Docker は自動起動しないので、リブートしてしまうことはありません。

シリアルコンソールにログインプロンプトが現れたら SSH サーバも起動していますので、改めて一般ユーザーで SSH ログインして必要な対処を行います。

## 対処方法

リブートを回避してシステムの起動に成功したら問題に対処します。 Ubuntu 20.04 LTS の最新カーネルのバグなので、修正されたカーネルがリリースされるまでは、不具合がなかったひとつ前のカーネル Linux 5.13.0-1025-azure を使うのがよいでしょう。

ということで `/etc/default/grub` の `GRUB_DEFAULT=0` を編集して自動起動したい GRUB のメニューエントリを指定します。

```:/etc/default/grub (抜粋)
GRUB_DEFAULT="Ubuntu, with Linux 5.13.0-1025-azure"
```

これでもよいのですが、メニューエントリの文字列を正確に指定するのが面倒です。せっかくシリアルコンソールを有効化して GRUB にアクセスできるようになったので、 GRUB で選択したメニューを保存するようにしてみます。 `GRUB_DEFAULT=saved` とした上で `GRUB_SAVEDEFAULT=true` を追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61759/f8cc5c44-cbce-e706-b4ac-fe25c98a21b2.png)
このように変更した上で `update-grub` コマンドを実行してからリブートします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61759/8addbc67-10f6-f334-f956-70de2b97798b.png)
続いてシリアルコンソールの GRUB メニューから Linux 5.13.0-1025-azure を選択して起動すると、それが次回以降の起動でも自動的に選択されるようになります。

不具合が修正されたカーネルがリリースされたら、それを APT でインストールした上で、今度は GRUB の先頭のメニュー「Ubuntu」を選択して起動します。これにより元の `GRUB_DEFAULT=0` の挙動と同じく、常にインストールされた中で最新のカーネルから起動するようになります。

## おわりに

突然発生した重篤なシステム起動不能のトラブルに驚きましたが、このような障害の解決に必要なシリアルコンソールとブートローダの操作を Azure VM ではまともにやったことがなかったので、顛末を記事にしてみました。基本的なことではありますが参考になれば幸いです。

Azure VMのシリアルコンソールがそこそこまともに使えることがわかったので、Azure VMでLinux以外のOSを動かす開発にも活用できそうだとも思いました。