AnsibleでAKS（Azure Kubernete Service）を操作する

# はじめに
IaC（Infrastructure as Code）が叫ばれだして久しい昨今、みなさんどんなツールをお使いでしょうか。
[Terraform](https://www.terraform.io/)？[ARMテンプレート](https://docs.microsoft.com/ja-jp/azure/azure-resource-manager/templates/)？はたまた[Bicep](https://docs.microsoft.com/ja-jp/azure/azure-resource-manager/bicep/)でしょうか。
本記事では、[Ansible](https://www.ansible.com/)を利用してのAKS操作を試してみた記録をまとめます。

# 環境
本記事の内容は、[Azure Cloud Shell](https://azure.microsoft.com/ja-jp/features/cloud-shell/#overview)(Bash版)上で実行しました。

```bash
$ ansible --version
ansible 2.10.2
  config file = None
  configured module search path = ['/home/ussvgr/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /opt/ansible/lib/python3.7/site-packages/ansible
  executable location = /opt/ansible/bin/ansible
  python version = 3.7.3 (default, Jul 25 2020, 13:03:44) [GCC 8.3.0]
```

# 準備：Collectionのインストール（バージョンアップ）
Cloud Shell上の環境には、AnsibleでAzureリソースを操作するためのモジュールがデフォルトで導入済みです。
が、いささかバージョンが古いのでバージョンアップしておきます。（2021/10/31時点の情報です）

導入済みのCollectionのバージョンは、以下の通り1.2.0のようです。（CHANGELOG.mdファイルの最新の記述から確認）

```bash
$ head /opt/ansible/lib/python3.7/site-packages/ansible_collections/azure/azcollection/CHANGELOG.md
# Change Log

## v1.2.0 (2020-10-09)

### NEW MODULES
  - azure_rm_backupazurevm  ([#248](https://github.com/ansible-collections/azure/pull/248))
  - azure_rm_backupazurevm_info ([#248](https://github.com/ansible-collections/azure/pull/248))
  - azure_rm_recoveryservicesvault ([#254](https://github.com/ansible-collections/azure/pull/254))
  - azure_rm_openshiftmanagedcluster ([#276](https://github.com/ansible-collections/azure/pull/276))
```

ansible-galaxyコマンドで最新のCollectionをインストールします。

```bash
$ ansible-galaxy collection install azure.azcollection
Starting galaxy collection install process
Process install dependency map
Starting collection install process
Installing 'azure.azcollection:1.10.0' to '/home/ussvgr/.ansible/collections/ansible_collections/azure/azcollection'
Downloading https://galaxy.ansible.com/download/azure-azcollection-1.10.0.tar.gz to /home/ussvgr/.ansible/tmp/ansible-local-1517j3twu3a/tmpo_e77nln
azure.azcollection (1.10.0) was installed successfully
```
v.1.10.0がインストールされました。

# 最小のPlaybookでAKSクラスターを作成する
Azure公式にも[AnsibleでAKSクラスターを作成するドキュメント](https://docs.microsoft.com/ja-jp/azure/developer/ansible/aks-configure-clusters)がありますが、こちらに記載されているサンプルのPlaybookはazure.azcollectionのv1.2.0を前提として記述されています。
最新のバージョンではサービスプリンシパルやノードのSSH keyを省略可能なため、ただクラスタを作成するだけなら以下のように簡略化できます。
（サービスプリンシパル未指定時はマネージドIDで作成されます）

```aks.yaml
- name: Create Azure Kubernetes Service
  hosts: localhost
  connection: local
  tasks:
  - name: Create resource group
    azure.azcollection.azure_rm_resourcegroup:
      name: aks_rg
      location: japaneast
  - name: Create AKS Cluster
    azure.azcollection.azure_rm_aks:
      name: akstest
      kubernetes_version: 1.20.7
      location: japaneast
      resource_group: aks_rg
      dns_prefix: akstest
      agent_pool_profiles:
        - name: agentpool1
          mode: System
          count: 1
          vm_size: Standard_D2_v2
```

ansible-playbookコマンドを実行します。

```bash
$ ansible-playbook aks.yaml
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

PLAY [Create Azure Kubernetes Service] *********************************************************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************************************************************
ok: [localhost]

TASK [Create resource group] *******************************************************************************************************************************************************
changed: [localhost]

TASK [Create AKS Cluster] **********************************************************************************************************************************************************
[WARNING]: Azure API profile latest does not define an entry for ContainerServiceClient
changed: [localhost]

PLAY RECAP *************************************************************************************************************************************************************************
localhost                  : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
```

できました。
![スクリーンショット 2021-10-31 15.07.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274613/3944f261-0976-8a8b-624f-9b3a198043a3.png)

# ノードのスケールアウト
ノードの数を1として作成しましたが、これを2台にスケールアウトさせます。

```（抜粋）aks.yaml
      agent_pool_profiles:
        - name: agentpool1
          mode: System
          count: 2
          vm_size: Standard_D2_v2
```
先程のPlaybookのagent_pool_profiles内のcountを１から２へ変更しました。

再度ansible-playbookコマンドを実行します。

```bash
$ ansible-playbook aks.yaml
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

PLAY [Create Azure Kubernetes Service] *********************************************************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************************************************************
ok: [localhost]

TASK [Create resource group] *******************************************************************************************************************************************************
ok: [localhost]

TASK [Create AKS Cluster] **********************************************************************************************************************************************************
[WARNING]: Azure API profile latest does not define an entry for ContainerServiceClient
changed: [localhost]

PLAY RECAP *************************************************************************************************************************************************************************
localhost                  : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
```
適用途中の状態
![スクリーンショット 2021-10-31 15.16.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274613/d26130f9-6445-6267-09a4-e90141e09020.png)
ansible-playbookコマンドが完了すると全ノードが準備完了になりました。
![スクリーンショット 2021-10-31 15.16.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274613/8907a087-1bca-e18a-5057-7d11c71b2a40.png)

countの値を再度1に戻して適用すればスケールインされます。

# ノードのスケールアップ（SKUの変更）
ノードのサイズを変更してみます。
先程のPlaybookのagent_pool_profiles内のvm_sizeを「Standard_D2_v2」から「Standard_D3_v2」へ変更しました。

```（抜粋）aks.yaml
      agent_pool_profiles:
        - name: agentpool1
          mode: System
          count: 1
          vm_size: Standard_D3_v2
```

```bash
$ ansible-playbook aks.yaml
〜略〜
TASK [Create AKS Cluster] **********************************************************************************************************************************************************
[WARNING]: Azure API profile latest does not define an entry for ContainerServiceClient
fatal: [localhost]: FAILED! => {"changed": false, "msg": "Error creating the AKS instance: Operation failed with status: 'Bad Request'. Details: Changing property 'agentPoolProfile.vmSize' is not allowed."}

PLAY RECAP *************************************************************************************************************************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0
```
これはエラーになってしまいました。既存のノードプールのVMサイズを直接変更することはできないようです。

では、新規サイズのノードプールを一旦追加、追加が完了したら旧ノードプールを削除、という形で実行してみます。
元のagentpool1のサイズは「Standard_D2_v2」のまま、新たにagentpool2を追加しました。

```（抜粋）aks.yaml
      agent_pool_profiles:
        - name: agentpool1
          mode: System
          count: 1
          vm_size: Standard_D2_v2
        - name: agentpool2
          mode: System
          count: 1
          vm_size: Standard_D3_v2
```

```bash
$ ansible-playbook aks.yaml
〜略〜
PLAY RECAP *************************************************************************************************************************************************************************
localhost                  : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
```
agentpool2が追加されました。
![スクリーンショット 2021-10-31 15.34.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274613/3ecb0f4e-8058-4ef3-e292-eede6a77eb80.png)

Playbookからagentpool1の記述を削除し再度実行。

```（抜粋）aks.yaml
      agent_pool_profiles:
        - name: agentpool2
          mode: System
          count: 1
          vm_size: Standard_D3_v2
```

```bash
$ ansible-playbook aks.yaml
〜略〜
PLAY RECAP *************************************************************************************************************************************************************************
localhost                  : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
```
「Standard_D3_v2」のagentpool2だけが残る形となり、スケールアップの目的は果たせました。
![スクリーンショット 2021-10-31 15.37.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274613/eb96ba7d-b79e-b5ee-f454-9867e61baeb0.png)

# AKSクラスターのバージョンアップ
最初のPlaybookでKubernetesのバージョンを1.20.7で作成しましたが、これのバージョンアップを行います。

```（抜粋）aks.yaml
    azure.azcollection.azure_rm_aks:
      name: akstest
      kubernetes_version: 1.20.9
      location: japaneast
      resource_group: aks_rg
      dns_prefix: akstest
```
kubernetes_versionの値を書き換えてansible-playbookを実行します。

```bash
$ ansible-playbook aks.yaml
〜略〜
PLAY RECAP *************************************************************************************************************************************************************************
localhost                  : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
```
以下のようにAPIサーバー・ノードプールともに1.20.9になりました。
![スクリーンショット 2021-10-31 15.51.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274613/9f5a89fa-361b-f064-2eca-165a98f7f440.png)

また、Ansibleのモジュールには[azure_rm_aksupgrade_info](https://docs.ansible.com/ansible/latest/collections/azure/azcollection/azure_rm_aksupgrade_info_module.html)という、指定したAKSクラスターのバージョンアップ可能な対象バージョン一覧を取得するモジュールがあります。

以下のようなPlaybookを作成し実行します。

```aksupgrade_info.yaml
- name: Get available upgrade version for AKS instance
  hosts: localhost
  connection: local
  tasks:
  - name: Get available upgrade version
    azure.azcollection.azure_rm_aksupgrade_info:
      name: akstest
      resource_group: aks_rg
    register: upgradable_version
  - name: Show Versions
    debug:
      msg: "{{ upgradable_version }}"
```

```bash
$ ansible-playbook aksupgrade_info.yaml
〜略〜
TASK [Get available upgrade version] ***********************************************************************************************************************************************
[WARNING]: Azure API profile latest does not define an entry for ContainerServiceClient
ok: [localhost]

TASK [Show Versions] ***************************************************************************************************************************************************************
ok: [localhost] => {
    "msg": {
        "azure_aks_upgrades": {
            "agent_pool_profiles": null,
            "control_plane_profile": {
                "kubernetes_version": "1.20.9",
                "name": null,
                "os_type": "Linux",
                "upgrades": [
                    {
                        "is_preview": null,
                        "kubernetes_version": "1.21.1"
                    },
                    {
                        "is_preview": null,
                        "kubernetes_version": "1.21.2"
                    }
                ]
            }
        },
        "changed": false,
        "failed": false,
        "warnings": [
            "Azure API profile latest does not define an entry for ContainerServiceClient"
        ]
    }
}

PLAY RECAP *************************************************************************************************************************************************************************
localhost                  : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
```

1.20.9からのバージョンアップ可能バージョンは、1.21.1と1.21.2の2つであると確認できます。
これを組み合わせ、以下のように「指定できる最大のバージョンにアップグレード」というPlaybookを書くこともできます。

```aks_upgrade.yaml
- name: Upgrade version for AKS instance
  hosts: localhost
  connection: local
  tasks:
  - name: Get available upgrade version
    azure.azcollection.azure_rm_aksupgrade_info:
      name: akstest
      resource_group: aks_rg
    register: upgradable_version
  - name: Upgrade AKS Cluster
    azure.azcollection.azure_rm_aks:
      name: akstest
      kubernetes_version: "{{ upgradable_version.azure_aks_upgrades.control_plane_profile.upgrades[-1].kubernetes_version }}"
      location: japaneast
      resource_group: aks_rg
      dns_prefix: akstest
      agent_pool_profiles:
        - name: agentpool2
          mode: System
          count: 1
          vm_size: Standard_D3_v2
```

# AKSクラスターの削除
以下のPlaybookでAKSクラスターを削除できます。

```aks_delete.yaml
- name: Delete AKS instance
  hosts: localhost
  connection: local
  tasks:
  - name: Delete AKS Cluster
    azure.azcollection.azure_rm_aks:
      name: akstest
      resource_group: aks_rg
      state: absent
```

```bash
$ ansible-playbook aks_delete.yaml
〜略〜
TASK [Delete AKS Cluster] **********************************************************************************************************************************************************
[WARNING]: Azure API profile latest does not define an entry for ContainerServiceClient
changed: [localhost]

PLAY RECAP *************************************************************************************************************************************************************************
localhost                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
```

#おわりに
AnsibleでもAKSに対するいろんな操作が可能でした。
ドキュメント見る限りもっと細かくAKSクラスターのオプションを指定可能なようなので、引き続き触ってみます。

#参考
- [Microsoft公式ドキュメント](https://docs.microsoft.com/ja-jp/azure/developer/ansible/aks-configure-clusters)
- [azure_rm_aksモジュールのドキュメント](https://docs.ansible.com/ansible/latest/collections/azure/azcollection/azure_rm_aks_module.html)
- [azure_rm_aksversion_infoモジュールのドキュメント](https://docs.ansible.com/ansible/latest/collections/azure/azcollection/azure_rm_aksversion_info_module.html)
- [azure_rm_aks_infoモジュールのドキュメント](https://docs.ansible.com/ansible/latest/collections/azure/azcollection/azure_rm_aks_info_module.html)
- [azure_rm_aksupgrade_infoモジュールのドキュメント](https://docs.ansible.com/ansible/latest/collections/azure/azcollection/azure_rm_aksupgrade_info_module.html)