Azure Kubernetes Service(AKS)でPodからAzureリソースにアクセスする

# はじめに

この記事ではAzure Kubernetes Service(以下、AKS)のPodからAzureリソースにアクセスする方法である、AAD Pod Identityについて解説します。

# 想定読者
- Kubernetesを利用したことがある方
- Azureが好きな方

# Azureにおける認証ツール

アプリケーションがAzureリソースへ接続する際に利用される認証ツールとして、**サービスプリンシパル**と**マネージドID**があります。

[**サービスプリンシパル**](https://docs.microsoft.com/ja-jp/azure/active-directory/develop/app-objects-and-service-principals)では、Azure Active Directory（以下、AAD)で発行する「アプリケーションID」と「アプリケーションシークレット」を利用して認証し、Azureリソースへの接続を実現するものです。AWSのIAMユーザに該当します。
この方法ではID・シークレットを利用者側で管理する必要があるため、情報流出のリスクがあり、資格情報の管理場所や運用の検討が必要となります。

このような課題に対して、Azureでは[**マネージドID**](https://docs.microsoft.com/ja-jp/azure/active-directory/managed-identities-azure-resources/overview)というツールを提供しています。マネージドIDはAzureリソースに対してのみ割り当てることができるもので、マネージドIDを利用することで、利用者は資格情報を意識（管理）することなくAzureリソースへの認証を実現できます。

この記事ではAKSでマネージドIDを利用するための方法を解説していきます。

# AAD Pod Identityとは

AAD Pod IdentityとはAKSのPodでマネージドIDを利用するためのアドオンです。（Helm等を利用してインストールする方法もありますが、この記事では割愛します）
AAD Pod Identityの概念図は次の通りです。（[こちら](https://docs.microsoft.com/ja-jp/azure/aks/developer-best-practices-pod-security)から図を引用）

![basic-pod-identity.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2155066/1f8c9525-0968-0ce4-6641-be73d6f68692.png)


AAD Pod IdentityアドオンをAKSにインストールすると、上図のPod IdentityがAKSで利用できるようになります。このPod IdentityがPodとAADを中継してアクセストークンを受け渡し、PodからAzureリソースへ接続できるようになります。

# AAD Pod Identityを利用してAzureリソースにアクセスする

ここからは[公式ドキュメント](https://docs.microsoft.com/ja-jp/azure/aks/use-azure-ad-pod-identity#run-a-sample-application)にならって、AAD Pod Identityを利用してPodからVMのメタデータを取得する手順を実行します。
なお、AAD Pod Identityアドオンは現在プレビュー中です。

## Azure Cloud Shell

この記事では、Azure Cloud Shell(Bash)からAzure CLIを利用します。
プレビュー中であるAAD Pod Identityの機能を有効化します。

```sh
az feature register --name EnablePodIdentityPreview --namespace Microsoft.ContainerService
```

コマンドを実行後、`State`が`Registered`になっていることが確認できればOKです。
つぎにAKS用の拡張コマンドである`aks-preview`をインストールします。

```sh
az extension add --name aks-preview
az extension update --name aks-preview
```

`az extension list`を実行したときに`aks-preview`のバージョンが0.5.5以降であることを確認します。

## AKSの構築
AKSをリンクを参考に構築します。

- [AKS](https://qiita.com/kk-ishii/items/e0f61e6411d9983a30b3)

  ネットワークの種類はAzure CNIを選択してください。

## AKSへの接続情報取得

以下コマンドを実行し、AKSへの接続情報を取得します。

```sh
az aks get-credentials --resource-group <リソースグループ名> --name <AKSクラスタ名>
```

`kubectl config get-contexts`を実行したときに作成したAKSクラスターが選択されていればOKです。


## AAD Pod Identityのインストール

AAD Pod Identityをインストールします。

```sh
az aks update -g <リソースグループ名> -n <AKSクラスター名> --enable-pod-identity
```

インストール後、`az aks get-credentials --resource-group aks-test --name aks-cluster`で、`NMI`のDaemonSetを確認できればインストール完了です。

ここで確認できる**Node Management Identity(NMI)** は、各ノードで実行されるポッドからのセキュリティトークンの要求を監視・取得し、ポッドに代わってAADからトークンを取得する機能を持ちます。

## マネージドIDの作成とロールの割り当て

Podに割り当てるマネージドIDを作成します。

```sh
az identity create --resource-group <リソースグループ名> --name <マネージドID名>
```

作成したマネージドIDに対して、このシナリオで必要となる`Virtual Machine Contributor`のロールを割り当てます。対象のリソースグループはAKSのノードが含まれるリソースグループとします。

```sh
# AKSノードがあるリソースグループ名取得
NODE_GROUP=$(az aks show -g <リソースグループ名> -n <AKSクラスタ名> --query nodeResourceGroup -o tsv)
# リソースグループのリソースID取得
NODES_RESOURCE_ID=$(az group show -n $NODE_GROUP -o tsv --query "id")
# マネージドIDのクライアントID取得
IDENTITY_CLIENT_ID="$(az identity show -g <リソースグループ名> -n <マネージドID名> --query clientId -otsv)"
# マネージドIDにロール割り当て
az role assignment create --role "Virtual Machine Contributor" --assignee "$IDENTITY_CLIENT_ID" --scope $NODES_RESOURCE_ID
```

マネージドIDへのロールの割り当てはAzureポータルから確認できます。
対象のリソースグループからマネージドIDを選択し、Azureロールの割り当てを選択したときに下図の通り表示されていればOKです。

![managed-id-role.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2155066/052c98ac-141f-1162-49ab-5729d0ea9b7e.jpeg)


## AzureIdentityとAzureIdentityBindingの作成

マネージドIDを利用するための[AzureIdentity](https://azure.github.io/aad-pod-identity/docs/demo/standard_walkthrough/#3-deploy-azureidentity)、および[AzureIdentityBinding](https://azure.github.io/aad-pod-identity/docs/demo/standard_walkthrough/#5-deploy-azureidentitybinding)を作成します。

**AzureIdentity**はその名の通りIDを保管するためのリソースで、マネージドIDのクライアントIDとリソースIDが記録されています。
**AzureIdentityBinding**は**AzureIdentity**とラベルを関連付けるためのリソースです。ラベルについては後述します。

以下のコマンドにより作成します。


```sh
# マネージドIDのリソースID取得
export IDENTITY_RESOURCE_ID="$(az identity show -g <リソースグループ名> -n <マネージドID名> --query id -otsv)"
az aks pod-identity add --resource-group <リソースグループ名> --cluster-name <AKS名> --namespace <AKSの名前空間>  --name <Pod Identity名> --identity-resource-id ${IDENTITY_RESOURCE_ID}
```

コマンド実行後、`kubectl get AzureIdentity,AzureIdentityBinding --namespace <名前空間>`でリソースが取得できればOKです。

## マネージドIDを利用したアプリケーションのデプロイ

マネージドIDを利用したアプリケーションをデプロイします。
以下のマニフェストを作成してください。`aadpodidbiding`ラベルには、前項で作成したPod Identity名を入力します。この`aadpodidbiding`に入力されたラベル名とマネージドIDの紐づけを`AzureIdentityBinding`のリソースで行っています。

ここで起動するアプリケーションはマネージドIDからトークンを取得し、AKSクラスターのノードに利用されるVMのメタデータを取得します。`Virtual Machine Contributor`のロールをマネージドIDに割り当てたのはそのためです。


```yaml
apiVersion: v1
kind: Pod
metadata:
  name: demo
  labels:
    aadpodidbinding: <Pod Identity名>
spec:
  containers:
  - name: demo
    image: mcr.microsoft.com/oss/azure/aad-pod-identity/demo:v1.6.3
    args:
      - --subscriptionid=<サブスクリプションID>
      - --clientid=<マネージドIDのクライアントID>
      - --resourcegroup=<リソースグループ名>
    env:
      - name: MY_POD_NAME
        valueFrom:
          fieldRef:
            fieldPath: metadata.name
      - name: MY_POD_NAMESPACE
        valueFrom:
          fieldRef:
            fieldPath: metadata.namespace
      - name: MY_POD_IP
        valueFrom:
          fieldRef:
            fieldPath: status.podIP
  nodeSelector:
    kubernetes.io/os: linux
```

作成したマニフェストをPod Identityを作成した名前空間にデプロイします。

```sh
kubectl apply -f demo.yaml --namespace <名前空間>
```

## 動作確認

コマンドを実行し、アプリケーションのログを確認します。

```sh
kubectl logs demo --follow --namespace <名前空間>
```

ログからトークンが取得されたこと、およびVMのメタデータが取得できたことが確認できます。

```sh
successfully acquired a token using the MSI, msiEndpoint(http://169.254.169.254/metadata/identity/oauth2/token)
successfully acquired a token, userAssignedID MSI, msiEndpoint(http://169.254.169.254/metadata/identity/oauth2/token) clientID(fcba39b8-ce43-4c6f-a112-8144efa93012)
successfully made GET on instance metadata, {"compute":{"location":"japaneast".....
```

# おわりに

この記事ではAAD Pod Identityを利用して、PodからマネージドIDを利用する方法を解説しました。

本文中の内容以外にも、Pod IdentityをHelmで構築する方法や、ネットワークとしてKubernetを利用する方法など、様々な方法がサポートされています。是非この記事を足がかりに理解を深めていただけると幸いです。