Amazon Web Services ブログ

Amazon EKS で CNI カスタムネットワーキングと Pod セキュリティグループを活用する

この記事は Leveraging CNI custom networking alongside security groups for pods in Amazon EKS (記事公開日: 2022 年 8 月 19 日) を翻訳したものです。

はじめに

Amazon Elastic Kubernetes Service (Amazon EKS) は、独自の Kubernetes コントロールプレーンやノードを構築、運用、保守することなく、AWS 上で Kubernetes を実行できるマネージドサービスです。Amazon EKS は、Amazon VPC Container Network Interface (CNI) プラグインを使用して、Virtual Private Cloud (VPC) のネットワーク機能をネイティブでサポートします。このプラグインは、各 Kubernetes Pod に VPC の IPv4 または IPv6 アドレスを割り当てます。VPC CNI プラグインによって、Pod は AWS ネットワーク本来のパフォーマンスと他の AWS サービスとの連携を利用することができます。この記事では、VPC CNI の CNI カスタムネットワーキングPod セキュリティグループ、2 つの機能について説明します。Kubernetes ワークロードに対するスケーラブルで安全なアーキテクチャを提供するために、これらを組み合わせた利用方法についてウォークスルーを提供します。

CNI カスタムネットワーキング

AWS は、Classless Inter-Domain Routing (CIDR) の範囲が大きい VPC に Amazon EKS クラスターを展開することを推奨しています。この推奨は、VPC CNI が IP アドレスを Pod に割り当てる方法 (Pod ごとに 1 つの IP) と、クラスター内のワークロード数が増加するにつれてクラスターの容量が利用可能な IP アドレスに制限されることに基づいています。大きな CIDR 範囲を利用できない場合 (オンプレミスのインフラストラクチャと IP アドレス空間を共有する環境など) では、100.64.0.0/10 または 198.19.0.0/16 の範囲から CIDR 範囲を VPC に設定することができます。これらの範囲は、CNI カスタムネットワーキングと連携して、Pod 用の追加の IP アドレス空間を提供します。

Pod セキュリティグループ

マイクロセグメンテーションは、コンテナネットワーキングにおける重要なコンセプトの 1 つです。これには、どの Kubernetes Pod がクラスター内の他のどの Pod と通信できるかの制御と、どの Pod がクラスター外の依存するサービスと通信できるかの制御が含まれます。Pod セキュリティグループを有効化すると、Amazon Elastic Compute Cloud (Amazon EC2) のセキュリティグループと Kubernetes Pod を統合し、Kubernetes ワークロードに AWS ネイティブのネットワークセグメンテーションを提供します。また、VPC CNI は、複数の Pod が Elastic Network Interface (ENI) を共有するデフォルトの構成ではなく、Pod ごとに個別の ENI を作成します。各 Pod がそれぞれ ENI を持つため、異なるセキュリティグループを各 Pod にアタッチすることができます。一般的な例として、Amazon Relational Database Service (Amazon RDS) データベースへのアクセスを特定のセキュリティグループを持つ Pod に制限することが挙げられます。Pod セキュリティグループの詳細については、ローンチブログの記事を参照ください。

VPC CNI トラフィックフロー

CNI カスタムネットワーキングと Pod セキュリティグループを併用して実行する際のトラフィックフローを理解するためには、VPC CNI の基本を理解することが重要です。

CNI カスタムネットワーキングを有効化しない場合のデフォルト動作

VPC CNI のデフォルト構成では、Amazon EC2 インスタンスはワーカーノードのセキュリティグループがアタッチされた 1 つの ENI を持ち、基礎となるサブネット上にプロビジョニングされます。ホストへのアクセスに使用される プライマリ IP が ENI にアタッチされています。Amazon EC2 インスタンスに Pod がデプロイされると、VPC CNI はその ENI の上限に達するまで VPC から セカンダリ IP を同じ ENI にアタッチします。そして、VPC CNI は同じサブネット上に 2 つ目の ENI をプロビジョニングして、それに対して セカンダリ IP をアタッチしていきます。このプロセスは、インスタンスの ENI とセカンダリ IP の上限に達するまで継続されます。

デフォルト動作の構成図

図では以下を説明しています。

  • Pod はワーカーノードに接続されている ENI を共有します。
  • 同じセキュリティグループがワーカーノードのすべての ENI にアタッチされ、すべての Pod に適用されます。

Pod セキュリティグループを利用しない場合の CNI カスタムネットワーキングの有効化

