Azure VM の Ubuntu を 18.04(LTS) から 20.04(LTS) へアップグレードしたら Grub が壊れた→復旧

## はじめに

Azure VM の Ubuntu 18.04(LTS) を 20.04(LTS) へアップグレードして再起動したところ、いつまでたってもSSHでリモート接続できなくなりました。

Azure Portal の シリアルコンソールも応答なし。
Azure Portal の Azure VM のブート診断のスクリーンショットに下記のメッセージが・・・。

```txt
'grub_file_filters' not found
```

Grub が壊れてしまったようです。

## 環境

* Azure VM の Ubuntu 18.04(LTS) 
* Windows 10 1909
* WSL Ubuntu 20.04.1 LTS (GNU/Linux 4.4.0-18362-Microsoft x86_64)
* PowerShell 7.0.3 + Microsoft Azure CLI 2.14.0

## アップグレードの手順

Azure VM の Ubuntu 18.04(LTS) へ、WSL の Ubuntu から SSH でリモート接続して下記手順でアップグレードを行いました。

1. 事前にアップデートの確認と適用

    ```bash
    $ sudo apt update
    $ sudo apt upgrade
    ```

2. アップグレードの実行

    ```bash
    $ sudo do-release-upgrade
    ```

3. 選択肢は基本 y(YES)、アップグレードの提案はすべて受け入れ、LXD は 4.0

4. アップグレードを完了させるための再起動

5. 再起動できず・・・（'grub_file_filters' not found）

## まずは情報収集

Grub を再インストールして修復したいのですが、Azure VM の環境でどうしたものかと悩んでいたところ、マイクロソフト社の下記情報を見つけました。

