Azure Virtual Machine と MAC address

皆様こんにちは。 [株式会社ACCESS Advent Calendar 2020](https://qiita.com/advent-calendar/2020/access) 22日目の記事です。

昨日は @Momijinn さんが Docker で HTTP/2 対応 の web server を動かす話をしてくれました。コンテナいいですよね。

最近、Azure 上で Gurobi optimizer という数理最適化のソルバを動かすために、 MAC address に紐付けられたライセンスを扱う機会がありました。

Gurobi に限らず、ハードウェアに縛られてしまうソフトウェアがありますよね。

昨今ではコンテナ上でアプリケーションを動かすことも多いのですが（Azure Machine Learning も然り）、コンテナでは NIC を固定できないので、こういったライセンスが必要なソフトウェアを扱う場合は別途 MAC address を固定できるサーバーを用意しなければならないつらみがあります。

Gurobi の場合は、AWS EC2 においてライセンスを扱うためのノウハウが [サポートページ](https://support.gurobi.com/hc/en-us/articles/360031842772-How-do-I-configure-an-AWS-EC2-instance-so-the-license-file-remains-valid-) に記載がありますが、Azure に関しては記載がありませんでした。

この記事では最初に、Azure Virtual Machine (VM) において MAC アドレスがどのように割り当てられているか？を記載して、

あるユースケースを考えて、Azure CLI を使った操作手順をご紹介します。

## Azure VM の MAC アドレスがどのように割り当てられているか？

Azure Network Interface (NIC) によって決まっています。

したがって、NIC さえ同じものを使っていれば、VM インスタンスを入れ替えてしまっても同じライセンスを使うことができます。

このあたりは、ドキュメントをよく探してみると記載がされています。

> Azure assigns the MAC address to your NIC, if you destroy your VM you cannot designate the preferred MAC address like you would an IP address. But...there's a workaround...when you delete a virtual machine in Azure, the disks and NIC are left behind. We could have easily just re-deployed a new virtual machine, using the former disks and NICs. That way the MAC address wouldn't change and we wouldn't have to reinstall our software.

[Understanding static MAC address licensing in Azure](https://techcommunity.microsoft.com/t5/itops-talk-blog/understanding-static-mac-address-licensing-in-azure/ba-p/1386187)

> Each NIC attached to a VM is assigned a MAC address that doesn't change until the VM is deleted.

[Virtual networks - Azure Virtual Machines](https://docs.microsoft.com/en-us/azure/virtual-machines/network-overview#network-interfaces)

Azure NIC は AWS の Elastic network interfaces (ENI) に相当するサービスですね。

## あるインスタンスの NIC を別のインスタンスに付け替える操作例

既に動いている VM の NIC を別の VM に付け替えることを想定して、一連の操作例をご紹介します。

### az login

azure cli のインストールは [ドキュメント: Install the Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli) を御覧ください。

下記コマンドを打つとブラウザで、どのアカウントにログインするか選択してログインできます (今回は個人の Azure アカウントを使います)。

```
az login
```

### リソースグループの作成

はじめに、リソースグループを準備します。

Azure ではリソースグループでリソースを管理します。

このリソースグループの中で、サンプルのリソースを作っていくことにします。

```
az group create --name sample_rg --location japaneast
```

### 1つ目の VM の作成

```shell
az vm create \
  --resource-group sample_rg \
  --name sample_vm01 \
  --image UbuntuLTS \
  --size Standard_B1ls \
  --admin-username azureuser \
  --generate-ssh-keys \
  --output json \
  --verbose
```

今回は、おやすい Standard_B1ls インスタンスを選択しています。

インスタンスのサイズは下記のページで確認できます。

https://azure.microsoft.com/ja-jp/pricing/details/virtual-machines/linux/

--generate-ssh-keys で自動で ssh 鍵が作成されますが、id_rsa が既に存在している場合は、その鍵を利用します。

このように VM を作成することで、OS disk と NIC も同時に作成されます。

成功すると下記のようなログが出力されます。

```
{- Finished ..
  "fqdns": "",
  "id": "/subscriptions/your-subscription-id/resourceGroups/sample_rg/providers/Microsoft.Compute/virtualMachines/sample_vm01",
  "location": "japaneast",
  "macAddress": "xx-xx-xx-xx-xx-xx",
  "powerState": "VM running",
  "privateIpAddress": "10.0.0.4",
  "publicIpAddress": "xxx.xxx.xxx.xxx",
  "resourceGroup": "sample_rg",
  "zones": ""
}
```

ここにある `"macAddress": "xx-xx-xx-xx-xx-xx",` がライセンスに紐付けられるものとなります。

### ssh で VM にアクセスし MAC address を確認する

publicIpAddress を使って ssh できます。

```shell
ssh azureuser@xxx.xxx.xxx.xxx
```

eth0 の MAC address を確認してみます。

```
azureuser@samplevm01:~$ cat /sys/class/net/eth0/address
xx:xx:xx:xx:xx:xx
```

たしかに、VM を作成したときのログに出力されている MAC address であることを確認しました。

### NIC の確認

VM にアタッチされている NIC は下記のコマンドでを確認することができます。

```shell
$ az vm nic list -g sample_rg --vm-name sample_vm01
[
  {
    "id": "/subscriptions/your-subscription-id/resourceGroups/sample_rg/providers/Microsoft.Network/networkInterfaces/sample_vm01VMNic",
    "primary": null,
    "resourceGroup": "sample_rg"
  }
]
```

NIC 関連のコマンドは、 `az vm nic` と `az network nic` があります。

それぞれ `az vm nic --help` と `az network nic --help` で使い方を確認できます。

この NIC を別の VM に付け替えることが今回の目的ですが、VM に少なくとも 1 つは NIC が必要なので外せません。

そのまま NIC をデタッチしようとすると下記のようにエラーがでます。

```
az vm nic remove \
>   --nics sample_vm01VMNic \
>   --resource-group sample_rg \
>   --vm-name sample_vm01
BadRequestError: Virtual machine sample_vm01 must have at least one network interface.
```

NIC をデタッチするには、sample_vm01 に 2つの NIC をつけて片方をデタッチするなどの方法が考えられます。

ここでは sample_vm01 の image を backup して、その image から新規 VM を作成し、元々の NIC をアタッチすることにします。

### VM image の作成

image から作成する VM が sample_vm01 と同じ状態であることを確認するために、sample_vm01 のホームディレクトリに `zzz` という空ファイルを作成しておきます。

```
ssh azureuser@xxx.xxx.xxx.xxx
touch zzz
```

では、ローカルに戻って、下記のような手順で sample_vm01 のイメージを作成していきます。

```
az sig create --resource-group sample_rg --gallery-name sample_gallery

az sig image-definition create \
   --resource-group sample_rg \
   --gallery-name sample_gallery \
   --gallery-image-definition sample_definition \
   --publisher sample_publisher \
   --offer sample_offer \
   --sku sample_sku \
   --os-type Linux \
   --os-state specialized

az sig image-version create \
   --resource-group sample_rg \
   --gallery-name sample_gallery \
   --gallery-image-definition sample_definition \
   --gallery-image-version 1.0.0 \
   --target-regions "japaneast" "japanwest" \
   --replica-count 2 \
   --managed-image "/subscriptions/your-subscription-id/resourceGroups/SAMPLE_RG/providers/Microsoft.Compute/virtualMachines/sample_vm01"
```

ここの step が完了するまで結構待ちました。

```
az sig image-definition list \
  --resource-group sample_rg \
  --gallery-name sample_gallery \
  --query "[].[name, id]" \
  --output tsv
```

image definition が作成されていることを確認します。

```shell
$ az sig image-definition list \
>   --resource-group sample_rg \
>   --gallery-name sample_gallery \
>   --query "[].[name, id]" \
>   --output tsv
sample_definition	/subscriptions/your-subscription-id/resourceGroups/sample_rg/providers/Microsoft.Compute/galleries/sample_gallery/images/sample_definition
```

これで sample_vm01 の image を作成できました。

### 新しい VM を image から作成する

image から新しい sample_vm02 を作成します。 (ここで `--nics` オプションを使うことで既存の NIC を選択して VM を作成できますが、まだ sample_vm01 にくっついているので NIC は新規作成することにします。)

```
az vm create \
   --resource-group sample_rg \
   --name sample_vm02 \
   --size Standard_B1ls \
   --image "/subscriptions/your-subscription-id/resourceGroups/sample_rg/providers/Microsoft.Compute/galleries/sample_gallery/images/sample_definition" \
   --specialized
```

下記のようなログが出ます。

```
{- Finished ..
  "fqdns": "",
  "id": "/subscriptions/your-subscription-id/resourceGroups/sample_rg/providers/Microsoft.Compute/virtualMachines/sample_vm02",
  "location": "japaneast",
  "macAddress": "yy-yy-yy-yy-yy-yy",
  "powerState": "VM running",
  "privateIpAddress": "10.0.0.5",
  "publicIpAddress": "yyy.yyy.yyy.yyy",
  "resourceGroup": "sample_rg",
  "zones": ""
}
```

ssh してみましょう。

```
ssh azureuser@yyy.yyy.yyy.yyy
```

home ディレクトリで ls すると、sample_vm01 で作成した `zzz` というファイルが存在していることを確認できます。

```
azureuser@samplevm01:~$ ls
zzz
```

### 古い VM の削除

sample_vm01 は不要なので削除します (インスタンスを削除しても OS disk と NIC は残るので後から復元も可能です)。

```shell
$ az vm delete \
  --resource-group sample_rg \
  --name sample_vm01

Are you sure you want to perform this operation? (y/n): y
```

削除後に、NIC の一覧を確認してみます。

```
$ az network nic list --query "[].[name, id]"
[
  [
    "sample_vm01VMNic",
    "/subscriptions/your-subscription-id/resourceGroups/sample_rg/providers/Microsoft.Network/networkInterfaces/sample_vm01VMNic"
  ],
  [
    "sample_vm02VMNic",
    "/subscriptions/your-subscription-id/resourceGroups/sample_rg/providers/Microsoft.Network/networkInterfaces/sample_vm02VMNic"
  ]
]
```

sample_vm01 は削除されましたが、sample_vm01VMNic は残っていることを確認できました。

### 古い VM に付いていた NIC を新しい VM にセットする

sample_vm01VMNic を sample_vm02 にアタッチします。

```
az vm nic set \
  --resource-group sample_rg \
  --nics sample_vm01VMNic \
  --vm-name sample_vm02
```

下記のように確かに、sample_vm01VMNic が sample_vm02 にアタッチされていることを確認しました。

```
$ az vm nic list \
>   --resource-group sample_rg \
>   --vm-name sample_vm02
[
  {
    "id": "/subscriptions/your-subscription-id/resourceGroups/sample_rg/providers/Microsoft.Network/networkInterfaces/sample_vm01VMNic",
    "primary": true,
    "resourceGroup": "sample_rg"
  }
]
```

ssh して MAC address を確認すると、たしかに sample_vm01 で確認したときと同じになっていることを確認できます。

```
ssh azureuser@yyy.yyy.yyy.yyy
```

```
azureuser@samplevm01:~$ cat /sys/class/net/eth0/address
xx:xx:xx:xx:xx:xx
```

### VM を resize する

蛇足っぽくなりますが、最後に VM instance のサイズを変更する方法をご紹介します。

以下のコマンドでインスタンスサイズを無停止で変更することができます。

```
az vm resize \
  --resource-group sample_rg \
  --name sample_vm02 \
  --size Standard_DS3_v2
```

ただし、Premium storage supported のインスタンスサイズからサポートされていないインスタンスへの変更は下記のエラーのようにできないので、そういった変更をしたいかつ NIC を変えたくない場合は、上記で示したような手順で image から新規 VM を作成する際に VM size を指定しましょう。

```
$ az vm resize \
>   --resource-group sample_rg \
>   --name sample_vm02 \
>   --size Standard_D2_v2
BadRequestError: Requested operation cannot be performed because the VM size Standard_D2_v2 does not support the storage account type Premium_LRS of disk ''. Consider updating the VM to a size that supports Premium storage.
```

### NIC を指定した Auto Scaling

今回は試しませんでしたが、Azure VM Scale Set (VMSS) で実現できると思われます。

[Overview of autoscale with Azure virtual machine scale sets - Azure Virtual Machine Scale Sets](https://docs.microsoft.com/en-us/azure/virtual-machine-scale-sets/virtual-machine-scale-sets-autoscale-overview)

vmss での nic の設定は下記が参考になりそうです。

[Networking for Azure virtual machine scale sets - Azure Virtual Machine Scale Sets](https://docs.microsoft.com/en-us/azure/virtual-machine-scale-sets/virtual-machine-scale-sets-networking#multiple-nics-per-virtual-machine)

### リソースグループの削除

作ったリソースは放っておくと課金されるので、最後に、忘れずにリソースグループを削除しましょう。
（また削除すると復元もできません）

```
az group delete --resource-group sample_rg
```

## まとめ

以上、Azure VM と NIC/MAC address 周りの workaround を紹介しました。

Azure Portal での操作はわかりやすくて便利ですが、細かい設定はできないこともあるみたいなので、そういう場合は Azure CLI を使いましょう。

(例えば、既存の OS disk や NIC を指定して新規 VM を作成することは現時点では Azure Potal の GUI ではできなかったと思います。)

Azure CLI の使い方はコマンドのヘルプ もしくは [ドキュメント](https://docs.microsoft.com/en-us/cli/azure/) に整備されているので、これらを見ればかゆいところに手が届くはず！

明日は、@Hiroshi_Tsukamoto さんが ドローン関係の記事を書いてくださるそうです。おたのしみに！