AKO : Avi Kubernetes Operator

## AKO (Avi Kubernetes Operator)とは

KubernetesやOpenShift環境でAvi Kubernetes Operator (AKO)を利用することにより、Kubernetes/OpenShiftのリソースをAvi Controller APIに変換し、NSX ALBによりLoadBalancerとIngressリソースを提供することが可能になります。NSX ALBが持つアプリケーショントラフィック可視化機能を利用することも可能です。

2021年2月現在、対応しているKubernetesは 1.16〜1.19、OpenShift 4.3〜4.5、CNIはAntrea, Calico, OpenShift SDNです。([Compatibility Guide for AKO](https://avinetworks.com/docs/ako/1.3/ako-compatibility-guide/))

### NSX Advanced Load Balancer (NSX ALB)について

NSX ALBは(旧Avi Vantage Platform)はロードバランサーを制御するAvi Controllerと、実際のロードバランサー機能を提供するService Engine(SE)と呼ばれるデータプレーンで構成されます。vSphereやAWS等の環境で利用可能で、性能要件に応じてSEを増やすことでLoad Balancerの性能をスケールアウトすることが可能です。また、ロードバランサー上で様々な情報を収集し、Controller上でアプリケーションの情報を可視化することが可能です。

### アーキテクチャ

Kubernetes/OpenShift環境のAKOは以下の3つのコンポーネントで構成されます。

- Avi Controller
- Service Engines (SE)
- Avi Kubernetes Operator (AKO)

![https://avinetworks.com/docs/ako/1.3/ako-design-and-deployment/img/ako1.png](https://avinetworks.com/docs/ako/1.3/ako-design-and-deployment/img/ako1.png)

### AKO Networkの構成方法

SEはKubernetesクラスター外部に仮想マシンとしてデプロイされます。SEがLoad Balancer/Ingress Controllerとして受信したリクエストはPodに負荷分散するためPod CIDRに対してSEからルーティングされる必要があります。クラスターで使用されるCNIの構成に応じて次のオプションを利用できます。

1. Podに対して外部から直接ルーティングできない場合 - Static Route
  - 一般的なCNIはクラスター内部で隔離されたCIDRを利用しPod間のネットワークを構成します。CNIはクラスター内の各NodeにPod用CIDRを割り当てます。SEをKubernetesノードと同じネットワークにデプロイする場合、AKOでStatic Routeを設定することで、SEからPod CIDR向けのStatic RouteのネクストホップとしてNodeのIPアドレスが構成されます。
2. Podに対して外部から直接ルーティングできない場合 - Node Port
    - SEがKubernetesノードと同じネットワークにデプロイされない場合、NodePortサービスをVirtualServiceのプールメンバーのエンドポイントとして使用することが可能です。このオプションではNodePortのServiceを作成し、config.serviceTypeパラメータを利用してNodePortベースのRoute/Ingressを有効にします。nodeSelectorLabels.keyとnodeSelectorLabels.valueパラメーターは、AKOのインストール中に指定し、負荷分散のためにクラスターから必要なノードを選択します。 クラスター内の必要なノードには、構成済みのキーと値のペアでラベルを付ける必要があります。
3. Podサブネットが外部から直接ルーティング可能
    - NSX-T CNI、AWS CNI、Azure CNIなどPodのサブネットが外部に対してルーティング可能な場合に利用することができます。これらのCNIを利用している場合、SEがPodのIPアドレスに到達するために特別な設定は不要です。AKOの設定で「Static Route Programming」を無効化することができます。SEはPodに対してルーティング可能なネットワークであればどこにでもデプロイすることが可能です。

## AKOを使ってみる

以下のような環境でAKOを利用してみます。上記3つのオプションの中で「Podに対して外部から直接ルーティングできない場合 - Static Route」の構成に該当します。

### ネットワーク構成

今回は、以下のような構成でKubernetes環境でAKOを利用してみます。各NodeはvSphere上の仮想マシンとして構成しており、Avi Controllerと各Nodeは同じセグメント(ポートグループ)に接続されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27668/91318717-7e88-2247-e9f0-cfea0dfca895.png)

### Kubernetesクラスター

Master x3、Node x 3のKubernetesクラスターでCNIとしはAntreaを利用しています。

```bash
$ kubectl get node -o wide
NAME          STATUS   ROLES                  AGE   VERSION   INTERNAL-IP     EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION     CONTAINER-RUNTIME
avi-master1   Ready    control-plane,master   13d   v1.20.2   172.16.250.11   <none>        Ubuntu 20.04.1 LTS   5.4.0-65-generic   containerd://1.3.3-0ubuntu2.2
avi-master2   Ready    control-plane,master   13d   v1.20.2   172.16.250.12   <none>        Ubuntu 20.04.1 LTS   5.4.0-65-generic   containerd://1.3.3-0ubuntu2.2
avi-master3   Ready    control-plane,master   13d   v1.20.2   172.16.250.13   <none>        Ubuntu 20.04.1 LTS   5.4.0-65-generic   containerd://1.3.3-0ubuntu2.2
avi-node1     Ready    <none>                 13d   v1.20.2   172.16.250.21   <none>        Ubuntu 20.04.1 LTS   5.4.0-65-generic   containerd://1.3.3-0ubuntu2.2
avi-node2     Ready    <none>                 13d   v1.20.2   172.16.250.22   <none>        Ubuntu 20.04.1 LTS   5.4.0-65-generic   containerd://1.3.3-0ubuntu2.2
avi-node3     Ready    <none>                 13d   v1.20.2   172.16.250.23   <none>        Ubuntu 20.04.1 LTS   5.4.0-65-generic   containerd://1.3.3-0ubuntu2.2
```

### AKO(Avi Kubernetes Operator)構成方法

#### Avi Controllerの準備

vSphere環境上のKubernetesクラスターでAKOを利用するにはAvi Controllerに最低限の設定を行う必要があります。

1. Controller OVAのデプロイ
2. IPAM/DNS Profileの作成
3. DNS Profileの作成
4. CloudとしてvSphere環境を登録
    1. IPAM/DNS Profileの指定
    2. Management NetworkでSEが利用するポートグループとIP Addressプールを指定
    3. NetworksとしてVIPネットワーク用のポートグループにIP Adressプールを指定

今回はAvi Controllerに2つのネットワークを構成しました。

- Management Network : SEがAvi Controllerと通信するためのネットワーク
  ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27668/bf8fb326-0af7-1450-0f7e-353ba3d63d39.png)

- VIP用ネットワーク : Service/Ingressリソースが利用するVIPを配置するネットワーク
  ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27668/947a6f8a-5723-5d91-ec03-ee5d13bf1e7e.png)