[Linux VM boots to Grub Rescue](https://docs.microsoft.com/ja-jp/azure/virtual-machines/troubleshooting/troubleshoot-vm-boot-error)

「Recommended steps」には該当する情報がなかったので、リンクを辿ってみました。

[Azure Linux 仮想マシンをカーネル関連のブート問題から回復する方法](https://docs.microsoft.com/ja-jp/troubleshoot/azure/virtual-machines/kernel-related-boot%20issues)

「構成ファイルを更新する方法」の「方法 2: オフライン修復」あたりかなと思い、リンク先を見てみました。

[Troubleshoot a Linux VM by attaching the OS disk to a recovery VM with the Azure CLI](https://docs.microsoft.com/ja-jp/azure/virtual-machines/troubleshooting/troubleshoot-recovery-disks-linux)

「Recovery process overview」に書かれている下記手順のように、問題の発生している Azure VM のOSディスクのスナップショットからディスクを作成し、修復用の Azure VM にマウントして、問題を解消したうえで元の Azure VM に戻せば行けそうです。

1. Stop the affected VM.

2. Take a snapshot from the OS disk of the VM.

3. Create a disk from the OS disk snapshot.

4. Attach and mount the new OS disk to another Linux VM for troubleshooting purposes.

5. Connect to the troubleshooting VM. Edit files or run any tools to fix issues on the new OS disk.

6. Unmount and detach the new OS disk from the troubleshooting VM.
Change the OS disk for the affected VM.

## 手順1 : 仮想マシンを停止する

* Azure Portal から、問題の発生している Azure VM を停止しました。

* [Azure CLI の最新版](https://docs.microsoft.com/ja-jp/cli/azure/install-azure-cli)をインストールし、Azure CLI を利用して停止しても OK ですね。

    ```powershell
    > az login
    > az vm stop --resource-group <リソースグループ名> --name <仮想マシン名>
    ```

## 手順2 : 影響を受けている VM の OS ディスクのスナップショットを作成する

* 問題の発生している Azure VM の OSディスクのスナップショットを作成します。

* Azure CLI を利用しています。（ディスクIDを元にスナップショットを作成）

    ```powershell
    > $osdiskid=(az vm show -g <リソースグループ名> -n <仮想マシン名> --query "storageProfile.osDisk.managedDisk.id" -o tsv)
    > az snapshot create --resource-group <リソースグループ名> --source "$osdiskid" --name <スナップショット名>
    ```

## 手順3 : スナップショットからディスクを作成する

* 作成したスナップショットをもとに、修復対象とするマネージドディスクを作成します。

* Azure CLI を利用しています。（スナップショットをもとにマネージドディスクを作成）

    ```powershell
    > $resourceGroup="<リソースグループ名>"
    > $snapshot="<スナップショット名>"
    > $osDisk="<新しいディスク名＝修復用>"
    > $diskSize=30 # 必要なサイズ（GB）を指定
    > $storageType="Standard_LRS" #必要なストレージタイプを指定
    > $osType="Linux" # 必要なOSタイプを指定
    > $snapshotId=(az snapshot show --name $snapshot --resource-group $resourceGroup --query id -o tsv)
    > az disk create --resource-group $resourceGroup --name $osDisk --sku $storageType --size-gb $diskSize --source $snapshotId
    ```

## 手順4-A : ディスクをマウントして修復するための Azure VM を作成する

* Azure Portal から、修復に利用するための Azure VM を新たに作成しました。

* 参考

    * [Ansibleを利用してAzure仮想マシンを作成するためのAzure資格情報の作成と準備](https://qiita.com/kerobot/items/42d234df680772bedf0d)

    * [Ansibleを利用してAzure仮想マシンを作成する](https://qiita.com/kerobot/items/c2718a53ef61b70f6046)

* 同じリソースグループ内の同じ仮想ネットワーク内に、同じサイズの仮想マシン、パブリックIPアドレス、ネットワークセキュリティグループ、ネットワークインターフェースを作成しています。

* 作成した仮想マシンにDNS名ラベルを設定し、SSHでリモート接続できるところまで確認しました。

## 手順4-B : 新しい仮想ハード ディスクを別の VM に接続する

* Azure Portal から、スナップショットから作成したディスクを、修復用の Azure VM に接続しました。

* Azure CLI を利用してディスクを接続しても OK ですね。

    ```powershell
    > $newdiskid=(az disk show -g $resourceGroup -n $osDisk --query id -o tsv)
    > az vm disk attach --disk $newdiskid --resource-group $resourceGroup --size-gb $diskSize --sku $storageType --vm-name <修復用の仮想マシン名>
    ```

## 手順5-A : 接続されたデータ ディスクをマウントする

* 修復に利用するための Azure VM にSSHでリモート接続し、接続したディスクをマウントしました。

* 修復用の Azure VM のディスク利用状況を確認します。

    ```bash
    $ df -h

    Filesystem      Size  Used Avail Use% Mounted on
    udev            2.0G     0  2.0G   0% /dev
    tmpfs           394M  676K  393M   1% /run
    /dev/sdc1        29G  1.4G   28G   5% /
    tmpfs           2.0G     0  2.0G   0% /dev/shm
    tmpfs           5.0M     0  5.0M   0% /run/lock
    tmpfs           2.0G     0  2.0G   0% /sys/fs/cgroup
    /dev/sdc15      105M  3.6M  101M   4% /boot/efi
    /dev/sdb1       7.9G   36M  7.4G   1% /mnt
    tmpfs           394M     0  394M   0% /run/user/1000
    ```

* マウント対象のディスクを確認するため、ブロックデバイスを一覧表示します。

    ```bash
    $ lsblk -o NAME,HCTL,SIZE,MOUNTPOINT | grep -i "sd"

    sda     3:0:0:0      30G
    ├─sda1             29.9G
    ├─sda14               4M
    └─sda15             106M
    sdb     1:0:1:0       8G
    └─sdb1                8G /mnt
    sdc     0:0:0:0      30G
    ├─sdc1             29.9G /
    ├─sdc14               4M
    └─sdc15             106M /boot/efi
    ```

* sdb と sdc はマウント済なので、sda が修復対象のディスクのようです。

* マウント先のディレクトリを作成し、sda の sda1 をマウントします。

    ```bash
    $ sudo mkdir /datadrive
    $ sudo mount /dev/sda1 /datadrive
    ```

* 修復対象のディスクをマウントできたことを確認します。

    ```bash
    $ ls /datadrive/
    bin   dev  home        initrd.img.old  lib64       media  opt   root  sbin  srv  tmp  var      vmlinuz.old
    boot  etc  initrd.img  lib             lost+found  mnt    proc  run   snap  sys  usr  vmlinuz
    ```

## 手順5-B : 新しい OS ディスクの問題を修正する

* マウントした修復対象のディスクを弄るために、sys, proc, dev をバインドマウントして chroot 環境へ切り替えます。

    ```bash
    $ sudo mount --bind /sys /datadrive/sys
    $ sudo mount --bind /proc /datadrive/proc
    $ sudo mount --bind /dev /datadrive/dev
    $ sudo chroot /datadrive
    ```

* 修復対象のディスクに対して Grub をインストールします。

    ```bash
    $ sudo grub-install /dev/sda

    Installing for i386-pc platform.
    Installation finished. No error reported.
    ```

## 手順6-A : 新しい OS ディスクのマウントを解除して切断する

* 修復したディスクのマウントを解除します。

    ```bash
    $ cd /
    $ sudo umount /dev/sda1
    ```

* Azure Portal から、修復用の Azure VM に接続している修復したディスクを切断しました。

* Azure CLI を利用してディスクを切断しても OK ですね。

    ```powershell
    > az vm disk detach -g <リソースグループ名> --vm-name <修復用の仮想マシン名> --name <新しいディスク名＝修復用>
    ```

* 不要であれば、修復用の Azure VM を停止しておきます。

## 手順6-B : 影響を受けている VM の OS ディスクを変更する

* Azure Portal から、問題の発生している Azure VM を停止したうえで、OSディスクを修復したディスクと交換しました。

* Azure Portal の、仮想マシンのディスクの「OSディスクのスワップ」を利用しています。

* Azure CLI を利用してディスクを変更しても OK ですね。

    ```powershell
    > az vm stop -n <仮想マシン名> -g <リソースグループ名>
    > $newdiskid=(az vm show -g <リソースグループ名> -n <新しいディスク名＝修復用> --query id -o tsv)
    > az vm update -g <リソースグループ名> -n <仮想マシン名> --os-disk $newdiskid
    > az vm start -n <仮想マシン名> -g <リソースグループ名>
    ```

## さいごに動作確認

修復したディスクに差し替えた Azure VM にSSHでリモート接続できることを確認しました。20.04(LTS) へのアップグレードも完了しています。

```bash
Welcome to Ubuntu 20.04.1 LTS (GNU/Linux 5.4.0-1031-azure x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Sat Nov  7 13:14:17 JST 2020

  System load:  0.04               Processes:             139
  Usage of /:   26.2% of 28.90GB   Users logged in:       1
  Memory usage: 61%                IPv4 address for eth0: 10.0.0.5
  Swap usage:   0%

 * Introducing self-healing high availability clustering for MicroK8s!
   Super simple, hardened and opinionated Kubernetes for production.

     https://microk8s.io/high-availability

0 updates can be installed immediately.
0 of these updates are security updates.


Last login: Fri Nov  6 13:55:00 2020 from 133.200.8.0
```

スナップショットや差し替え前のディスクなど不要なものがあれば削除しておきましょう。

修復用の Azure VM を残しておく場合は、停止済み（割り当て解除）にしておくと安心ですね。

## おわりに

結果として修復できてよかったのですが、アップグレード前にバックアップしておくべきでした。