AKS での複数可用性ゾーンの利用と、ZRS Azure Disk Persistent Volume（永続ボリューム）

# 1. はじめに
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117986/13c91c26-b271-2851-8e35-0be501abc2df.png)

今回は AKS(Azure Kubernetes Service) での複数可用性ゾーンの利用と、ZRS(ゾーン冗長ストレージ | Zone Redundant Storage) Azure Disk Persistent Volume についての記事となります。

:::note info
Azure disk の ZRS 対応について、[東日本リージョンでは2023年6月に GA ](https://azure.microsoft.com/ja-jp/updates/generally-available-zone-redundant-storage-for-azure-disks-is-now-available-in-japan-east-and-korea-central/)されており、既に VM 等で利用されている方も多いかもしれません。
今回は AKS での 利用についてあらためて検証した結果を記事にしました。 
なお Azure disk の ZRS 対応リージョンについては、順次拡大中となります！
:::

AKS(Azure Kubernetes Service)  では、可用性ゾーンを利用することで、クラスタの高可用性を向上させることができます。
可用性ゾーン（Availability Zone)とは、物理的に分離されたデータセンターのグループで、同じリージョン内に複数存在します。

また、AKS(Azure Kubernetes Service) では、ノードプールごとに可用性ゾーンを指定することができます。
複数の可用性ゾーンを指定した場合、ノードプール内のノードは、指定された可用性ゾーンに分散されて起動し、ロードバランサーやパブリックIPアドレスなどのリソースも、ゾーン冗長に作成されます。

しかし、AKS で可用性ゾーン（Availability Zone)を利用する場合、かつ、Azure Disk Persistent Volume(永続領域) を使用する際に注意が必要です。

https://learn.microsoft.com/ja-jp/azure/aks/availability-zones

> Azure ディスク可用性ゾーンのサポート
> Azure マネージド LRS ディスクを使用するボリュームはゾーン冗長リソースではありません。ゾーンをまたいでアタッチされるため、サポートされません。 ターゲット ポッドをホストしている指定ノードと同じゾーン内のボリュームを併置する必要があります。
> Azure マネージド ZRS ディスク (Azure Disk CSI ドライバー v1.5.0 以降でサポート) を使用するボリュームは、ゾーン冗長リソースです。 これらのボリュームは、すべてのゾーンおよび非ゾーン エージェント ノード上でスケジュールできます。

上記の制限について詳しく解説していきます。

Azure Disk は、仮想マシンにアタッチされる永続的なストレージで、AKS では、Persistent Volume (PV) と Persistent Volume Claim (PVC) を使って、Azure Disk を Pod にマウントすることができます。

また、Azure Disk には LRS (ローカル冗長ストレージ | Locally Redundant Storage) とZRS (ゾーン冗長ストレージ | Zone Redundant Storage) の2種類があります。
* LRS Azure Disk は、同じ 可用性ゾーン（Availability Zone)内の3つのレプリカで構成されます。
* ZRS Azure Disk は、異なる 可用性ゾーン（Availability Zone)内の3つのレプリカで構成されます。
* 以下は　LRS と ZRS の違いイメージ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117986/2f9dc733-ba48-10b7-afca-63565fdce684.png)

ここで、LRS Azure Disk を使用する場合は、以下の点に注意しなければなりません。

- LRS Azure Diskはゾーン冗長に対応していません。つまり、Azure Disk が作成された可用性ゾーン（Availability Zone)がダウンした場合は、Azure Disk も利用できなくなります。
- LRS Azure Disk は可用性ゾーン（Availability Zone)をまたいでアタッチできません。つまり、Azure Disk が作成された可用性ゾーンと異なる可用性ゾーン上で起動しているノードからは Azure Disk をマウントすることができません。
- AKS では、LRS Azure Disk を動的に作成する場合は、最初にアタッチされたノードのゾーンに固定されます。つまり、ノードプールのスケールアップやスケールダウンなどでノードのゾーンが変わった場合は、Pod から Azure Disk をマウントできなくなります。

これらの制約からわかるように、LRS Azure Disk はゾーン冗長の AKS クラスタには適していません。
Pod が起動するためには、Azure Disk と同じゾーンのノードが必ず存在しなければなりませんので、可用性ゾーンを1つに固定するなどの対処が必要でした。