#### AKOの構成

AKOはhelmによりインストールします。

- helmレポジトリの追加

```bash
$ helm repo add ako https://avinetworks.github.io/avi-helm-charts/charts/stable/ako
"ako" has been added to your repositories

$ helm repo list
NAME    URL
ako     https://avinetworks.github.io/avi-helm-charts/charts/stable/ako
```

- AKO用ネームスペースの作成

```bash
$ kubectl create ns avi-system
namespace/avi-system created
```

- AKOをインストールする際のパラメータを設定するvalues.yamlのテンプレート作成

```bash
$  helm show values ako/ako > values.yaml
```

- values.yamlのパラメータ指定

```values.yaml``` ではAvi Controllerのアドレスやクレデンシャルの他、IngressやServiceリソース作成時に作成されるVirtual Serivce(VIP)をどのサブネット、ポートグループで利用するかを指定します。Service Engineはここで指定したポートグループに接続され、VIPが払い出されます。以下はvaleus.yamlの設定箇所の抜粋です。

```yaml
AKOSettings:
  clusterName: "avi-cluster"		# A unique identifier for the kubernetes cluster
NetworkSettings:
  subnetIP: "172.16.251.0"			# Subnet IP of the vip network
  subnetPrefix: "255.255.255.0"		# Subnet Prefix of the vip network
  networkName: "alb-vip"			# Network Name of the vip network
L7Settings:
  shardVSSize: "SMALL"				# Use this to control the layer 7 VS numbers. ENUMs: LARGE, MEDIUM, SMALL
ControllerSettings:
  controllerVersion: "20.1.3"		# The controller API version
  controllerIP: "172.16.250.1"		# Controller IP Address
avicredentials:
  username: "<USER>"				# Controller Username
  password: "<PASSWORD>"			# Controller Password
```