CNI カスタムネットワーキングを有効化すると、Pod は最初の ENI 上にプロビジョニングされません。代わりに、カスタム CNI CIDR 範囲の IP アドレスを持つ 2 番目の ENI がワーカーノードにアタッチされ、Pod は non-routable (外部のネットワークと重複可能) な CIDR 範囲のアドレスを利用してこの ENI にアタッチされます。この non-routable な CIDR 範囲から消費されるセカンダリ IP と ENI の数は、各インスタンスの上限によって制限されます。

CNI カスタムネットワーキングの構成図

上図では以下を説明しています。

  • ワーカーノードはプライマリサブネットから 1 つの ENI と 1 つの IP アドレスを使用します。
  • ワーカーノードに接続された追加の ENI は、異なるサブネット ( 100.64 の CIDR 範囲 ) にプロビジョニングされます。
  • Pod はワーカーノードのサブネットから IP アドレスを消費しなくなりました。代わりに non-routable な CIDR 範囲から IP を消費します。
  • 追加の ENI には、 non-routable な各サブネットの ENIConfig カスタムリソース内で定義された独自のセキュリティグループ (複数の設定可能) がアタッチされています。
  • CNI カスタムネットワーキングは、ネットワークセグメンテーションのための Kubernetes ネットワークポリシーエンジン (Calico ネットワークポリシーなど) をサポートしています。

Pod セキュリティグループを利用する場合の CNI カスタムネットワーキングの有効化

CNI カスタムネットワーキングと共に Pod セキュリティグループを使用する場合、non-routable なサブネット内の共有 ENI に Pod を接続するか、同じく non-routable なサブネット内の専用のブランチ ENI に Pod を接続するかを、SecurityGroupPolicy カスタムリソース内のロジックが決定します。

Pod セキュリティグループを使用した CNI カスタムネットワーキングの構成図

図では以下を説明しています。

  • ワーカーノードはプライマリサブネットから 1 つの ENI と 1 つの IP アドレスを使用します。
  • ワーカーノードは non-routable なサブネット内のトランク ENI として別の ENI を使用します。トランク ENI を利用することで Pod はブランチENI を作成できます。作成できるブランチ ENI の数は、Amazon EC2 インスタンスタイプ毎のブランチ ENI の上限数に基づきます。
  • SecurityGroupPolicy のセレクターに一致しない Pod は共有 ENI 上に起動されます (Pod 1) 。
  • SecurityGroupPolicy のセレクターに一致する Pod は独自のセキュリティグループを持つブランチ ENI 上に起動されます (Pod 2 と Pod 3) 。

Ingress と Egress トラフィック

関連するセキュリティグループのルールが確立されていれば、VPC 内のすべての Amazon EKS トラフィックが互い (Amazon EKS コントロールプレーン ENI、ワーカーノードプライマリ ENI、Pod ENI) にルーティングすることができます。しかし、CNI カスタムネットワーキングのサブネットは VPC 外ではルーティングできないため、クラスターに入ってくるインバウンドトラフィックと VPC 外の依存関係に通信するワークロードのトラフィックに対して、どのようにルーティングするか検討する必要があります。

インバウンドトラフィックには、AWS Load Balancer Controller によって管理される Kubernetes Ingress オブジェクトを使用します。AWS Load Balancer Controller を使用することで、Application Load Balancer (ALB) または Network Load Balancer (NLB) をルーティング可能なプライベートまたはパブリックサブネットに作成することができます。

Pod が VPC 外の依存関係に通信する場合、NAT を設定して以下を許可します。

Pod の SNAT

デフォルトでは、Pod が VPC に関連づけられた CIDR ブロック内にない IPv4 アドレスに通信する場合、VPC CNI は Pod の IPv4 アドレスを Pod が動作しているワーカーノードのプライマリ ENI の プライベート IPv4 アドレスに変換します。ビルトインされた NAT により、 non-routable なサブネット内の Pod は、VPC 外の依存関係に通信することができます。

また、AWS_VPC_K8S_CNI_EXTERNALSNATtrue に設定することで、デフォルトの構成を変更することができます。これは、AWS CNI が行う Pod の SNAT ではなく、VPC NAT Gateway などの外部ソースネットワークアドレス変換 (SNAT) を使用することを意味します。詳細は、こちらのドキュメントを参照ください。

クラスター内の Pod が、外部の VPN、Direct Connect、外部の VPC からのインバウンド通信を許可する必要があり、その Pod がインターネットゲートウェイを介して直接インターネットにアクセスする必要がない場合、Pod の SNAT を無効化してください。ただし、ワーカーノードはプライベートサブネットで実行し、AWS NAT Gateway または別の外部 NAT デバイスを介してインターネットに接続する必要があります。

トラフィックパターン

次の図は、Pod セキュリティグループといくつかのトラフィックパターンと合わせて、CNI カスタムネットワーキングによる典型的な Amazon EKS のセットアップを説明しています。