しかし、可用性ゾーンを利用する目的は、ノードの障害やメンテナンスなどでノードが利用できなくなった場合でも、別の可用性ゾーン上のノードで Pod を再配置することですが、LRS Azure Disk を使用すると、この目的を達成できません。

では、どうすればよいのでしょうか？
複数の可用性ゾーンを有効にした AKS クラスタで、Azure Disk の Persistent Volume (PV) が必要な場合は、かならず ZRS Azure Disk を使用してください。

ZRS Azure Disk は、以下の特徴を持ちます。

- ZRS Azure Disk はゾーン冗長に対応しています。つまり、Azure Disk が作成された可用性ゾーンがダウンしても、別の可用性ゾーンのレプリカを利用できます。
- ZRS Azure Disk は可用性ゾーンをまたいでアタッチできます。つまり、Azure Disk が作成された可用性ゾーンと異なる可用性ゾーン上のノードでもマウントすることができます。
- ZRS Azure Disk を動的に作成した場合でも、最初にアタッチされたノードの可用性ゾーンに依存しません。つまり、ノードプールのスケールアップやスケールダウンなどでノードの可用性ゾーンが変わっても、Azure Disk をマウントすることができます。

ZRS Azure Disk は、ゾーン冗長の AKS クラスタにおいて、Azure Disk を使用する場合のベストプラクティスです。
ZRS Azure Disk を使用することで、可用性ゾーンのメリットを最大限に活かすことができます。

それでは、このあと、ゾーン冗長を有効にした AKS クラスタでの　LRS と ZRS Azure Disk の Persistent Volume (PV) 挙動の違いを確認していきます。

:::note info
本記事の実行結果等は執筆時点（2022年12月13日）での内容となります
:::

# 2.事前準備
1. ゾーン冗長 AKS クラスタの作成
2. カスタムの`StorageClass`の作成
3. PVC と Deployment の作成

## 2.1. ゾーン冗長 AKS クラスタの作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117986/a88f9d9c-2ad7-07ee-903e-59ce74bcfd60.png)

初期状態として以下のような可用性ゾーンを有効にした AKS クラスタを用意します。
* リージョン: `japaneast`
* ノードプール
　　* 可用性ゾーン: ゾーン 1, ゾーン 2
　　* ノード数： 2
* az cli で作成する場合は以下 

``` bash 
# create resouce group 
az group create --name rg-demo-aks-zrs --location japaneast
# create AKS Cluster, 2node, 2zone
az aks create -g rg-demo-aks-zrs \
-n aks-demo-zrs01 \
--enable-managed-identity \
--enable-addons monitoring \
--enable-msi-auth-for-monitoring  \
--node-count 2 \
--generate-ssh-keys --zones 1 2
```

作成した AKS ノードプールのノード(VM)の可用性ゾーン（Availability Zone)を`kubectl`コマンドから確認します。

``` bash
takuya [ ~ ]$ az aks get-credentials --resource-group demo-aks-zrs --name demo-aks-zrs01
Merged "demo-aks-zrs01" as current context in /home/takuya/.kube/config
takuya [ ~ ]$ kubectl get node -L topology.kubernetes.io/region -L topology.kubernetes.io/zone
NAME                                STATUS   ROLES   AGE   VERSION   REGION      ZONE
aks-nodepool1-28151597-vmss000000   Ready    agent   34m   v1.27.7   japaneast   japaneast-1
aks-nodepool1-28151597-vmss000001   Ready    agent   34m   v1.27.7   japaneast   japaneast-2
```

* Azure Poratl からもAKS ノードプールの状況を確認

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117986/ea9bdf9b-3e27-1b9e-2270-8cd4b3f8105f.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117986/856e8794-2d79-dfbb-3503-78f8b6441003.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117986/ea4a21d0-7a5a-42fe-578e-a9dd7bbe35b2.png)

以上で、複数の可用性ゾーンを有効にした AKS クラスタの準備ができました。

## 2.2. カスタムの`StorageClass`の作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117986/3377429c-133a-7f28-cf16-643c47db797c.png)

つづいて、Azure disk の LRS / ZRS は `StorageClass`で指定するため、ぞれぞれのカスタムの`StorageClass`を用意します。

