AKS で asp.net core アプリケーション : Azure CNI ネットワーク と Application Gateway

[前回の記事](https://qiita.com/kenakamu/items/aee6748b6d15b8aab34f)では複数ノードプールを使う方法を見ていきましたが、この環境では HTTP アプリケーションのルーティングアドオンが使えなかったため、現在イングレスコントローラーがありません。今回の記事ではイングレスコントローラーを考察していきます。

# Web アプリケーション ファイアウォール (WAF)

Azure で WAF といえば Azure Application Gateway です。今回は以下の構成を目指して構築していきます。
![](https://docs.microsoft.com/ja-jp/azure/aks/media/operator-best-practices-network/web-application-firewall-app-gateway.png)

[情報元: Web アプリケーション ファイアウォール (WAF) を使用してトラフィックをセキュリティで保護する](https://docs.microsoft.com/ja-jp/azure/aks/operator-best-practices-network#secure-traffic-with-a-web-application-firewall-waf)

# Azure CNI ネットワーク 

これまでは既定の Kubenet ネットワークを使ってきましたが、[ベストプラクティスガイダンス](https://docs.microsoft.com/ja-jp/azure/aks/operator-best-practices-network#choose-the-appropriate-network-model)には、Kubenet のいくつかの欠点と、運用環境は Azure CNI を使うべきというガイダンスが提示されています。また今回利用する Application Gateway は Azure CNI が前提となっています。

Azure CNI を使うと、以下のことができます。

- Azure 仮想ネットワークからノードとポッドに IP を割り当てる
- 全て Azure 仮想ネットワークに存在するため、他の Azure やオンプレミスリソースにルーティングできる
![](https://docs.microsoft.com/ja-jp/azure/aks/media/operator-best-practices-network/advanced-networking-diagram.png)

確かに kubenet ではポッドにはクラスタ内で管理される IP 範囲からアドレスを受け取っており、ノードとの IP 範囲は異なっていました。AKS のネットワークの概念については [Azure Kubernetes Service (AKS) でのアプリケーションに対するネットワークの概念](https://docs.microsoft.com/ja-jp/azure/aks/concepts-network) により詳細があります。

## 仮想ネットワークのサイズ

Azure CNI を使うとすべての IP アドレスが仮想ネットワークから割り当てられるため、十分な数の IP を提供できるサブネットが必要となります。計算方法も含めて詳細は [クラスターの IP アドレス指定を計画する](https://docs.microsoft.com/ja-jp/azure/aks/configure-azure-cni?view=azure-cli-latest#plan-ip-addressing-for-your-cluster) を参照してください。

# Azure CNI を利用したクラスタの作成

前回複数ノードプールの作成で作り直したクラスタですが、ネットワーク環境を Azure CNI にするため再度作り直します。またいくつかの前提作業が必要となります。

## 前提

1\. 既存の myaks を削除。ACR はそのまま使うため残しておく。

2\. AKS クラスタを構築するための仮想ネットワークを作成。サイズについては必要に応じて変更。

```shell
az network vnet create -g netcoresample -n netcoresampleaksvnet --address-prefix 10.0.0.0/16 --subnet-name akssubnet --subnet-prefix 10.0.0.0/24
```
3\. サービスプリンシパルの作成。

```shell
az ad sp create-for-rbac --skip-assignment
```
4\. 既存の ACR のリソース名を取得。

```shell
az acr show --resource-group netcoresample --name kenakamuacr --query "id" --output tsv  
```

5\. 作成したサービスプリンシパルに ACR のプル権限を付与。

```shell
az role assignment create --assignee <appId> --scope <acrId> --role acrpull
```

6\. 仮想ネットワークサブネットのリソース名を取得。

```shell
az network vnet subnet list -g netcoresample --vnet-name netcoresampleaksvnet --query "[0].id" --output tsv
```

7\. 作成したサービスプリンシパルに、仮想ネットワークサブネットのネットワーク共同作成者権限を付与。

```shell
az role assignment create --assignee <appId> --scope <subnet-id> --role "network contributor"
```

## クラスタの作成

1\. az aks create コマンドでクラスタを作成。appId、password はサービスプリンシパル作成時に取得された値を利用。Azure CNI の利用と複数ノードプールを使う。各種パラメーターの詳細は[展開のパラメーター](https://docs.microsoft.com/ja-jp/azure/aks/configure-azure-cni?view=azure-cli-latest#deployment-parameters)参照。

```shell
az aks create -g netcoresample \
-n myaks -c 2 \
--service-principal <appId> \
--client-secret <password> \
--vm-set-type VirtualMachineScaleSets \
--load-balancer-sku standard \
--network-plugin azure \
--vnet-subnet-id <subnet-id> \
--docker-bridge-address 172.17.0.1/16 \
--dns-service-ip 10.1.0.10 \
--service-cidr 10.1.0.0/24 \
--generate-ssh-keys
```

2\. 作成完了後、ポータルからネットワーク設定を確認。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/3a0f7f8f-0c14-962a-681b-902e0b417d2b.png)

3\. Azure CNI にクラスタ構築が完了後、前回同様に新しいノードプールを追加。

```shell
az aks nodepool add -g netcoresample --cluster-name myaks --name nodepool2 -c 1 --node-vm-size Standard_DS1_v2
```

4\. AKS に接続。

```shell
az aks get-credentials -g netcoresample -n myaks
```

4\. ノード一覧を取得。名前と同時に IP も確認。

```shell
>kubectl get nodes -o wide
NAME                                STATUS   ROLES   AGE     VERSION    INTERNAL-IP   EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION      CONTAINER-RUNTIME
aks-nodepool1-23705949-vmss000000   Ready    agent   16m     v1.13.11   10.0.0.4      <none>        Ubuntu 16.04.6 LTS   4.15.0-1059-azure   docker://3.0.6
aks-nodepool1-23705949-vmss000001   Ready    agent   16m     v1.13.11   10.0.0.35     <none>        Ubuntu 16.04.6 LTS   4.15.0-1059-azure   docker://3.0.6
aks-nodepool2-23705949-vmss000000   Ready    agent   9m34s   v1.13.11   10.0.0.66     <none>        Ubuntu 16.04.6 LTS   4.15.0-1059-azure   docker://3.0.6
```

5\. SQL 配置用ノードに kind=sql:NoSchedule でテイント。

```shell
kubectl taint node aks-nodepool2-23705949-vmss000000 kind=sql:NoSchedule
```

6\. 同様にアプリ用ノードもテイント実施。

```shell
kubectl taint node aks-nodepool1-23705949-vmss000000 kind=app:NoSchedule
kubectl taint node aks-nodepool1-23705949-vmss000001 kind=app:NoSchedule
```

# アプリのデプロイ

クラスタの作成が完了したので、アプリをデプロイします。

1\. apply コマンドでデプロイ実行。

```shell
kubectl apply -f myapp.yaml
```
2\. ポッドの IP を確認。ノードと同じ範囲がアサインされている。また意図したノードに配置されていることも確認。

```shell
>kubectl get pods -o wide
NAME                                READY   STATUS    RESTARTS   AGE     IP          NODE                                NOMINATED NODE   READINESS GATES
core3webapp-67b678b946-cthmp        1/1     Running   4          2m57s   10.0.0.15   aks-nodepool1-23705949-vmss000000   <none>           <none>
core3webapp-67b678b946-rd6nc        1/1     Running   4          2m57s   10.0.0.61   aks-nodepool1-23705949-vmss000001   <none>           <none>
mssql-deployment-6597f9f5b6-z8g9j   1/1     Running   0          2m58s   10.0.0.73   aks-nodepool2-23705949-vmss000000   <none>           <none>
```

3\. サービスを取得してブラウザでアクセス。動作を確認。

```shell
>kubectl get svc
NAME               TYPE           CLUSTER-IP   EXTERNAL-IP     PORT(S)        AGE
core3webapp        LoadBalancer   10.1.0.149   20.44.134.195   80:30889/TCP   3m28s
kubernetes         ClusterIP      10.1.0.1     <none>          443/TCP        22m
mssql-deployment   ClusterIP      10.1.0.148   <none>          1433/TCP       3m29s
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/a018c220-22a9-fdbe-5d77-d76ce0b525a0.png)

# Application Gateway

次に Application Gateway と AKS の構成を見ていきます。現在 Application Gateway と AKS をつなぐ方法は 2 つあり [Application Gateway Kubernetes Ingress](https://github.com/Azure/application-gateway-kubernetes-ingress) を使う方法が最新です。今回は何が変わったのかを理解するためにあえて古い方法でまず構成して、次回新しい方法を試してみます。

## Application Gateway のインストール

1\. Application Gateway をインストールするサブネットを、仮想ネットワークに作成。

```shell
az network vnet subnet create -g netcoresample --vnet-name netcoresampleaksvnet --name appgatewaysubnet --address-prefixes 10.0.1.0/24
```
2\. パブリック IP 作成。

```shell
az network public-ip create -g netcoresample -n netcoresampleagPublicIP --allocation-method Static --sku Standard
```

3\. Application Gateway の作成。15-30 分程度かかるため、コーヒーでも飲んで待機。

```shell
az network application-gateway create \
-g netcoresample \
-n netcoresampleag \
--capacity 1 \
--sku Standard_v2 \
--vnet-name netcoresampleaksvnet \
--subnet appgatewaysubnet \
--public-ip-address netcoresampleagPublicIP\
 --location japaneast
```
4\. 作成が完了したら Azure ポータルでも確認。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/691a9055-1eed-f721-0345-3143842379be.png)

## AKS の構成と Application Gateway の構成

現在は AKS のサービスが外部から直接アクセスできるため、Application Gateway からのみアクセスできるよう変更します。

1\. myapp.yaml の最後にあるサービス定義を変更し、インターナルロードバランサーを構成。

```yaml:myapp.yaml
<省略>
---
apiVersion: v1
kind: Service
metadata:
  name: core3webapp
  labels:
    app: core3webapp
  annotations:
    service.beta.kubernetes.io/azure-load-balancer-internal: "true"
spec:
  type: "LoadBalancer"
  ports:
  - protocol: TCP
    port: 80
  selector:
    app: core3webapp
```
2\. 変更をデプロイ。

```shell
kubectl apply -f myapp.yml
```

3\. サービスより EXTERNAL-IP が仮想ネットワークのアドレスであることを確認。

```shell
>kubectl get svc
NAME               TYPE           CLUSTER-IP   EXTERNAL-IP   PORT(S)        AGE
core3webapp        LoadBalancer   10.1.0.149   10.0.0.97     80:30889/TCP   8h
kubernetes         ClusterIP      10.1.0.1     <none>        443/TCP        8h
mssql-deployment   ClusterIP      10.1.0.148   <none>        1433/TCP       8h
```

4\. Application Gateway を Azure ポータルから開き、バックエンドプールを選択。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/a581c91d-0072-b26f-d02d-71d74fce0a02.png)

5\. 既存の appGatewayBackendPool を選択し、ターゲットに EXTERNAL-IP の値を設定。「保存」をクリック。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/03858247-825b-661d-d2de-969f35dc42ad.png)

6\. フロントエンド IP 構成よりパブリック IP を確認。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/0d8cd426-796a-148d-7fff-e8a5b4dee123.png)

7\. ブラウザより動作を確認。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/abc8ad7c-7d67-14a5-b9fa-30bfe9927222.png)

## まとめ

初めに描いた構成にはなりましたが、この構成の課題は Application Gateway が AKS 側の情報を把握できない事です。正常性プローブを使って定期的にアプリの死活監視はできますが、AKS 側から変更を動的に更新することはできません。
これを実現する方法として [Application Gateway Kubernetes Ingress](https://github.com/Azure/application-gateway-kubernetes-ingress) があるため、次回はこちらを構成してみます。

[次の記事へ](https://qiita.com/kenakamu/items/6997c894e6cce58f2ea2)
[目次へ戻る](https://qiita.com/kenakamu/items/e155740aa60c85a3d867)

# 参照

[Application Gateway](https://docs.microsoft.com/ja-jp/azure/application-gateway/overview)
[Azure Kubernetes サービス (AKS) で Azure CNI ネットワークを構成する](https://docs.microsoft.com/ja-jp/azure/aks/configure-azure-cni?view=azure-cli-latest)
[Azure Kubernetes Service (AKS) でのアプリケーションに対するネットワークの概念](https://docs.microsoft.com/ja-jp/azure/aks/concepts-network)
[Azure Kubernetes Service (AKS) で内部ロード バランサーを使用する](https://docs.microsoft.com/ja-jp/azure/aks/internal-lb)
[Application Gateway Kubernetes Ingress](https://github.com/Azure/application-gateway-kubernetes-ingress)