VPC CNIを深掘り: IPAMDとSecurity Groups For Podを徹底分析

技術本部 サービスリライアビリティグループ(SRG)の石川 雲(@ishikawa_kumo)です。
#SRG(Service Reliability Group)は、主に弊社メディアサービスのインフラ周りを横断的にサポートしており、既存サービスの改善や新規立ち上げ、OSS貢献などを行っているグループです。
この記事では、VPCCNIアドオンの動作と原理を解説し、Security Groups for Pod(SGP)を使用した際の注意点について説明します。また、2020年から発生し始めた「」というエラーに関して、ソースコードレベルの説明と分析を行う内容となっています。
 

AWS VPCアドオン


AWS VPC CNIは、EKSのネットワーキング構成において中心的な役割を果たしており、AWS VPCとネイティブな連携することで、EKS上でのネットワーク管理を効率化しています。
AWS VPC CNIは以下のコンポーネントによって構成されています。
  • CNI Plugin
    • CNIの仕様に沿って、gRPCでIPAMDと通信してIPを取得し、ネットワーク設定を行う
  • IPAMD Plugin
    • 各NodeにおいてAWS ENIを管理し、迅速なPod起動のためにIPアドレスの事前付与を行う
それぞれの関係はProposalに示された図に基づいています(実際に呼ばれる名前と若干違います)。

仕組み

VPC CNIは、PodのIP割り当てとネットワークルーティングの機能を主に提供しています。
  • PodのIP割り当て
    • PodへのIPアドレス割り当ては、EC2 ENIとIPAMDを使用して行われます。一般的なフローは以下のようになります。
  • ネットワークルーティング
    • Pod間やPodから外部へのネットワークルーティングは、複数のルーティングテーブルとIPTABLEを使用して構成されています。その使い分けは以下の通りです。
    • Pod ↔ 外部ネットワーク: IPTABLE
      • 通常のSNATのように、パケットが仮想ネットワークインターフェース(veth)とEC2 ENI(ethなど)を通過する際に実行されます。
    • Pod ↔ Pod: ルーティングテーブル
      • Pod間の通信やPodから外部への通信は、主にvethとethなどを介して行われ、さまざまなルーティングテーブルが暗黙的に適用されます。

ライフサイクル

VPCCNIのバイナリは、主に7種類があります
Binary説明
aws-k8s-agentIPAMDのgRPCサーバ
aws-cniCNI Pluginの実装、主にVPC内ENI/IPとLinux Networkなどの設定を管理する
grpc-health-probeIPAMD向けヘルスプローブの確認ツール
cni-metrics-helperメトリクスを収集し、CloudWatchへPutMetricsするサポートツール
aws-vpc-cni-init()システムパラメータ、IPV6、各バイナリファイルの複製など、前提条件が整っていることを保証するための初期化コンポーネント
aws-vpc-cni過去ではとして存在し、現在はIPAMDの起動とライフサイクル管理を行うツール
egress-cniCNI Pluginの実装、主にSNATを使用して、アウトバウンドトラフィックのための特定のルーティングルールやポリシーを管理する
: 過去では、によって起動の順番などを制御していましたが、1.11以降にバイナリファイルの制御は主にGoファイルによって行われます。
実行順番
  1. CNI Pluginバイナリをkubeletの所定のディレクトリ()にコピーする
  1. IPAMDを起動する
  1. を使ってIPAMDへのヘルスチェックを行う
  1. を実行させる 現時点はInitContainerとして使用されているため、コメントアウトされています。
    1. 過去では、が成功すると、を使って特定の空ファイルを作成し、がその空ファイルの存在を確認するような流れがありましたが、 への移行によりこの流れは廃止となりました。(廃止されてはいますが、コメントとしてはまだ存在し、将来的に他の方法に変更される可能性があります。)
  1. CNIConfigファイルをkubeletの所定のディレクトリにコピーする
  1. IPAMDの起動を待つ
IPAMD関連の実行は主に を使用しています。以下は を使用してヘルスチェックを行うコードの抜粋です。

CNI Pluginの詳細

