AKSのimage自動更新するメンテナンスウィンドウをSTGのみ有効にする

AKSのワーカノードのノードプールにVMSSをつかっていて、
本番環境のノードイメージが先に新しくなって問題が起きる事件が2回くらいあったので自動更新をステージングだけにしようかという話に。
https://learn.microsoft.com/ja-jp/azure/aks/planned-maintenance
https://learn.microsoft.com/ja-jp/azure/aks/auto-upgrade-cluster

terraformで環境毎のパラメータを変数にして管理していて、リソースは共通で書いてるので、リソース内部に差分を出す書き方をしないといけなかった（dynamicで解決。countやfor_eachではリソース毎になり細かいブロックの差を出せない）

```hcl
resource "azurerm_kubernetes_cluster" "app" {
  name                       = "${var.system_prefix}-${var.region_prefix}-cluster-aks"
  location                   = azurerm_resource_group.all.location
  resource_group_name        = azurerm_resource_group.all.name
  dns_prefix                 = "${var.system_prefix}-${var.region_prefix}-cluster-aks"
  kubernetes_version         = var.aks_app_version
  automatic_channel_upgrade  = try(var.automatic_channel_upgrade, null)

  dynamic "maintenance_window" {
    for_each = try(var.maintenance_window, null) == null ? [] : [1]
    content {
      dynamic "allowed" {
        for_each = var.maintenance_window.allowed == null ? [] : [1]
        content {
          day   = var.maintenance_window.allowed.day
          hours = var.maintenance_window.allowed.hours
        }
      }
    }
  }
```
要はnullを返すようにとdynamicブロックの利用で解決。
もう少し細かい話だと、
　tryで変数値がなかったらnullを返していて、
　dynamic内のfor_eachではcountでやるように値がnullかそうでないかの条件分岐みたいな感じで変数が無かったら配列やらマップやらを渡さない動きになる模様。環境によって設定自体が要らないようなケースでこの書き方をするしかないことに。


```hcl:count参考例（リソース単位での制御しかできない）
resource "azurerm_frontdoor" "stg" {
  count                                        = var.env == "stg" ? 1 : 0
  name                                         = "${var.env}${var.system_prefix}-${var.region_prefix}-fd"
  location                                     = "global"
～略～
```

変数側↓
```hcl:stgのみ以下を書く(hoursはUTC)
automatic_channel_upgrade = "node-image"
maintenance_window = {
  allowed = {
    day = "Friday"
    hours = ["1","2","3","4","5","6","7"]
  }
```
["1","3"]という指定だと日本時間の10時台と12時台だけ発動するんだと思われる。

変数スキーマ↓
```hcl
variable "automatic_channel_upgrade" {
  default     = ""
}
variable "maintenance_window" {
  default = {
    allowed = {
      day = ""
      hours = []
    }
  }
}
```

参考
https://github.com/aztfmod/terraform-azurerm-caf/blob/main/modules/compute/aks/aks.tf#L267
https://qiita.com/minamijoyo/items/3a7467f70d145ac03324#try
https://qiita.com/minamijoyo/items/3a7467f70d145ac03324#dynamic-blocks

- ノードイメージの手動更新方法

ステージングと同じバージョンにしないと自動と変わらない話になるのでどうにかなりませんかとサポートに聞いてみたところ
→特定のバージョンを指定してノードイメージを更新することはできないと回答あり。
　ただし、現状上げられる最新バージョンの確認は可能だし現在のバージョンの確認も可能
　なので、STGのバージョンと現在上げられるバージョンが同じだった場合に更新する方向にする手動オペレーションに。

上げられる最新バージョンの確認コマンドは以下
```
rg=＜リソースグループ名＞
aksname=＜AKSクラスタ名＞
subscription=＜サブスクリプションID＞
nodepool=＜ノードプール名＞

az account set --subscription $subscription
az aks nodepool get-upgrades \
    --nodepool-name ${nodepool} \
    --cluster-name ${aksname} \
    --resource-group ${rg}
```
現在のノードイメージバージョンはノードプールのノードの一覧から、または`kubectl get nodes -o wide`などで確認できる

イメージ更新コマンドはケースによるようですがとりあえず以下でよさそう
https://learn.microsoft.com/ja-jp/azure/aks/planned-maintenance

```
az aks upgrade \
    --resource-group myResourceGroup \
    --name myAKSCluster \
    --node-image-only
```
更新をトリガーされると終わるまで止まらない（kuredだと中途半端に止まったりしていたので公式に寄せた経緯がある）がPodDusruptionBudget（クラスタ上に最低限起動させておきたいPod数をDeployment単位で指定する等の定義）の影響でサスペンドされたりすることは自動の場合でもありました（設定の問題というよりはNWやノードの調子がアレとかのほかとの複合という感じ）。

イメージ更新された履歴をたどりたい場合は以下のKustoクエリで取れる
```
 AzureActivity
| where OperationNameValue contains "REIMAGE/ACTION"
```

ステージング環境の現在のバージョンと本番環境の上げられる最新が一致してなければ見送りとかをスクリプト組んでやってもいい気はするけど、タイミングによっては延々見送りになりかねなかったりしそうというか。
クラスタのパッチバージョンだけでなくノードイメージのバージョン指定もさせてくれないかなーという気持ちです。（VMSSとしてならできてもAKS越しだからという話だったりはしそうで、ただVMSSとしていじりまわすとAKSの管理から外れそうでやめといたほうがよさそうなので公式的になんとかしてくれないかなという気持ち）


以上