:::note info
デフォルトで用意されている、`StorageClass`は、LRS のため、ZRS のカスタムの`StorageClass`の作成します。
:::

ベースとなる AKS 初期から組み込まれている`managed-csi`の内用を確認すると
以下の通り、`skuname: StandardSSD_LRS`であることがわかります。

``` bash
takuya [ ~ ]$ kubectl get sc
NAME                    PROVISIONER          RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
azurefile               file.csi.azure.com   Delete          Immediate              true                   60m
azurefile-csi           file.csi.azure.com   Delete          Immediate              true                   60m
azurefile-csi-premium   file.csi.azure.com   Delete          Immediate              true                   60m
azurefile-premium       file.csi.azure.com   Delete          Immediate              true                   60m
default (default)       disk.csi.azure.com   Delete          WaitForFirstConsumer   true                   60m
managed                 disk.csi.azure.com   Delete          WaitForFirstConsumer   true                   60m
managed-csi             disk.csi.azure.com   Delete          WaitForFirstConsumer   true                   60m
managed-csi-premium     disk.csi.azure.com   Delete          WaitForFirstConsumer   true                   60m
managed-premium         disk.csi.azure.com   Delete          WaitForFirstConsumer   true                   60m
managed-premium         disk.csi.azure.com   Delete          WaitForFirstConsumer   true                   60m
takuya [ ~ ]$ kubectl get sc managed-csi -o yaml
allowVolumeExpansion: true
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  creationTimestamp: "2023-12-13T03:47:20Z"
  labels:
    addonmanager.kubernetes.io/mode: EnsureExists
    kubernetes.io/cluster-service: "true"
  name: managed-csi
  resourceVersion: "380"
  uid: 56c60813-8a7f-45bc-b593-9c4f834ab72e
parameters:
  skuname: StandardSSD_LRS
provisioner: disk.csi.azure.com
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer
takuya [ ~ ]$ 
```

`managed-csi`と同内容（`skuname: StandardSSD_LRS`）で別名`managed-csi-lrs`の`StorageClass`を用意します。

``` managed-csi-lrs.yaml
allowVolumeExpansion: true
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  labels:
    addonmanager.kubernetes.io/mode: EnsureExists
    kubernetes.io/cluster-service: "true"
  name: managed-csi-lrs
parameters:
  skuname: StandardSSD_LRS
provisioner: disk.csi.azure.com
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer
```

つづいて、`managed-csi`の内容を`StandardSSD_ZRS`変更した、別名`managed-csi-zrs`の`StorageClass`を用意します。

``` managed-csi-zrs.yaml
allowVolumeExpansion: true
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  labels:
    addonmanager.kubernetes.io/mode: EnsureExists
    kubernetes.io/cluster-service: "true"
  name: managed-csi-zrs
parameters:
  skuname: StandardSSD_ZRS
provisioner: disk.csi.azure.com
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer
```

以下の通り、それぞれのカスタムの`StorageClass`を作成します。

``` bash
takuya [ ~ ]$ kubectl apply -f managed-csi-lrs.yaml 
storageclass.storage.k8s.io/managed-csi-lrs created
takuya [ ~ ]$ kubectl apply -f managed-csi-zrs.yaml 
storageclass.storage.k8s.io/managed-csi-zrs created
takuya [ ~ ]$ kubectl get sc | grep managed-csi
managed-csi             disk.csi.azure.com   Delete          WaitForFirstConsumer   true                   68m
managed-csi-lrs         disk.csi.azure.com   Delete          WaitForFirstConsumer   true                   5m38s
managed-csi-premium     disk.csi.azure.com   Delete          WaitForFirstConsumer   true                   68m
managed-csi-zrs         disk.csi.azure.com   Delete          WaitForFirstConsumer   true                   5m30s
takuya [ ~ ]$ 
```

## 2.3. PVC と Deployment の作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117986/786bfcec-7b8f-a3a9-3c66-944d26f0e1fc.png)

つづいて、作成したカスタムの`StorageClass`を指定した PVC と　その PVC を利用する Deployment のマニフェストファイルを準備して AKS クラスタへ`kubectl apply`します。
これにより、Dynamic にそれぞれ PV が作成され、Deployment により作成された Pod へ Azure disk がマウントされることを確認します。



