Kubernetes に対応した Cisco ACI CNI を用いた通信制御と External IP に対するロードバランス処理の仕組み

# はじめに
本エントリーは Cisco Systems 合同会社 社員有志 による Advent Calendar 2019 の 12/12(木) 分です。
2019年の他のエントリーについては以下を参照下さい。
https://qiita.com/advent-calendar/2019/cisco

同時に、記載の情報は2019/12時点の情報に基づきます。

# Cisco ACI とは
Cisco Application Centric Infrastructure (以下、ACI)そのものについて説明していると終わらないので、ACIについては、以下を参照してください。
- [ACI How To (日本語で作成されたACI関連 各種参考情報のまとめ)](https://learningnetwork.cisco.com/docs/DOC-25952)
- [ACI Official Guide (Documents, Release Notes, Scalability, etc)](https://www.cisco.com/c/en/us/support/cloud-systems-management/application-policy-infrastructure-controller-apic/tsd-products-support-series-home.html)

以下では、ACIの基本的な仕組みと用語をご理解頂けていることが前提で記載させて頂いています。

###ACI Endpoint
ACIを活用する上で、個別のコンピューティングリソースを把握できることはとても重要です。それが物理サーバであろうと、仮想マシンであろうと、そしてコンテナであろうと、それらを個別のEndpointとして識別と区分さえできれば、ACIは適切な制御を行うことができます。ACI CNIを用いた連携によって、ACIはKubernetes側で管理されている個別のPodを個別のEndpointとして認識することができるようになります。

> ACIに接続したコンピューティングリソース側でACIとは連携しないホストベースのオーバーレイソリューションを利用した場合、ACIからはホスト間の通信としてしか認識ができなくなります。この場合、ACI側の機能としてEndpointの識別単位はホストとなってしまい、その上に存在し通信を行っているであろうVM等は識別できなくなってしまいます。

以下はACI CNIによってEndpointとして認識されているKubernetes Podの状態を確認しているスクリーンショットです。EPGのに紐付いたPod名とそのMACアドレス及びIPアドレス、動作しているWorker Node名、管理連携のためのVMM Domain名、通信経路として利用しているInterface、そしてEncap VXLAN等の情報が一覧で表示されています。
![ACI CNIによって認識されたEndpointとしてのPodのネットワーク接続状態](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216144/f41cd799-e36b-c692-8405-cd347743a070.png)

###ACI連携するKubernetesノードの形態
ACIはあらゆるコンピューティングリソースに対してネットワークを提供しますが、ACI CNIを用いた連携を利用する場合であっても、Kubernetesノードは物理サーバを用いたベアメタル構成と仮想マシンを用いた構成の両方に対応します。物理サーバの場合はACI Leaf配下に接続したKubernetesノードを、仮想マシンの場合はvSphere環境の分散仮想スイッチに接続した仮想マシンとしてのKubernetesノードを対象に連携を構成することが可能です。仮想マシンを使ってKubernetesノードを構成する場合には、VM側でTag VLANを利用するためTrunk構成のポートグループが必要となりますが、その構成もACI連携の中で自動的に作成されます。

- **物理サーバの場合の接続構成例**
 ![物理サーバをKubernetesノードとして利用した場合の構成例](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216144/6efc7d76-3d93-3bd7-c1d0-d28f4d11d202.png)

- **仮想マシンの場合の接続構成例**
 ![仮想マシンをKubernetesノードとして利用した場合の構成例](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216144/0d8b7c3a-c0fa-b9b6-24bc-c0355fc4c479.png)

※いずれも[Enable Infrastructure VLAN]のチェックが必要となる理由は、Kubernetesノードとの間でVXLAN通信を行う際に利用されるVLANがInfra VLANであるためです。

# ACIとKubernetesの連携動作概要
ACIとKubernetesの連携を理解するためには、「管理連携」と「通信制御」の2つの側面に分けてご理解頂く必要があります。

###管理連携
管理連携はACIのコントローラであるAPIC (Application Policy Infrastructure Controller)とKubernetes Master Nodeとの間でのAPI通信に基づく連携動作です。ACI-Kubernetes連携は、基本的にはKubernetesの裏側の仕組みとしてCNIを通じて「KubernetesからACIが制御される」方向での連携となりますが、APICもKubernetesのMaster Nodeから得た情報を使って、ACI側として個々のPodを識別して状態を把握することが可能となります。

![ACI CNIを用いてVMM Domain連携した場合のACI側からのKubernetesの認識例](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216144/ec9527ba-487d-c2c1-2aec-8966c4ceeef5.png)

###通信制御
一方、実際のWorker Nodeとの間では各ノード側で動作するOpen vSwitch (OVS)を制御すると共にVXLAN通信の紐付けを管理するためにOpFlex AgentがKubernetesの全ノードで実行されます。OpFlex AgentはOVSを内部的にはOpenFlowを用いて制御し、同時に「どのWorker NodeでどのPodが動作しているのか」等の情報をOpFlex Proxyとして動作するACI側のLeafスイッチを経由してAPICと連携します。

![ACI CNIによる管理連携とOpFlexによる通信連携の概略図](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216144/500a8691-424b-17b4-46d6-bb6e3ebe41bd.png)

利用するOVSは標準で提供されているものをそのままご利用頂くことができます(特別な改変やカスタムバージョンの利用は必要ありません)。OpFlex Agentを通じてACI側のEPGを反映したVXLANのIDであるVNIDをPod通信に反映させることによって、ACIはPodからの通信を識別してEPGとして紐付けることが可能となります。

# ACI CNI を利用頂くことのメリット
Kubernetes用のCNIといえばFlannelやCalico等が有名ですが、シスコがデータセンターネットワーク向けに提供しているVXLAN FabricとSDN/Policyの統合・融合技術である Cisco ACI もKubernetes CertifiedなCNIを提供しています。

https://kubernetes.io/docs/concepts/cluster-administration/networking/#aci

サポートされるKubernetes及び各種コンテナベースのソリューション(OpenShift, Docker EE等)については、以下のURLにある"ACI Virtualization Compatibility Matrix"を参照して下さい。2019/12/12時点ではKubernetesは1.16まで、OpenShiftは3.11まで、そしてDocker EE 2.1 (UCP 3.1)等がサポートされています。

https://www.cisco.com/c/dam/en/us/td/docs/Website/datacenter/aci/virtualization/matrix/virtmatrix.html

###特別な仕組みを使わない、という価値
ACIをKubernetes用のCNIとして用いる最大のメリットは、逆説的ですがコンテナネットワークに対して「特別な仕組み」を使わない、という点にあります。ACIはNexus 9000シリーズスイッチによって構成されたVXLAN FabricをAPICによって統合管理するソリューションですので、あらゆるコンピューティングリソースに対して共通の仕組みを使ったポリシー制御を実現することができます。対象が物理サーバであっても仮想マシンであっても、そしてコンテナであっても、「特定の対象だけに最適化された」仕組みを使うのではなく、あらゆる対象に対して共通のルールとしてポリシーを適用することができるため、様々なコンピューティングリソースを組み合わせて、もしくは可能性として将来において利用する場合であっても管理が煩雑化せずに環境をシンプルに保つことが可能となります。

###ACI CNIとしてKubernetesに対応することの意味
また、CNIを用いた標準仕様に準拠することによって、KubernetesやOpenShiftなどをACIと共にご利用頂く際に一切の機能を制限をしないというメリットがあります。Kubernetes側を活用したマイクロサービスなアプリケーションを開発する管理者にとっては、ACIの存在を意識する必要性はありません。同時にインフラ管理者にとってはACI CNIを用いることによって、物理ネットワークによる通信制御の信頼性や安定性、性能をコンテナに対しても提供することが可能となりますし、APICがKubernetesのマスタノードとAPIで連携することによって、ACI側としても通信の対象を単なるMACアドレスやIPアドレスとしてだけでなく、Pod / Deployment / Service / Namespace 等といった Kubernetes における論理的な管理区分に基づいて把握することができるためネットワーク管理側からのコンテナに対する可視性が大幅に向上します。

# ACI CNIを用いた通信制御
###コンテナ管理者とネットワーク管理者
コンテナの管理はアプリケーション側の開発者で、ネットワークの管理はネットワーク側のインフラ担当者、というような場合、Kubernetesにおけるネットワークの境界はなかなか曖昧です。Clusterごと分離したり、Namespaceより細かい範囲ではセキュリティは考えない、というような割り切りができるならばいいのかもしれませんが、インフラ管理者側から会社のポリシーを反映させたセキュリティを適用しようとした場合に、物理サーバや仮想マシンであればスイッチにおけるACLや境界FWなどで対応することができましたが、コンテナに対しては従来とは全く異なる仕組みで対処しなければならなくなるとすると、管理者的にはツラい状況です。ACI CNIを利用すると、物理サーバや仮想マシンに対して利用するACIのセキュリティ管理の仕組みであるEPGとContractをコンテナに対しても適用することが可能になるので、共通の管理性、ポリシーの適用対象としてKubernetes環境を扱うことができるようになります。

![開発者によるKubernetesの管理とネットワーク管理者によるセキュリティの適用の共存](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216144/077cb81d-e7d0-098d-d93e-f1c4c44840f7.png)

###ネットワークとしてのセキュリティや通信可能範囲の制御

ACI CNIを用いた連携においては、Cluster / Namespace / Deployment など柔軟な単位に対してEPGを紐付けることでネットワーク側としてのポリシーを構成することができます。KubernetesにおいてはPodの配置や数は動的に変化する対象ですが、ACI CNIを使った連携を構成していれば、そうしたコンテナ側の動的な変化に対しても自動的に対応することができる物理レベルでのセキュリティや通信可能な範囲を制御することが可能となります。

![ACIにおけるEPGをCluster/Namespace/Deploymentなどの単位に紐付けることが可能](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216144/5571cb2d-f8ac-37ce-ce05-9bedafb6a622.png)

ACIのEPGに対してKubernetesのNamespaceやDeploymentを紐付けるには、Kubernetesが持つAnnotationの仕組みを活用しています。`kubectl annotate`コマンドでの構成も可能ですが、ACIをKubernetesと連携して利用する操作を簡易化する`acikubectl`コマンドがACIのContainer Pluginの一部として提供されますので、そちらを利用したほうがシンプルな構文で構成頂くことが可能です。

下記は **Namespace "gb"** を ACIの **Tenant "k8s_demo2"** の **Application Profile "kubernetes"** 配下の **EPG "gb"** に紐付けるAnnotationルールを`acikubectl`コマンドを用いて構成する場合の例となります。Annotationsの行が、acikubectlコマンドによって構成されたEPG紐付けのための設定値となります。

```shell
root@k8sv1:~# acikubectl set default-eg namespace gb -t k8s_demo2 -a kubernetes -g gb
Setting default endpoint group:
Endpoint Group:
  Tenant: k8s_demo2
  App profile: kubernetes
  Endpoint group: gb
root@k8sv1:~#
root@k8sv1:~# kubectl describe namespace gb
Name:         gb
Labels:       <none>
Annotations:  opflex.cisco.com/endpoint-group: {"tenant":"k8s_demo2","app-profile":"kubernetes","name":"gb"}
Status:       Active

No resource quota.

No resource limits.
root@k8sv1:~#
```

上記を実行した結果、下記のようにNamespace "gb"に属するPodは全てACIにおいてEPG "gb"に紐付けられます。また、Namespace単位でEPGへの紐付けを定義していますので、この構成以降に当該Namespaceに展開された新しいPodや、deploymentとしてレプリカ数の増減などを行っても、その結果作成されたPodも自動的にEPGに紐付けられるため、同じセキュリティポリシーが適用されます。

![OpFlex連携によって通信に利用されるVXLAN IDが変更されることによってEPGへの紐付けを動的に制御することが可能](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216144/00b0680b-8287-5883-6d46-207ef7075e86.png)

この仕組は、KubernetesにおけるNetwork Policyとしての通信制御を阻害することはありません。Network Policyはyaml定義ファイルの中でホストレベルで適用されるStatefullなセキュリティとして、必要に応じてACI側でのEPGのマッピングと組み合わせてご利用頂くことが可能です。ACI側でのEPGへの紐付けは、ネットワーク側としての通信範囲やセキュリティの強制とポリシーの標準化に利用頂くことが可能な仕組みです。

![KubernetesのネットワークポリシーとEPGへのマッピング機能は補完関係にある](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216144/5f839d15-ba49-dd08-1743-5f39a50827aa.png)

# ACI CNIを用いたExternal IPに対するロードバランス処理
ACI CNI Pluginを用いた環境では、External IPに対する **type: LoadBalancer** の利用、つまり外部接続IPアドレスに対するロードバランスをKubernetesにおいて構成し利用することが可能です。ACI CNIでは何らかのロードバランサー(F5等)を用いてこの機能を利用することも可能ですが、単純に複数Podに対するロードバランス処理のみで良いのであれば、ACIの機能だけで対応することが可能です。

###ACIのService Graph + PBR機能の活用
ACI CNIにおけるロードバランス処理には、ACIがL4-L7連携するために実装しているService Graphと呼ばれる仕組みを上手く活用して実現しています。Service GraphはContractに紐付けられ、当該Contractの条件を満たした通信を何らかのL4-L7デバイス経由にする仕組みですが、一般的な使用方法については ACI How To および Whitepaper 等を参照して下さい。

- [Service Graph Design with Cisco Application Centric Infrastructure White Paper](https://www.cisco.com/c/en/us/solutions/collateral/data-center-virtualization/application-centric-infrastructure/white-paper-c11-734298.html)
- [Cisco Application Centric Infrastructure Policy-Based Redirect Service Graph Design White Paper](https://www.cisco.com/c/en/us/solutions/data-center-virtualization/application-centric-infrastructure/white-paper-c11-739971.html)

ACI CNIにおいてService Graphの振り分け先として構成される対象は、Kubernetesを構成するノードそれ自身です。Service GraphにはActive/Standby構成のL4-L7デバイスはもちろん、クラスタ構成やActive/Standby構成ではない同じ役割を持つ複数台のL4-L7デバイスをまとめて登録することによって、それらに通信を振り分ける機能がありますが、Kubernetesノードに対するロードバランスでもこの機能が活用されています。

![ACI Service Graphを用いたKubernetes External IPへのロードバランス機能の提供](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216144/2ec2f21b-5086-1940-6797-5871d94af4be.png)

```Shell
root@k8sv1:~# kubectl get svc -n gb
NAME           TYPE           CLUSTER-IP       EXTERNAL-IP    PORT(S)        AGE
frontend       LoadBalancer   10.151.202.87    192.168.51.5   80:32559/TCP   16d
redis-master   ClusterIP      10.151.84.56     <none>         6379/TCP       16d
redis-slave    ClusterIP      10.151.126.126   <none>         6379/TCP       16d
root@k8sv1:~#
```

ACIにおいて1つのKubernetesクラスタは1つのVRFとして構成され、その外部との通信は全てL3outを経由した外部接続として扱われます。ACIはL3out配下に2つのExternal EPGを構成し、それらExternal EPG間に適用したContractにロードバランス処理を行うために自動的に構成されたService Graphを紐付けます。

![Service Graphを用いたExternal IPへのロードバランスを実現する仕組み](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216144/fe93c62d-81dd-91f4-1b4c-e631f31399ae.png)

外部からL3outに届いた通信は、Static RouteとしてExternal IPがホストルートで指定されたExternal EPGにLPMに基づいて振り分けられ、Contractを通じてStatic Routeとして0.0.0.0/0を持つもう一方のExternal EPGへ通信が流されようとしますがその際にService Graphの定義に基づいてサービスモードとして登録されたKubernetesノードへの振り分けが処理されます。外部からExternal IP宛の通信はそのまま各ノードに振り分けられますが、各ノードのOVSにおいてExternal IPはPodのIPアドレスにNAT処理されます。Podからの返信は当該Podが動作するWorker Node上のOVSによって再度External IPにNAT変換され、Contractを通じて0.0.0.0/0を経路として持つExternal EPGを経由してL3outから外部に通信が行われることによって通信が成立します。

![External EPG間の通信がService Graphによって折り曲げられる仕組みを活用してロードバランスが動作する](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216144/ffc4c32e-4c74-ab7b-aabd-cfa44ad2f8cc.png)

この際、一般的なL4-L7デバイスであればService Graphに登録されているどのノードに転送されても同様に処理できることが前提ですので問題はないわけですが、Kubernetesの場合は当該Serviceに紐づくDeploymentを構成するPodがいくつ、どのWorker Nodeで動作しているのかは動的に変化します。これに対処するために、ACIはKubernetesとのAPI連携に基づいて当該Deploymentが動作するWorker NodeをService Graphの中で通信の折り曲げに利用されているPBR (Policy-based Redirect)処理の宛先として動的に登録・変更します。

以下の例では frontend の役割を持つPod数は1と指定されており、その実態は Worker Node "k8sv2" で動作しているため、このWorker Node 1台だけがPBRの宛先として構成されています。

![ロードバランスの振り分け先ノードはPBR宛先の動的な追加・削除によってPodに紐付けられる](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216144/5062bdcb-767c-2d19-c205-513b1954906d.png)

```Shell
root@k8sv1:~# kubectl get deployment -n gb
NAME           READY   UP-TO-DATE   AVAILABLE   AGE
frontend       1/1     1            1           17d
redis-master   1/1     1            1           17d
redis-slave    2/2     2            2           17d
root@k8sv1:~#
```

ここで`kubectl`コマンドを使ってレプリカ数を1から2に動的に変更してみます。すると、自動的にDeploymentに紐づくPodが追加されますが、ACI CNIはそれと同時にACI側にPBR振り分け先の追加を行い、新たにPodが追加された Worker Node "k8sv3" が登録されます。

![ロードバランスの振り分け先ノードはPBR宛先の動的な追加・削除によってPodに紐付けられる](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216144/b3eb62ef-01be-95ac-4660-e18c0fc5488d.png)

```Shell
root@k8sv1:~# kubectl scale deployment frontend -n gb --replicas 2
deployment.extensions/frontend scaled
root@k8sv1:~# kubectl get deployment -n gb
NAME           READY   UP-TO-DATE   AVAILABLE   AGE
frontend       2/2     2            2           17d
redis-master   1/1     1            1           17d
redis-slave    2/2     2            2           17d
root@k8sv1:~#
```

この動作は当然ながらレプリカ数の削減や、Podの再生成などに際しても動的に構成されるため、Podの配置や変化に合わせて自動的に連携が行われます。

この様に、ACI CNIを用いたKubernetes連携は、セキュリティやロードバランスなど、Kubernetesを実用的に利用する上で必要となるネットワーク機能を提供します。ACI CNIはACI環境でKubernetesを利用するのであれば、ぜひお使い頂きたい 非常に便利 かつ 使い勝手のいい実用的なコンテナネットワークを提供します。

ここでは省略させて頂いた構成手順などについては、以下のインストールガイドを参照して下さい。
- [Cisco ACI and Kubernetes Integration](https://www.cisco.com/c/en/us/td/docs/switches/datacenter/aci/apic/sw/kb/b_Kubernetes_Integration_with_ACI.html)

# 免責事項
本サイトおよび対応するコメントにおいて表明される意見は、投稿者本人の個人的意見であり、シスコの意見ではありません。本サイトの内容は、情報の提供のみを目的として掲載されており、シスコや他の関係者による推奨や表明を目的としたものではありません。各利用者は、本Webサイトへの掲載により、投稿、リンクその他の方法でアップロードした全ての情報の内容に対して全責任を負い、本Web サイトの利用に関するあらゆる責任からシスコを免責することに同意したものとします。