- helm chartのインストール

```bash
helm install ako/ako --generate-name --version 1.3.1 -f values.yaml -n avi-system
NAME: ako-1612391086
LAST DEPLOYED: Wed Feb  3 22:24:47 2021
NAMESPACE: avi-system
STATUS: deployed
REVISION: 1
```

- 作成の確認 : avi-systemネームスペースにako-0 podが作成されます。

```bash
$ helm list -n avi-system
NAME            NAMESPACE       REVISION        UPDATED                                 STATUS          CHART           APP VERSION
ako-1612391086  avi-system      1               2021-02-03 22:24:47.131176283 +0000 UTC deployed        ako-1.3.1       1.3.1

$ kubectl get pod -n avi-system
NAME    READY   STATUS    RESTARTS   AGE
ako-0   1/1     Running   0          3m6s
```
- Static Routeの確認 : SEはPod向けのLoadBalancerとして機能するため、各ノードCIDRに対するStatic Routeが構成されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27668/6a1223f4-920c-7870-0606-1dd967c4198a.png)

### Service type=LoadBalancerの作成

nginx podを作成してService type=LoadBalancerを指定してPodを公開してみます。以下の例ではAvi Controllerで指定したalb-vipのネットワークプール(172.16.251.1-172.16.251.100)から```172.16.251.1```がLoadBalancerのアドレスとして払い出されました。

```bash
$ kubectl create deploy nginx1 --image=nginxhellos/demo
deployment.apps/nginx1 created

$ kubectl get pods
NAME                      READY   STATUS    RESTARTS   AGE     IP            NODE        NOMINATED NODE   READINESS GATES
nginx1-6754b74cdd-9csrs   1/1     Running   0          4m45s   10.244.3.23   avi-node1   <none>           <none>

$ kubectl expose deploy nginx1 --type=LoadBalancer --port=80
service/nginx1 exposed

$ kubectl get service
NAME         TYPE           CLUSTER-IP      EXTERNAL-IP    PORT(S)        AGE
kubernetes   ClusterIP      10.96.0.1       <none>         443/TCP        4d23h
nginx1       LoadBalancer   10.103.97.178   172.16.251.1   80:30664/TCP   5m27s
```
最初にVirtual Serviceを必要とするリソース(Service/Ingress)が作成されると、akoによりSEのプロビジョニングが開始されます。SEをOVAからデプロイするため、Serviceにアクセスできるようになるまでしばらく時間がかかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27668/af56eb3f-048e-c1b3-eb6a-7f787fcbaca8.png)

デプロイされたSEは、管理用IPとしてAvi Controllerで設定したManagement Network(```alb-mgmt```)に接続され、values.yamlで指定したnetworkNameポートグループ(```alb-vip```)に接続してVIPが構成されます。今回の例ではKubernetes環境では、SEの管理用ネットワークとKubernetesノード通信用ネットワークとして```alb-mgmt```が利用されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27668/dbb35a36-70e9-d76b-739a-969c23aa86f5.png)

Avi Controllerを確認すると、Virtual Serviceとして「k8s--default-nginx1」(クラスター名--ネームスペース名-サービス名という命名規則)が作成され、しばらくするとLoadBalancerが処理した各種メトリックが表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27668/c5b1e335-2688-6241-c390-64f56ce35e79.png)

Virtual Serviceに関連付けられているPoolのServerにはPodのIPアドレスが構成されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27668/041cd3c6-29c3-9576-c221-fd0ec8bdb14b.png)

Pod数を1→5に変更してみます。