* LRS 用の PVC

``` pvc-azuredisk-csi-lrs01.yaml
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-azuredisk-lrs01
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
  storageClassName: managed-csi-lrs
```

* ZRS 用の PVC
``` pvc-azuredisk-csi-zrs01.yaml
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-azuredisk-zrs01
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
  storageClassName: managed-csi-zrs
```

* LRS 用の PVC を指定した Deployment
``` dp-pvc-lrs01.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-azuredisk-lrs01
  labels:
    node: aci
    app: nginx-azuredisk-lrs01
spec:
  strategy:
    type: RollingUpdate
  replicas: 1
  selector:
    matchLabels:
      app: nginx-azuredisk-lrs01
  template:
    metadata:
      name: nginx-azuredisk-lrs01
      labels:
        app: nginx-azuredisk-lrs01
    spec:
      containers:
        - image: mcr.microsoft.com/oss/nginx/nginx:1.17.3-alpine
          name: nginx-azuredisk
          command:
            - "/bin/sh"
            - "-c"
            - while true; do echo $(date) >> /mnt/azuredisk/outfile; sleep 1; done
          volumeMounts:
            - name: azuredisk
              mountPath: "/mnt/azuredisk"
      volumes:
        - name: azuredisk
          persistentVolumeClaim:
            claimName: pvc-azuredisk-lrs01
```

* ZRS 用の PVC を指定した Deployment

``` dp-pvc-zrs01.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-azuredisk-zrs01
  labels:
    node: aci
    app: nginx-azuredisk-zrs01
spec:
  strategy:
    type: RollingUpdate
  replicas: 1
  selector:
    matchLabels:
      app: nginx-azuredisk-zrs01
  template:
    metadata:
      name: nginx-azuredisk-zrs01
      labels:
        app: nginx-azuredisk-zrs01
    spec:
      containers:
        - image: mcr.microsoft.com/oss/nginx/nginx:1.17.3-alpine
          name: nginx-azuredisk
          command:
            - "/bin/sh"
            - "-c"
            - while true; do echo $(date) >> /mnt/azuredisk/outfile; sleep 1; done
          volumeMounts:
            - name: azuredisk
              mountPath: "/mnt/azuredisk"
      volumes:
        - name: azuredisk
          persistentVolumeClaim:
            claimName: pvc-azuredisk-zrs01
```

以下のように PVC , Deployment を Apply し Pod が正常に起動してそれぞれ LRS および ZRS の Azure Disk の PV をマウントできることを確認

``` bash
ttakuya [ ~ ]$ kubectl apply -f pvc-azuredisk-csi-lrs01.yaml 
persistentvolumeclaim/pvc-azuredisk-lrs01 created
takuya [ ~ ]$ kubectl apply -f pvc-azuredisk-csi-zrs01.yaml 
persistentvolumeclaim/pvc-azuredisk-zrs01 created
takuya [ ~ ]$ kubectl get pv,pvc -o wide
NAME                                        STATUS    VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS      AGE   VOLUMEMODE
persistentvolumeclaim/pvc-azuredisk-lrs01   Pending                                      managed-csi-lrs   60s   Filesystem
persistentvolumeclaim/pvc-azuredisk-zrs01   Pending                                      managed-csi-zrs   52s   Filesystem
takuya [ ~ ]$ 
takuya [ ~ ]$ kubectl apply -f dp-pvc-lrs01.yaml
deployment.apps/nginx-azuredisk-lrs01 created
takuya [ ~ ]$ kubectl apply -f dp-pvc-zrs01.yaml
deployment.apps/nginx-azuredisk-zrs01 created
takuya [ ~ ]$ 
takuya [ ~ ]$ kubectl get pv,pvc,deploy,pod -o wide
NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                         STORAGECLASS      REASON   AGE   VOLUMEMODE
persistentvolume/pvc-10b6bd60-89cf-489a-b68f-966143763b4e   10Gi       RWO            Delete           Bound    default/pvc-azuredisk-lrs01   managed-csi-lrs            63s   Filesystem
persistentvolume/pvc-95c186f1-ba4f-4e4f-ae6a-af2d565acfcb   10Gi       RWO            Delete           Bound    default/pvc-azuredisk-zrs01   managed-csi-zrs            58s   Filesystem

NAME                                        STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS      AGE     VOLUMEMODE
persistentvolumeclaim/pvc-azuredisk-lrs01   Bound    pvc-10b6bd60-89cf-489a-b68f-966143763b4e   10Gi       RWO            managed-csi-lrs   3m7s    Filesystem
persistentvolumeclaim/pvc-azuredisk-zrs01   Bound    pvc-95c186f1-ba4f-4e4f-ae6a-af2d565acfcb   10Gi       RWO            managed-csi-zrs   2m59s   Filesystem

NAME                                    READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS        IMAGES                                            SELECTOR
deployment.apps/nginx-azuredisk-lrs01   1/1     1            1           66s   nginx-azuredisk   mcr.microsoft.com/oss/nginx/nginx:1.17.3-alpine   app=nginx-azuredisk-lrs01
deployment.apps/nginx-azuredisk-zrs01   1/1     1            1           60s   nginx-azuredisk   mcr.microsoft.com/oss/nginx/nginx:1.17.3-alpine   app=nginx-azuredisk-zrs01

NAME                                         READY   STATUS    RESTARTS   AGE   IP            NODE                                NOMINATED NODE   READINESS GATES
pod/nginx-azuredisk-lrs01-5c4687b6d6-9474c   1/1     Running   0          67s   10.244.0.9    aks-nodepool1-28151597-vmss000001   <none>           <none>
pod/nginx-azuredisk-zrs01-96fc46f84-mrpnz    1/1     Running   0          61s   10.244.0.10   aks-nodepool1-28151597-vmss000001   <none>           <none>
takuya [ ~ ]$ 
```

