Azure Linux VM のディスク識別

## はじめに
こんにちは。
この記事は、[Microsoft Azure Tech Advent Calendar 2022](https://qiita.com/advent-calendar/2022/microsoft-azure-tech) 22日目の記事です。

Azure Linux VM にデータディスクを複数接続したとき、Linux に認識されているディスク (/dev/sdX) が Azure のどのディスクなのか識別したいときがあります。この記事では、[Linux VM デバイス名の変更トラブルシューティング](https://learn.microsoft.com/ja-jp/troubleshoot/azure/virtual-machines/troubleshoot-device-names-problems) に書いてあることをもう少し具体的に見ていきます。


## 準備
おもむろに 4GB のデータディスクを３つ接続した Gen1 Ubuntu VM を作成します。
```bash
az group create -g diskgroup -l japaneast
```
```bash
for i in `seq 1 3`; do az disk create -g diskgroup -n data$i -l japaneast --size-gb 4 --sku Standard_LRS; done
```
```bash
az vm create -g diskgroup -n diskvm --image UbuntuLTS --attach-data-disks data1 data2 data3
```

## `lsblk` コマンド
SSH 接続後、`lsblk` でブロックデバイスを表示してみます。

```bash
root@diskvm:~# lsblk -i
NAME    MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
sda       8:0    0   30G  0 disk
|-sda1    8:1    0 29.9G  0 part /
|-sda14   8:14   0    4M  0 part
`-sda15   8:15   0  106M  0 part /boot/efi
sdb       8:16   0    7G  0 disk
`-sdb1    8:17   0    7G  0 part /mnt
sdc       8:32   0    4G  0 disk
sdd       8:48   0    4G  0 disk
sde       8:64   0    4G  0 disk
sr0      11:0    1  628K  0 rom
```

`sda1` が `/` にマウントされているので、`sda` が OS ディスク、`sdb1` が `/mnt` にマウントされているので、`sdb1` が一時ディスク、`sdc`, `sdd`, `sde` がデータディスクっ __ぽい__ という事はわかります。また、Azure 上に限らず Linux においてはこのデバイス名の一貫性が保証されていないため、デバイス名だけを見て判断するのは軽率です。

## `lsscsi` コマンド
ディスクを識別する一つの方法は、`lsscsi` コマンドで SCSI アドレス ([Host:Bus:Target:Lun]) をみることです。
[VM の世代](https://learn.microsoft.com/ja-jp/azure/virtual-machines/generation-2#features-and-capabilities)により SCSI アドレスが若干異なるため、後述の `/dev/disk/azure` をみる方が確実ですが、少なくともデータディスクの LUN を見分けることが可能です。

```bash
root@diskvm:~# lsscsi
[0:0:0:0]    disk    Msft     Virtual Disk     1.0   /dev/sda
[1:0:1:0]    disk    Msft     Virtual Disk     1.0   /dev/sdb
[3:0:0:0]    disk    Msft     Virtual Disk     1.0   /dev/sdc
[3:0:0:1]    disk    Msft     Virtual Disk     1.0   /dev/sde
[3:0:0:2]    disk    Msft     Virtual Disk     1.0   /dev/sdd
[5:0:0:0]    cd/dvd  Msft     Virtual CD/ROM   1.0   /dev/sr0
```

今回の Gen1 の場合は SCSI アダプター番号 (Host) が `0` のものが OS ディスク、`1` のものが一時ディスク、`3` のものがデータディスクとなり、さらにその Lun が、Azure VM の LUN に一致しています。
つまり、このコマンド結果からは、`/dev/sdc` が LUN 0, `/dev/sde` が LUN 1, `/dev/sdd` が LUN 2 という事が確認できます。

`/sys/block/*/device` のシンボリックリンクでも SCSI アドレスが確認できます。

```bash
root@diskvm:~# ls -l /sys/block/*/device
lrwxrwxrwx 1 root root 0 Dec 21 14:44 /sys/block/sda/device -> ../../../0:0:0:0
lrwxrwxrwx 1 root root 0 Dec 21 14:44 /sys/block/sdb/device -> ../../../1:0:1:0
lrwxrwxrwx 1 root root 0 Dec 21 14:44 /sys/block/sdc/device -> ../../../3:0:0:0
lrwxrwxrwx 1 root root 0 Dec 21 14:44 /sys/block/sdd/device -> ../../../3:0:0:2
lrwxrwxrwx 1 root root 0 Dec 21 14:44 /sys/block/sde/device -> ../../../3:0:0:1
lrwxrwxrwx 1 root root 0 Dec 21 14:48 /sys/block/sr0/device -> ../../../5:0:0:0
```

## `/dev/disk/azure`
`/dev/disk/azure` ができている場合、このディレクトリ配下のシンボリックリンクにより識別が可能です。
`ls -l` や `tree` を使って確認してみます。
```bash
root@diskvm:~# ls -l /dev/disk/azure/*
lrwxrwxrwx 1 root root   9 Dec 21 14:44 /dev/disk/azure/resource -> ../../sdb
lrwxrwxrwx 1 root root  10 Dec 21 14:44 /dev/disk/azure/resource-part1 -> ../../sdb1
lrwxrwxrwx 1 root root   9 Dec 21 14:44 /dev/disk/azure/root -> ../../sda
lrwxrwxrwx 1 root root  10 Dec 21 14:44 /dev/disk/azure/root-part1 -> ../../sda1
lrwxrwxrwx 1 root root  11 Dec 21 14:44 /dev/disk/azure/root-part14 -> ../../sda14
lrwxrwxrwx 1 root root  11 Dec 21 14:44 /dev/disk/azure/root-part15 -> ../../sda15

/dev/disk/azure/scsi1:
total 0
lrwxrwxrwx 1 root root 12 Dec 21 14:44 lun0 -> ../../../sdc
lrwxrwxrwx 1 root root 12 Dec 21 14:44 lun1 -> ../../../sde
lrwxrwxrwx 1 root root 12 Dec 21 14:44 lun2 -> ../../../sdd
```
```bash
root@diskvm:~# apt install tree
root@diskvm:~# tree /dev/disk/azure/
/dev/disk/azure/
├── resource -> ../../sdb
├── resource-part1 -> ../../sdb1
├── root -> ../../sda
├── root-part1 -> ../../sda1
├── root-part14 -> ../../sda14
├── root-part15 -> ../../sda15
└── scsi1
    ├── lun0 -> ../../../sdc
    ├── lun1 -> ../../../sde
    └── lun2 -> ../../../sdd

1 directory, 9 files
```
`resource` が一時ディスク、`root` が OS ディスク、`scsi1/lunX` がデータディスクの LUN X に対応することを意味しています。
それらに続く `-part` はパーティション番号です。
このディレクトリやシンボリック リンクは、`udev` によって生成されています。udev ルールは VM エージェント (WALinuxAgent) パッケージに含まれているため、Azure MarketPlace のイメージから導入した Linux VM では殆どの場合に利用できるはずです。
なお、WALinuxAgent は GitHub 上で開発されいるため、最新のルールは下記リンク先から取得が可能です。

https://github.com/Azure/WALinuxAgent/blob/master/config/66-azure-storage.rules


## Azure IMDS から Azure Disk の情報を得る
実は前述の udev ルールを改造し、[Azure IMDS](https://learn.microsoft.com/ja-jp/azure/virtual-machines/linux/instance-metadata-service?tabs=linux) から得た情報を基にシンボリックリンクを作成しようと思っていたのですが、udevd が実行されるタイミングではネットワークが使用できないため断念しました。
今回は、LUN 番号からディスクのリソース情報を得るための方法だけ紹介します。

IMDS は JSON 形式のデータを返すため、jq をインストールします。
```bash
root@diskvm:~# apt install jq
```

`.compute.storageProfile.dataDisks[]` にデータディスク情報が含まれていますので、LUN で `select` することで、LUN に接続されているデータディスクのリソース情報を得ることができます。以下は LUN `"0"` のクエリ例です。
```bash
root@diskvm:~# /usr/bin/curl -s -H Metadata:true --noproxy "*" "http://169.254.169.254/metadata/instance?api-version=2021-02-01" | /usr/bin/jq -r '.compute.storageProfile.dataDisks[] | select(.lun=="0")'
{
  "caching": "None",
  "createOption": "Attach",
  "diskSizeGB": "4",
  "image": {
    "uri": ""
  },
  "lun": "0",
  "managedDisk": {
    "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/diskgroup/providers/Microsoft.Compute/disks/data1",
    "storageAccountType": "Standard_LRS"
  },
  "name": "data1",
  "vhd": {
    "uri": ""
  },
  "writeAcceleratorEnabled": "false"
}
```
これで、リソース名 `data1` のデータディスクが、VM の LUN 0 に接続されており、/dev/sdc として認識されている事が確認できました。

## まとめ

- デバイス名 (/dev/sdX) は変わることがあるので注意が必要
- `/dev/disk/azure` のシンボリックリンクを見ると、LUN との対応がわかる
- Azure IMDS を使うと、ディスク リソース情報が VM 内から見れる