ExpressRoute環境でVMSSを作成する

PaaS(WebApps)のオートスケーリングはとても楽で便利ですが、同じことをIaaS(VirtualMachine)でやろうとしたらどうなるかな？と思って構築しました。
せっかくなのでIaaSっぽく、閉域網(ExpressRoute)の環境で実現します。

Azure VirtualMachine ScaleSetsをExpressRouteのvNet内に構築します。
ポイントはパブリックロードバランサーではなく、インターナルロードバランサーを使用することです。
インターナルのVMSSはポータルからの作成はできないので、AzureCLI（CloudShell）でセットアップします。

VMSSの概要は公式ページ参照
[Azure Virtual Machine Scale Sets とは](https://docs.microsoft.com/ja-jp/azure/virtual-machine-scale-sets/virtual-machine-scale-sets-overview)

# Cloud Shell起動

Cloud Shell Bashを選択します。手順は公式ページ参照。
[Cloud Shell クイックスタート](https://docs.microsoft.com/ja-jp/azure/cloud-shell/quickstart)

# VM作成
VMSSでは元となるOSイメージが必要なので、まずはVMを作ります。

* 変数を定義します

```
export RESOURCE_GROUP=[任意のリソースグループ名]
export NSG_NAME=${RESOURCE_GROUP}-NSG
export NIC_NAME=${RESOURCE_GROUP}-NIC
export VM_NAME=${RESOURCE_GROUP}-VM
export USER_NAME=[任意のユーザ名]
export USER_PASS=[任意パスワード]
```

* リソースグループを作成します

```
az group create --name $RESOURCE_GROUP --location japaneast
```

* ネットワークセキュリティグループ(NSG)を作成します

```
az network nsg create --resource-group $RESOURCE_GROUP --name $NSG_NAME
```

* NSGにRDP許可設定を追加します

```
az network nsg rule create \
    --resource-group $RESOURCE_GROUP \
    --nsg-name $NSG_NAME \
    --name Allow-RDP-ALL \
    --protocol tcp \
    --priority 1000 \
    --destination-port-range 3389 \
    --access allow
```

* NSGにhttp許可設定を追加します

```
az network nsg rule create \
    --resource-group $RESOURCE_GROUP \
    --nsg-name $NSG_NAME \
    --name Allow-HTTP-ALL \
    --protocol tcp \
    --priority 1001 \
    --destination-port-range 80 \
    --access allow
```

* SubnetIDを取得します(異なるリソースグループのSubnetを指定する場合、Subnet名ではなく、ID指定しないといけない為)

```
export SUBNET_ID=`az network vnet subnet show --name [ExRouteのSubnet名] --resource-group [ExRouteのリソースグループ名] --vnet-name [ExRouteのvNet名] --query id -o tsv`
```

* 仮想NICを作成します

```
az network nic create \
    --resource-group $RESOURCE_GROUP \
    --name $NIC_NAME \
    --subnet $SUBNET_ID \
    --network-security-group $NSG_NAME
```

* VMを作成します

```
az vm create --resource-group $RESOURCE_GROUP \
    --name $VM_NAME \
    --image win2016datacenter \
    --location japaneast \
    --nics $NIC_NAME \
    --size Standard_DS1 \
    --admin-username $USER_NAME \
    --admin-password $USER_PASS
```

* 作成が完了すると、コマンド実行結果にPrivateIpAddressが出力される為、RDPで接続します。
ExpressRoute接続を想定しているため、パブリックIPは付与していません

* 接続できたらPowerShellを立ち上げます

* PowerShellプロンプトでIISをインストールします

```
Install-WindowsFeature -name Web-Server –IncludeManagementTools
Write-Output -InputObject “Azure!!! <br> VMSS!!!" > C:\inetpub\wwwroot\index.html
```

* 自PCのブラウザからPrivateIpAddress宛にアクセスし、Webページが表示されたらOKです

# sysprep実行
イメージ作成前にWindowsServerを一般化します。

<font color="Red">**sysprepは作成したWindowsServer上で実行します。自PCで実行しないように注意してください！**</font>

* 先程作成したVMにRDPでログインします
  
* ファイル名を指定して実行画面で「sysprep」を実行します
![image.png](https://qiita-image-store.s3.amazonaws.com/0/180335/21ffd16d-8608-e98b-5ecf-82e26ad9fc71.png)  
  
* Explorerが開くので「sysprep」を実行します
![image.png](https://qiita-image-store.s3.amazonaws.com/0/180335/d3c0c2e6-07b5-11e4-e46a-97131f0a8503.png)  

<font color="Red">**sysprepは作成したWindowsServer上で実行します。自PCで実行しないように注意してください！**</font>
  
* Generalizeにチェックを入れ、Shutdown OptionsをShutdownに変更して、OKをクリックします
![image.png](https://qiita-image-store.s3.amazonaws.com/0/180335/a1fdcafa-983a-28ab-1fd6-efa5431ba53a.png)

* しばらくするとRDPが切断されるので待ちます

# カスタムイメージ作成
一般化したVMからイメージを作成します

* Cloud Shellに戻り、VMが正常に停止されているか確認します

```
az vm get-instance-view -g $RESOURCE_GROUP -n $VM_NAME --query instanceView.statuses[1] -o table
```

* 「PowerState/stopped」が表示されていればOKです

* VMのAzureリソース割り当てを解除します

```
az vm deallocate -g $RESOURCE_GROUP -n $VM_NAME
```

* VMを一般化します  
  このコマンドでsysprepされるわけではありません。Azureから見て一般化されたとみなすフラグを立てるコマンドです。

```
az vm generalize -g $RESOURCE_GROUP -n $VM_NAME
```

* イメージを作成します

```
az image create -g $RESOURCE_GROUP -n ${VM_NAME}-IMAGE --source $VM_NAME
```

* イメージ化したので、元となったVMは削除しておきます

```
az vm delete -g $RESOURCE_GROUP -n $VM_NAME --no-wait –y
```

* OSディスクも削除します

```
export OSDISK=`az disk list -g $RESOURCE_GROUP --query [].name -o tsv`
az disk delete -g $RESOURCE_GROUP -n $OSDISK --no-wait –y
```

* 仮想NICも削除します

```
az network nic delete -g $RESOURCE_GROUP -n $NIC_NAME
```

* ポータルからリソースグループの中を見てみると、カスタムイメージとNSGのみになっているはずです  
  NSGはVMSS作成時に使いまわすので残しています。  
  不要なリソースをキチンと削除することは重要です。

# VMSS作成

* 変数を定義します

```
export RESOURCE_GROUP=[任意のリソースグループ名]
export NSG_NAME=${RESOURCE_GROUP}-NSG
export IMAGE_ID=`az image list --resource-group $RESOURCE_GROUP --query "[].id" -o tsv`
export SUBNET_ID=`az network vnet subnet show --name [ExRouteのSubnet名] --resource-group [ExRouteのリソースグループ名] --vnet-name [ExRouteのvNet名] --query id -o tsv`
export LB_NAME=${RESOURCE_GROUP}-LB
export BACKEND_POOL=LoadBalancerBackEnd
export FRONTEND_IP=LoadBalancerFrontEnd
export PROBE=LoadBalancerWebProbe
export RULE=weblb
export VMSS_NAME=${RESOURCE_GROUP}-VMSS
export USER_NAME=[任意のユーザ名]
export USER_PASS=[任意のパスワード]
```

* インターナルロードバランサーを作成します

```
az network lb create \
    -g $RESOURCE_GROUP \
    -n $LB_NAME \
    --subnet $SUBNET_ID \
    --backend-pool-name $BACKEND_POOL \
    --frontend-ip-name $FRONTEND_IP
```

* ロードバランサーに正常性プローブを作成します

```
az network lb probe create \
    -g $RESOURCE_GROUP \
    --lb-name $LB_NAME \
    -n $PROBE \
    --port 80 \
    --protocol tcp \
    --interval 15 \
    --threshold 5
```

* ロードバランサーに負荷分散規則を作成します

```
az network lb rule create \
    -g $RESOURCE_GROUP \
    --backend-port 80 \
    --frontend-port 80 \
    --lb-name $LB_NAME \
    -n $RULE \
    --protocol Tcp \
    --backend-pool-name $BACKEND_POOL \
    --floating-ip false \
    --frontend-ip-name $FRONTEND_IP \
    --probe-name $PROBE
```

* VMSSを作成します(ここではインスタンス1台構成で作ってます)

```
az vmss create \
    -g $RESOURCE_GROUP \
    -n $VMSS_NAME \
    --nsg $NSG_NAME \
    --load-balancer $LB_NAME \
    --subnet $SUBNET_ID \
    --image $IMAGE_ID \
    --authentication-type password \
    --admin-username $USER_NAME \
    --admin-password $USER_PASS \
    --backend-pool-name $BACKEND_POOL \
    --instance-count 1
```

* 作成が完了したらロードバランサーのフロントエンドIPアドレスを取得します

```
az network lb show -g $RESOURCE_GROUP -n $LB_NAME --query frontendIpConfigurations[].privateIpAddress -o tsv
```

* 自PCのブラウザからフロントエンドIPアドレス宛にアクセスし、Webページが表示されたらOKです

* せっかくなので手動でスケールアウトしてみます

```
az vmss scale -g $RESOURCE_GROUP -n $VMSS_NAME --new-capacity 2
```

# まとめ

WebAppsがどんなに楽で便利かよく分かりました。
（VMSSでブルーグリーンデプロイメントとかもっと大変そうです・・・）

とはいえ、閉域網でVMが必要でオートスケーリングが必要なケースもあると思います。
その場合はVMSSを選択肢に入れてみてはいかがでしょうか（でもやっぱりレアかな・・・）

# 追記：2018/02/07

## VMSSのインスタンスタイプやディスクタイプを指定して起動させる

VMSSの料金計算してて、そういえばディスクサイズってどうなんだろうと思って調べました。
デフォルトだとManagedDiskのStandard_LRSが設定されるようです。
なので、Windowsを初期イメージから立ち上げた場合は、S10(128GB)の使用量だけが料金として発生するということですね。

インスタンスタイプはStandard_D1_v2がデフォルトです。

では、インスタンスタイプとディスクタイプを指定してVMSSを作成してみます。
以下のように--vm-sku、--storage-skuオプションを付与します。

```
az vmss create \
    -g $RESOURCE_GROUP \
    -n $VMSS_NAME \
    --nsg $NSG_NAME \
    --load-balancer $LB_NAME \
    --subnet $SUBNET_ID \
    --image $IMAGE_ID \
    --authentication-type password \
    --admin-username $USER_NAME \
    --admin-password $USER_PASS \
    --backend-pool-name $BACKEND_POOL \
    --instance-count 1 \
    --vm-sku Standard_DS1_v2 \
    --storage-sku Premium_LRS
```