また、Podは可用性ゾーン`japaneast-2`の`aks-nodepool1-28151597-vmss000001`ノード上で動作していることがわかります。 

> 再掲： 作成した AKS ノードプールを`kubectl`コマンドから確認
> ``` bash
> takuya [ ~ ]$ kubectl get node -L topology.kubernetes.io/region -L topology.kubernetes.io/zone
> NAME                                STATUS   ROLES   AGE   VERSION   REGION      ZONE
> aks-nodepool1-28151597-vmss000000   Ready    agent   34m   v1.27.7   japaneast   japaneast-1
> aks-nodepool1-28151597-vmss000001   Ready    agent   34m   v1.27.7   japaneast   japaneast-2
> ```

また、Azure Portal から、PV 名より`MC_`リソースグループ内に Azure disk がそれぞれ作成されていることを確認します。

* pvc-10b6bd60-89cf-489a-b68f-966143763b4e
    * `Standard SSD LRS`
    * `Japan East (ゾーン 2)`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117986/6a6a85d7-fbf3-223e-c3c4-06234b16025d.png)

* pvc-95c186f1-ba4f-4e4f-ae6a-af2d565acfcb
    * `Standard SSD ZRS`
    * `Japan East`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117986/5e490638-6317-a684-0dfe-71835700ae9f.png)

以上で事前準備が整いました。

# 3. AKSのノード数を減らし、LRS と ZRS  Azure disk の PV 挙動の違いを確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117986/13c91c26-b271-2851-8e35-0be501abc2df.png)

いよいよ、ノード数を手動で2台から1台へ減少させ、LRS と ZRS  Azure disk の PV 挙動の違いを確認していきます。

Azure Portal より、ノードプールのスケーリングでノード数`2`から`1`へ変更します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117986/bddf6df2-28af-ea3c-21c9-2f32269cb87c.png)

ノードが1台のみになっていることを確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117986/02aaddb9-ec74-d47f-9909-7367b71837cb.png)

`kubectl`コマンドからも確認します。
```
takuya [ ~ ]$ kubectl get node -L topology.kubernetes.io/region -L topology.kubernetes.io/zone
NAME                                STATUS   ROLES   AGE    VERSION   REGION      ZONE
aks-nodepool1-28151597-vmss000000   Ready    agent   143m   v1.27.7   japaneast   japaneast-1
takuya [ ~ ]$ 
```