CNI Pluginは、CNIを実装した部分を指しています。(バイナリファイルの )
CNI Pluginのソースコードは にありますが、バージョンによってソースコードの位置が違います。バージョン1.6.0以前では、 のパスになります。詳細の変更はこちらのIssueCommitから確認できます。本記事の参考記事の一つ Amazon VPC CNI plugin for KubernetesのソースコードリーディングでEKSのネットワーキングについて理解を深める #1の内容は、2020年早期のソースコードベースに基づいて説明されるものです。
ファイル構成
CNI Pluginの登録
関数を使用して、(ネットワーク追加)と(ネットワーク削除)のコマンドをCNI Pluginとして登録する
Add コマンド
  1. 設定の読み込み
    1. 関数で、 とログ関連の変数を読み込み、全般設定を行います。この部分はほとんどエラーを起こしませんが、VPCCNI以外のCNIと同時に使用すると、MTUなどの変数で衝突が起こされ、以下のエラーが発生する可能性があります。
  1. Kubernetes引数の読み込み
    1. で関数でk8s自体の引数を読み込み、設定を行います。上記と同様に、他のCNIと同時に使うと、衝突が発生する可能性があります。
  1. IPAMDへのgRPC接続の設定
  1. ネットワークの追加APIリクエスト
    1. IPADMライブラリを使用して、gRPCを通してIPアドレスを取得します。
      この部分が失敗すると、エラーが発生します。バージョン1.6から1.11まで多くの関連Issueが報告され、実際弊社2021年のCNDT発表(09:10から)でもこの現象について初めて紹介しました。次の節での動作を詳細に説明したいと思います。
  1. ネットワーク設定の実装
    1. またはでVLANとLinux RoutingTableの設定を行います。
      こちらの部分は、 という変数を使用して分岐しています。が0ではない場合、Branch ENIを使用することを意味し、つまりSGP機能が有効化されていることです。
Del コマンド
  1. 設定の読み込み
    1. Addコマンドと同じです
  1. Kubernetes引数の読み込み
    1. Addコマンドと同じです
  1. 前回の結果による削除試行
    1. 関数を使用して前回の結果に基づいてリソースを削除します。
  1. IPAMDへのgRPC接続の設定
  1. ネットワーク削除APIリクエスト
    1. IPAMDライブラリを使って、gRPCを通してIPアドレスを削除します。
  1. ネットワークリソースのクリーンアップ
    1. Pod IPの釈放などを行います。SGP使用の場合、Branch ENIの削除も実行します。

IPAMD Pluginの詳細

IPAMD Pluginは主にIPAMD gRPCサーバを指しています。このプラグインはバイナリとして存在し、バイナリによって起動されます。ソースコードは に集中しています。
ファイル構成
IPAMDの処理概要
  1. 初期化プロセスには以下が含まれます
      • k8s API Serverとの接続確認
      • k8s API Client初期化
      • k8s Eventを発行するためのRecorder初期化
      • IPAMD Clientの初期化
  1. の別goroutineでの起動
    1. ここはIPAMDのコアの機能の一つとなります。Reconcile関数を通して、セカンドIPとENI(Trunk/Branchも含む)の管理を行います。このReconcileプロセスのログが多く、Nodeのにそのほとんどが記録されています。
  1. Prometheus Metrics API Serverの別goroutineでの起動
    1. 環境変数で無効化されていない場合、で起動します
  1. 内省API Serverの別goroutineでの起動
    1. 同じく環境変数で無効化されていない場合起動します。この内省/内観(Introspection) APIはIPAMDの運用状況を監視し、診断するために使用されます。現時点での内観APIは以下の四つの主要機能を提供します。
      • IPAMD ContextからENI情報を取得する
      • Node情報から特定のENI設定名を取得する
      • ネットワーク設定のデバッグ情報を取得する
      • IPAMDの環境設定のデバッグ情報を取得する
  1. gRPC Serverの起動

全体像

エラーについて