Ingress トラフィック

Ingress トラフィックパターン

  1. 同一クラスター内の Pod から Pod へのアクセス
  2. 同一 VPC 内の Amazon EC2 からのプライベートアクセス
  3. 接続されたプライベートネットワークからプライベートロードバランサーを経由したプライベートアクセス
  4. VPC のパブリックサブネットに設置されたパブリックロードバランサーを経由したパブリックアクセス

Egress トラフィック

Egress トラフィックパターン

  1. 同一クラスター内の Pod から Pod へのアクセス
  2. Pod から同じ VPC 内の他サービス (Amazon RDS データベースなど) へのアクセス
  3. Pod から接続されたプライベートネットワークへの NAT を経由したアクセス
    1. AWS CNI プラグインによる Pod の SNAT を使用した場合
    2. プライベート NAT Gateway を使用した場合
  4. 接続されたネットワーク内の Proxy を経由した Pod からインターネットへのアクセス

これまでの図では、Pod (ブランチ ENI)、ワーカーノード、Amazon EKS コントロールプレーン、Amazon RDS、ALB に異なるセキュリティグループを関連づけ、それらのインバウンドおよびアウトバウンドアクセスを制御しています。

ウォークスルー

このウォークスルーでは、Pod セキュリティグループCNI カスタムネットワーキングを利用した Amazon EKS クラスターのセットアップを自動化する方法を紹介します。このスタックを Terraform でデプロイするためのサンプルコードは、この GitHub リポジトリでホストされています。

このサンプルコードでは、以下のリソースをビルドしています。

Amazon EKS の構築例

前提条件

構築手順

次のコードを実行して環境の構築をします。


$ git clone https://github.com/aws-samples/terraform-cni-custom-network-sample
$ terraform init
$ terraform apply

この Terraform モジュールは、CNI カスタムネットワーキングと Pod セキュリティグループが設定された Amazon EKS クラスターをデプロイします。

以下は、このモジュールがデプロイする内容の内訳です。

  1. パブリック NAT Gateway 用のパブリックサブネット、Amazon EKS クラスターや Amazon EC2 ワーカーノード用のプライベートサブネット、Kubernetes Pod 用の non-routable なイントラサブネットを持つ VPC を作成します。パブリック NAT Gateway は、パブリックサブネットに作成され、プライベートサブネットのデフォルトゲートウェイとして使用されます。プライベートサブネットの 1 つにプライベート NAT Gateway が作成され、イントラサブネットのデフォルトゲートウェイとして使用されます。
  2. Kubernetes API サーバーがパブリックに公開された状態で、ブライベートサブネットに EKS クラスターを作成します。これは、必要に応じてプライベート API サーバーのエンドポイントに変更することができます。
  3. CNI カスタムネットワーキングを設定します。CNI カスタムネットワーキングを設定するには、2 つの手順を実行する必要があります。

はじめに、CNI カスタムネットワーキングを設定するために、AWS CNI プラグインに必要な環境変数をクラスターに配備された aws-node デーモンセットに設定します。

$ kubectl set env daemonset aws-node -n kube-system AWS_VPC_K8S_CNI_CUSTOM_NETWORK_CFG=true
$ kubectl set env daemonset aws-node -n kube-system ENI_CONFIG_LABEL_DEF=topology.kubernetes.io/zone

ここでは、AWS_VPC_K8S_CNI_CUSTOM_NETWORK_CFG で CNI カスタムネットワーキングを有効化し、ENI_CONFIG_LABEL_DEF で各ワーカーノードで使用する ENIConfig を選択するためのワーカーノードのラベルを定義します。topology.kubernetes.io/zone ラベルは、マネージドノードグループ内の各ノードに自動的に設定されます。

次に、イントラサブネットを参照する ENIConfig カスタムリソースの設定をします。あるアベイラビリティゾーンの ENIConfig の例を次のコードで説明します。

apiVersion: crd.k8s.amazonaws.com/v1alpha1
kind: ENIConfig
metadata:
 name: "ap-southeast-2a"
spec:
 subnet: "${subnet_a}"
 securityGroups:
 - ${NODE_SG}
 