つづいて、Pod の状況を確認します。
+ node02(vmss000001) で Running 中の Pod が 削除（terminated）され、node02(vmss000001) より node1(vmss000000) で Pod を起動しようとするが
+ node01(vmss000000) の可用性ゾーンでは、LRS の Azure Disk が存在しないため、LRS の Pod は PV/PVC をマウントできず、`Pending`の状態となります。
+ 一方 ZRS の Pod は、node01 のリージョンに Azure Disk が存在するため、Pod はPV/PVC をマウント可能なため、`Running`の状態となります。

```
takuya [ ~ ]$ kubectl get pv,pvc,deploy,pod -o wide 
NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                         STORAGECLASS      REASON   AGE   VOLUMEMODE
persistentvolume/pvc-10b6bd60-89cf-489a-b68f-966143763b4e   10Gi       RWO            Delete           Bound    default/pvc-azuredisk-lrs01   managed-csi-lrs            33m   Filesystem
persistentvolume/pvc-95c186f1-ba4f-4e4f-ae6a-af2d565acfcb   10Gi       RWO            Delete           Bound    default/pvc-azuredisk-zrs01   managed-csi-zrs            33m   Filesystem

NAME                                        STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS      AGE   VOLUMEMODE
persistentvolumeclaim/pvc-azuredisk-lrs01   Bound    pvc-10b6bd60-89cf-489a-b68f-966143763b4e   10Gi       RWO            managed-csi-lrs   35m   Filesystem
persistentvolumeclaim/pvc-azuredisk-zrs01   Bound    pvc-95c186f1-ba4f-4e4f-ae6a-af2d565acfcb   10Gi       RWO            managed-csi-zrs   35m   Filesystem

NAME                                    READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS        IMAGES                                            SELECTOR
deployment.apps/nginx-azuredisk-lrs01   0/1     1            0           33m   nginx-azuredisk   mcr.microsoft.com/oss/nginx/nginx:1.17.3-alpine   app=nginx-azuredisk-lrs01
deployment.apps/nginx-azuredisk-zrs01   1/1     1            1           33m   nginx-azuredisk   mcr.microsoft.com/oss/nginx/nginx:1.17.3-alpine   app=nginx-azuredisk-zrs01

NAME                                         READY   STATUS    RESTARTS   AGE     IP            NODE                                NOMINATED NODE   READINESS GATES
pod/nginx-azuredisk-lrs01-5c4687b6d6-2hn7l   0/1     Pending   0          7m14s   <none>        <none>                              <none>           <none>
pod/nginx-azuredisk-zrs01-96fc46f84-wrv85    1/1     Running   0          7m14s   10.244.1.13   aks-nodepool1-28151597-vmss000000   <none>           <none>
```

`Pending`状態の Pod の詳細状況を`kubectl describe`で確認すると

```
takuya [ ~ ]$ kubectl describe pod/nginx-azuredisk-lrs01-5c4687b6d6-2hn7l 
Name:             nginx-azuredisk-lrs01-5c4687b6d6-2hn7l
Namespace:        default
Priority:         0
Service Account:  default
Node:             <none>
Labels:           app=nginx-azuredisk-lrs01
                  pod-template-hash=5c4687b6d6
Annotations:      <none>
Status:           Pending
IP:               
IPs:              <none>
Controlled By:    ReplicaSet/nginx-azuredisk-lrs01-5c4687b6d6
Containers:
  nginx-azuredisk:
    Image:      mcr.microsoft.com/oss/nginx/nginx:1.17.3-alpine
    Port:       <none>
    Host Port:  <none>
    Command:
      /bin/sh
      -c
      while true; do echo $(date) >> /mnt/azuredisk/outfile; sleep 1; done
    Environment:  <none>
    Mounts:
      /mnt/azuredisk from azuredisk (rw)
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-pknnr (ro)
Conditions:
  Type           Status
  PodScheduled   False 
Volumes:
  azuredisk:
    Type:       PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
    ClaimName:  pvc-azuredisk-lrs01
    ReadOnly:   false
  kube-api-access-pknnr:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       <nil>
    DownwardAPI:             true
QoS Class:                   BestEffort
Node-Selectors:              <none>
Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
  Type     Reason            Age    From               Message
  ----     ------            ----   ----               -------
  Warning  FailedScheduling  8m56s  default-scheduler  0/2 nodes are available: 1 node(s) had volume node affinity conflict, 1 node(s) were unschedulable. preemption: 0/2 nodes are available: 2 Preemption is not helpful for scheduling..
  Warning  FailedScheduling  3m54s  default-scheduler  0/1 nodes are available: 1 node(s) had volume node affinity conflict. preemption: 0/1 nodes are available: 1 Preemption is not helpful for scheduling..
takuya [ ~ ]$ 
```