```bash
$ kubectl scale deploy nginx1 --replicas=5
deployment.apps/nginx1 scaled

$ kubectl get pods -o wide
NAME                      READY   STATUS    RESTARTS   AGE     IP            NODE        NOMINATED NODE   READINESS GATES
nginx1-6754b74cdd-2t8zb   1/1     Running   0          2s      10.244.5.19   avi-node3   <none>           <none>
nginx1-6754b74cdd-9csrs   1/1     Running   0          9m17s   10.244.3.23   avi-node1   <none>           <none>
nginx1-6754b74cdd-fh72t   1/1     Running   0          2s      10.244.5.18   avi-node3   <none>           <none>
nginx1-6754b74cdd-qkpnt   1/1     Running   0          2s      10.244.4.10   avi-node2   <none>           <none>
nginx1-6754b74cdd-t464l   1/1     Running   0          2s      10.244.3.24   avi-node1   <none>           <none>
```

PoolのServerもPodの増加に追従して負荷分散対象を修正しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27668/44a90fe7-166d-4a66-4135-8e2217f1c1d3.png)

### Ingressとしての利用

AKOはIngressリソースにも対応しています。Ingress利用時はDNS Virtual Serviceを併用することでIngressリソース向けの名前解決を提供することも可能です。

#### DNS Virtual Serviceの作成と設定

> 本環境のDNSサーバーをexample.comゾーンの権威サーバーとして構成し、サブドメイン```ako.example.com```の委譲先としてDNS Virtual Serviceで指定するIPアドレス(172.16.251.253)を指定しています。

Virtual Service作成画面で、Application Profileとして「System-DNS」を選択してDNS Virtual Serviceを作成します。VIPとして手動で172.16.251.253を割り当てています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27668/3b714a7e-1499-714b-4cbf-a36d59070223.png)

Administration > Settings > DNS Serviceを開き、作成したDNS Virtual Serviceを選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27668/26f26ba2-8022-3fd2-a9fb-5f0a35da6acb.png)

Infrastructure > Cloudで、「State Based DNS Registration」が有効になっていることを確認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27668/8628457d-ac04-9724-45f3-98c6d5004582.png)

#### Ingressリソースの作成

Ingressリソースを作成します。

```bash
$ cat << EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx1-ingress
spec:
  rules:
  - host: nginx1.ako.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: nginx1
            port:
              number: 80
EOF
```

Ingressリソースを確認するとAvi Controllerで指定したalb-vipのネットワークプール(172.16.251.1-172.16.251.100)から172.16.251.4がIngressリソースとして払い出されています。

```bash
# kubectl get ingress
NAME             CLASS    HOSTS                    ADDRESS        PORTS   AGE
nginx1-ingress   avi-lb   nginx1.ako.example.com   172.16.251.4   80      3s
```

Ingressとして作成されるVirtual Serviceは複数のIngressリソースで共有されるため、Virtual Serviceの「Application Domain Name」には複数のIngressリソースのFQDNが構成されます。


```bash
# kubectl get ingress
NAME             CLASS    HOSTS                    ADDRESS        PORTS   AGE
httpd1-ingress   avi-lb   httpd1.ako.example.com   172.16.251.4   80      4s
nginx1-ingress   avi-lb   nginx1.ako.example.com   172.16.251.4   80      14m
```


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27668/5172d837-cde4-66f0-1a80-2c945bac3902.png)

nginx1.ako.example.comやhttpd1.ako.example.comのAレコードはDNS Virtual Serviceに登録されるため、FQDNでアクセスすることが可能です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27668/15af6992-b85d-a9bb-9ac9-6d5926a52ce3.png)


#### アプリケーショントラフィックの可視化

Ingressで作成したVirtual ServiceはL7ロードバランサーとして構成される(Application Profile = System-HTTP)ためアプリケーションレベルの可視化が可能です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27668/83fd353a-8dc2-7e2b-cc67-3612241df0d6.png)

## まとめ

Kubernetes環境でNSX-ALBを利用してServiceやIngressリソースを提供することが可能です。NSX ALBを利用することで、ControllerのGUIからトラフィックを詳細に可視化することが可能です。
[VMware Tanzu](https://tanzu.vmware.com/tanzu)のStandard Edition以上にはNSX Advanced LoadBalancerがバンドルされるため、Tanzuの中でLoadBalancerを利用するための重要なコンポーネントになっていくと思われます。