前節で、SGPを利用する際に頻発する というエラーを簡単に紹介しました。以下では、IPAMD 関数の詳細を分析し、エラーの原因となる可能性を探ることにします。
AddNetworkの処理概要
  1. PodENIが有効化された場合(SGP利用)の処理
    1. PodのResource Profileにてを持つPodが対象です
      1. SGP対象のPodは、以下二箇所にVPCCNIによって修正されます。
    2. Annotation の値から以下の情報を取得し、それに基づいてIPv4またはIPv6による追加の検証を行います。
    1. AnnotationにIPが存在しない場合、EC2 ENI DatastoreからIPを取得する
    1. AnnotationにIPが存在した場合、そのIPを現存のVPC IP Poolに追加する
    1. CNI PluginへIP追加のレスポンスを行う
    上記の流れで、任意のところでエラーが発生すると、Pod起動が遅延され、というエラーが繰り返して出現することがあります。
    デバッグする際には、Nodeにアクセスしてipamd.logの中身を分析し、関係のあるレベルのログを特定して解析すること重要です。出現する可能性のあるレベルのログは以下となります。
    ログメッセージ説明SGP有効化との関連性原因推測
    CNI側とIPAMD側が保持しているバージョン情報が一致しないなしDaemonSet更新タイミンの問題
    IPAMD側が受け取ったPod名のPodが存在しない、Annotationの情報取得失敗ありAnnotationとResource Profile管理仕組みのバグ
    Nodeに搭載中のTrunk ENIが存在しないか、LinkIndex情報を取得できないなし何かのバグ
    Annotataionの情報パース失敗ありParser自体と利用のバグ
    2020年から2022年まで、類似Issueが50件以上報告されています。AWS VPC CNI自身のバグが主な原因とされています。
    そのほかに、以下の原因も多いです。
    • SGPに対応していないEC2 Instance Typeを使用した (t系を使用したとか)

    Security Groups For Pods


    Security GroupsをNodeレベルではなくPodレベルで適用する機能です。これにより、EKS Pod と VPC内の他のサービス間との通信セキュリティを強化することができます。
    一般的なユースケースは EKS Pod ↔ RDS/ElastiCache通信制御です。

    仕組み

    Podごとにという種類のNetworkInterfaceを作成し、Branch ENI対象にSecurity Groupを割り当てる仕組みです。これらのBranch ENIは、EKS Node上のTrunk ENIに接続されています。Trunk ENIは中央ハブとして機能し、複数のBranch ENIのネットワークトラフィックを管理します。Trunk ENIは、SGPが有効化される時点で、 daemonsetによって作成されます。

    設定方法とENVの説明

    VPCCNI v1.14以降、SGP有効化の必須手順は、Container対象に、以下の設定を設定する必要があります。
    DaemonSet 
    1.10以前、Liveness/Readiness Probes利用する際、initContainers対象に、以下の設定も設定する必要があります。
    1.10以前、のデフォルト値がstrictであるため、全てのトラフィックはPodごとのSGによって制御されます。KubeletがTCPを介してLiveness/Readiness ProbesのためにPodに接続するトラフィックもその対象になってしまいます。
    ENFORCING_MODEについて
    • Strictモード
      • SG付きPodへ全てのInbound/OutboundトラフィックはBranch ENIのSGによってのみ制御されます。一方、Pod間の全てのInbound/OutboundトラフィックはVPCの中に入ります。
    • Standardモード
      • (VPC内)全ての通信はPrimary ENIとBranch ENIの両方のSGが適用されます。
      • SG付きPodへ全てのInbound/OutboundトラフィックはBranch ENIのSGのみによって制御されます。ただし、kubeletからのInbound/OutboundトラフィックはNode SGによって制御され、Branch ENIのSGのルールは適用されないため、SGPを使用する際に、NodeSGとBranch ENIのSGを同時にPodに設定する必要があります。
      • VPC外(External VPN/Direct Connection/External VPC)へのOutboundトラフィックについては、以下の条件が適用されます。
        • がが有効な場合、トラフィックはSNATされず、SGのルールによって制御されます
        • が無効化されている場合、トラフィックはeth0経由でSNATされ、eth0に関連付けられたSGのルールのみによって制御されます。つまり、Branch ENIのSGのルールは適用されず、Primary ENIのSGのルールのみが適用されます
    WARM_*, IP_COOLDOWN_PERIODについて
    系のENVは、主にENIの搭載やセカンドIPの付与に関連しており、SGP自体の動作とは直接関係していません。上記のVPCCNI節には、WARM_*系のENVが関連するソースコードの記載が見つかりませんでした。 2021年のIssueでは、解決が困難なエラーについて、一部のIssue討論者の中では系ENVと ENVが影響しているのではないかとの見解がありましたが、メインテナーによって「影響がない」とドキュメントで明記され、否定されました。
    Any of the WARM targets do not impact the scale of the branch ENI pods so you will have to set the WARM_{ENI/IP/PREFIX}_TARGET based on the number of non-branch ENI pods. If you are having the cluster mostly using pods with a security group consider setting WARM_IP_TARGET to a very low value instead of default WARM_ENI_TARGET or WARM_PREFIX_TARGET to reduce wastage of IPs/ENIs.

    制約

    • 前述通り、SecurityGroupPolicyを作成する際、Podに設定するSecurityGroupは、Branch ENI SGとNode SGの両方を含める必要があります。
    • Trunk ENIもNodeが搭載できるENI数に含まれるため、NodeのENI数は使用中のInstance TypeがサポートしているENI数に達した場合、Trunk ENI作成されないため、該当のNode上SGが適用したPodが作成されません。
    • runk ENIが作成できるBranch ENI数(すなわちPod数)は限られています。ドキュメントでは載っていないため、こちらのソースコードのMapから実際の数を計算してください。計算方法は以下となります。
      • SGPサポートしているInstanceTypeは、上記ソースコードのMapにおいて、と記述されているものだけです
      • SGP適用後のPod再起動は通常より時間がかかるため、最適なDeployment Update Strategyを採用してください

      参考文献


      終わりに


      VPCCNIは、最近ついにNetwork Policy Engineとして使えるようになりました。2019年以降、進化を続け、最近では使い心地も良くなってきました。未来のVPCCNIに期待しています。
      SGPの公式チュートリアルドキュメントには、赤色枠で示されたImportantに、重要な情報と制約条件が記載されています。繰り返して確認するのが重要でしょう。
      今回のIPAMDとSGPに関連するソースコードを理解することで、トラブルシューティングの精度が大幅に向上しました。今後もこういった学習方法で挑戦してみたいと思います。
       
      SRG では一緒に働く仲間を募集しています。 ご興味ありましたらぜひこちらからご連絡ください。
       
      SRG では最近出たホットなIT技術や書籍などについてワイワイ雑談するポッドキャストを運営しています。ぜひ、作業のお供に聞いていただければ幸いです。
      このエントリーをはてなブックマークに追加