以下のように`FailedScheduling `として、LRS の Pod は Azure Disk を`0/1 nodes are available: 1 node(s) had volume node affinity conflict. preemption: 0/1 nodes are available: 1 Preemption is not helpful for scheduling..`というメッセージで起動に失敗していることを確認できます。

```  Type     Reason            Age    From               Message
  ----     ------            ----   ----               -------
  Warning  FailedScheduling  8m56s  default-scheduler  0/2 nodes are available: 1 node(s) had volume node affinity conflict, 1 node(s) were unschedulable. preemption: 0/2 nodes are available: 2 Preemption is not helpful for scheduling..
  Warning  FailedScheduling  3m54s  default-scheduler  0/1 nodes are available: 1 node(s) had volume node affinity conflict. preemption: 0/1 nodes are available: 1 Preemption is not helpful for scheduling..
```

# 4. TIPS Kubernetes内での LRS, ZRS PV の制御
Kubernetes 上で、LRS の Azure Disk と ZRS の Azure Disk の PV リソースの中身を見てみると、Kubernetes の Afinity ルール の機能を利用して、可用性ゾーンを制御していることがわかります。

```
takuya [ ~ ]$ kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                         STORAGECLASS      REASON   AGE
pvc-10b6bd60-89cf-489a-b68f-966143763b4e   10Gi       RWO            Delete           Bound    default/pvc-azuredisk-lrs01   managed-csi-lrs            39m
pvc-95c186f1-ba4f-4e4f-ae6a-af2d565acfcb   10Gi       RWO            Delete           Bound    default/pvc-azuredisk-zrs01   managed-csi-zrs            39m
takuya [ ~ ]$ kubectl get pv pvc-10b6bd60-89cf-489a-b68f-966143763b4e -o yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  annotations:
    pv.kubernetes.io/provisioned-by: disk.csi.azure.com
    volume.kubernetes.io/provisioner-deletion-secret-name: ""
    volume.kubernetes.io/provisioner-deletion-secret-namespace: ""
  creationTimestamp: "2023-12-13T05:41:32Z"
  finalizers:
  - kubernetes.io/pv-protection
  - external-attacher/disk-csi-azure-com
  name: pvc-10b6bd60-89cf-489a-b68f-966143763b4e
  resourceVersion: "28412"
  uid: f6fc711e-e32a-4204-a1ff-7d899a761554
spec:
  accessModes:
  - ReadWriteOnce
  capacity:
    storage: 10Gi
  claimRef:
    apiVersion: v1
    kind: PersistentVolumeClaim
    name: pvc-azuredisk-lrs01
    namespace: default
    resourceVersion: "28386"
    uid: 10b6bd60-89cf-489a-b68f-966143763b4e
  csi:
    driver: disk.csi.azure.com
    volumeAttributes:
      csi.storage.k8s.io/pv/name: pvc-10b6bd60-89cf-489a-b68f-966143763b4e
      csi.storage.k8s.io/pvc/name: pvc-azuredisk-lrs01
      csi.storage.k8s.io/pvc/namespace: default
      requestedsizegib: "10"
      skuname: StandardSSD_LRS
      storage.kubernetes.io/csiProvisionerIdentity: 1702439180671-9937-disk.csi.azure.com
    volumeHandle: /subscriptions/379623ee-242c-4125-9c4c-7dfb8d74cdc9/resourceGroups/mc_demo-aks-zrs_demo-aks-zrs01_japaneast/providers/Microsoft.Compute/disks/pvc-10b6bd60-89cf-489a-b68f-966143763b4e
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: topology.disk.csi.azure.com/zone
          operator: In
          values:
          - japaneast-2
  persistentVolumeReclaimPolicy: Delete
  storageClassName: managed-csi-lrs
  volumeMode: Filesystem
status:
  phase: Bound
takuya [ ~ ]$  kubectl get pv pvc-95c186f1-ba4f-4e4f-ae6a-af2d565acfcb  -o yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  annotations:
    pv.kubernetes.io/provisioned-by: disk.csi.azure.com
    volume.kubernetes.io/provisioner-deletion-secret-name: ""
    volume.kubernetes.io/provisioner-deletion-secret-namespace: ""
  creationTimestamp: "2023-12-13T05:41:37Z"
  finalizers:
  - kubernetes.io/pv-protection
  - external-attacher/disk-csi-azure-com
  name: pvc-95c186f1-ba4f-4e4f-ae6a-af2d565acfcb
  resourceVersion: "28462"
  uid: e496d654-b15f-4a34-8419-8afe702d2296
spec:
  accessModes:
  - ReadWriteOnce
  capacity:
    storage: 10Gi
  claimRef:
    apiVersion: v1
    kind: PersistentVolumeClaim
    name: pvc-azuredisk-zrs01
    namespace: default
    resourceVersion: "28430"
    uid: 95c186f1-ba4f-4e4f-ae6a-af2d565acfcb
  csi:
    driver: disk.csi.azure.com
    volumeAttributes:
      csi.storage.k8s.io/pv/name: pvc-95c186f1-ba4f-4e4f-ae6a-af2d565acfcb
      csi.storage.k8s.io/pvc/name: pvc-azuredisk-zrs01
      csi.storage.k8s.io/pvc/namespace: default
      requestedsizegib: "10"
      skuname: StandardSSD_ZRS
      storage.kubernetes.io/csiProvisionerIdentity: 1702439180671-9937-disk.csi.azure.com
    volumeHandle: /subscriptions/379623ee-242c-4125-9c4c-7dfb8d74cdc9/resourceGroups/mc_demo-aks-zrs_demo-aks-zrs01_japaneast/providers/Microsoft.Compute/disks/pvc-95c186f1-ba4f-4e4f-ae6a-af2d565acfcb
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: topology.disk.csi.azure.com/zone
          operator: In
          values:
          - japaneast-1
      - matchExpressions:
        - key: topology.disk.csi.azure.com/zone
          operator: In
          values:
          - japaneast-2
      - matchExpressions:
        - key: topology.disk.csi.azure.com/zone
          operator: In
          values:
          - japaneast-3
      - matchExpressions:
        - key: topology.disk.csi.azure.com/zone
          operator: In
          values:
          - ""
  persistentVolumeReclaimPolicy: Delete
  storageClassName: managed-csi-zrs
  volumeMode: Filesystem
status:
  phase: Bound
takuya [ ~ ]$ 
```