この例では、ENIConfig の名前としてアベイラビリティゾーン ap-southeast-2a を使用しています。アベイラビリティゾーン ap-southeast-2a に起動したワーカーノードには、topology.kubernetes.io/zone ラベルの値として ap-southeast-2 が割り当てられるため、この ENIConfig が適用されます。ENIConfig カスタムリソースの詳細についてはこちらを参照ください。

  1. Amazon EKS のマネージドノードグループを作成します。CNI カスタムネットワーキングを有効にした後にマネージドノードグループを作成すると、Pod を新しいサブネットに移行する必要がありません。マネージドノードグループを作成した後に CNI カスタムネットワーキングを有効化すると、CNI カスタムネットワーキングを有効にするために、すべてのワーカーノードを入れ替える必要があります。
  2. Kubernetes クラスター内でセキュリティグループポリシーを設定します。Pod セキュリティグループを設定するには、AWS CNI プラグイン (aws-node) に新たに環境変数を設定する必要があります。
$ kubectl set env daemonset aws-node -n kube-system ENABLE_POD_ENI=true
$ kubectl patch daemonset aws-node \
  -n kube-system \
  -p '{"spec": {"template": {"spec": {"initContainers": [{"env":[{"name":"DISABLE_TCP_EARLY_DEMUX","value":"true"}],"name":"aws-vpc-cni-init"}]}}}}'

次に、次のコードが示すようなセキュリティグループポリシーを作成する必要があります。

apiVersion: vpcresources.k8s.aws/v1beta1
kind: SecurityGroupPolicy
metadata:
  name: my-security-group-policy
  namespace: test-namespace
spec:
  podSelector:
    matchLabels:
      role: test-role
  securityGroups:
    groupIds:
      - ${SECURITY_GROUP}

このポリシーを適用すると、role: test-role ラベルを持つすべての Pod は、指定されたセキュリティグループがアタッチされてブランチ ENI を使用します。セキュリティグループポリシーの詳細についてはこちらを参照ください。

  1. サンプルのデプロイメントを起動します。role: test-role ラベルを持つ 2 つの nginx Pod を作成するデプロイメントを作成します。これらの Pod はイントラサブネットにデプロイされ、Pod セキュリティグループを使用します。

リソースの確認

Terraform のビルドは約 14 分で完了し、次のコマンドでクラスターにアクセスするための kubectlを設定します。

$ aws eks update-kubeconfig --name CNICustomNetworkDemoEKS --region <region-name>

すると、クラスターにアクセスできるようになります。

$ kubectl get pods -A -o wide
NAMESPACE        NAME                                 READY   STATUS    RESTARTS   AGE     IP             NODE                                           NOMINATED NODE   READINESS GATES
kube-system      aws-node-79th4                       1/1     Running   0          3h35m   10.0.3.21      ip-10-0-3-21.ap-southeast-2.compute.internal   <none>           <none>
kube-system      coredns-68f7974869-jlgsc             1/1     Running   0          3h39m   100.64.3.73    ip-10-0-3-21.ap-southeast-2.compute.internal   <none>           <none>
kube-system      coredns-68f7974869-qxn22             1/1     Running   0          3h39m   100.64.3.238   ip-10-0-3-21.ap-southeast-2.compute.internal   <none>           <none>
kube-system      kube-proxy-pj7vl                     1/1     Running   0          3h35m   10.0.3.21      ip-10-0-3-21.ap-southeast-2.compute.internal   <none>           <none>
test-namespace   deployment-example-744b74884-j4jxn   1/1     Running   0          3h23m   100.64.3.48    ip-10-0-3-21.ap-southeast-2.compute.internal   <none>           <none>
test-namespace   deployment-example-744b74884-tgx9j   1/1     Running   0          3h16m   100.64.3.220   ip-10-0-3-21.ap-southeast-2.compute.internal   <none>           <none>

2 つの nginx Pod が 100.64 のサブネットに起動されていることがわかります。Amazon EC2 コンソールのネットワークインターフェースのセクションから Pod の IP アドレス (今回の場合は 100.64.3.48) を検索すると、次の図のように ENI に aws-k8s-branch-eni という記述と example_sg という名前のセキュリティグループが使用されていることがわかります。

ブランチ ENI の例

VPC に Amazon RDS などのリソースを追加して、特定のユースケースをさらにテストすることができます。

クリーンアップ

テストが終わったら、次のコードを実行することでリソースをクリーンアップすることができます。

$ terraform destroy

まとめ

AWS CNI プラグインのカスタム設定により、CNI カスタムネットワーキングが有効になり、通常の Ingress と Egress トラフィックパターンを維持しつつ、各クラスターに non-routable な IP アドレス (ウォークスルーでは 100.64.0.0/16 の範囲) が与えられます。また、Pod セキュリティグループは、通信に VPC ネットワークを使用し、セキュリティグループがインバウンドとアウトバウンドのトラフィックを制御します。

追加の情報

Amazon EKS の Pod ネットワーキングについて、下記のような資料があります。

翻訳はソリューションアーキテクトの鈴木が担当しました。原文はこちらです。