`nodeAffinity:`により、マウント可能な可用性ゾーンがコントロールされていることがわかります。

* LRSの場合は作成時の特定の可用性ゾーン`japaneast-2`のみが設定されている

``` 
# 抜粋
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: topology.disk.csi.azure.com/zone
          operator: In
          values:
          - japaneast-2
```

* ZRSの場合は、リージョン内のすべての可用性ゾーン`japaneast-1`,`japaneast-2`,`japaneast-3`,””(可用性ゾーン無し)が設定されている

```
# 抜粋
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: topology.disk.csi.azure.com/zone
          operator: In
          values:
          - japaneast-1
      - matchExpressions:
        - key: topology.disk.csi.azure.com/zone
          operator: In
          values:
          - japaneast-2
      - matchExpressions:
        - key: topology.disk.csi.azure.com/zone
          operator: In
          values:
          - japaneast-3
      - matchExpressions:
        - key: topology.disk.csi.azure.com/zone
          operator: In
          values:
          - ""
```

# 5. まとめ
* 複数の可用性ゾーンを使用する Azure Kubernetes Service (AKS) クラスターにおいて、Azure Disk の PersitentVolume(PV)を利用する場合は、ZRS (ゾーン冗長ストレージ | Zone Redundant Storage)を利用しないと、ノードの増減や、バージョンアップ等のローリングアップデートなどで、意図した可用性を実現できませんのでご注意ください。
* 複数の可用性ゾーンを使用する AKS クラスターで、Azure Disk の PersitentVolume(PV)を利用する必要がある場合は、必ず、ZRS (ゾーン冗長ストレージ | Zone Redundant Storage)を利用してください。

以上

---