Istio Ambient の成熟化:様々な Kubernetes プロバイダーと CNI にわたる互換性
ワークロードポッドと ztunnel 間の革新的なトラフィックリダイレクトメカニズム。
Istio プロジェクトは 2022 年にアンビエントメッシュ(新しいサイドカーレスデータプレーンモード)を発表し、2023 年初頭にアルファ版を実装しました。
アルファ版では、限られた構成と環境下でのアンビエントデータプレーンモードの価値を実証することに重点を置いていました。しかし、条件は非常に限定されていました。アンビエントモードは、ワークロードポッドとztunnel間のトラフィックを透過的にリダイレクトすることに依存しており、当初使用していたメカニズムは、いくつかのサードパーティ製コンテナネットワークインターフェース(CNI)実装と競合していました。GitHub のイシューや Slack でのディスカッションを通じて、ユーザーはMinikubeやDocker Desktopで、CiliumやCalicoのような CNI 実装を使用し、OpenShiftやAmazon EKSのように社内 CNI 実装を提供するサービスでアンビエントモードを使用したいと考えていることが分かりました。あらゆる Kubernetes 環境で幅広いサポートを得ることが、アンビエントメッシュをベータ版に移行するための最優先事項となっています。人々は、Istio があらゆる Kubernetes プラットフォームと CNI 実装で動作することを期待するようになっています。結局のところ、アンビエントは、あらゆる場所に存在しなければアンビエントとは言えません!
Solo では、アンビエントモードを Gloo Mesh 製品に統合しており、この問題に対する革新的な解決策を考案しました。2023 年後半に、アンビエントをより早くベータ版に到達させるために変更をアップストリームすることにしました。これにより、より多くのユーザーが Istio 1.21 以降でアンビエントを運用し、既存または優先する CNI 実装に関係なく、プラットフォームでアンビエントサイドカーレスメッシュの利点を享受できるようになります。
どのようにしてここまで来たのか?
サービスメッシュと CNI:複雑な関係
Istio はサービスメッシュであり、厳密な定義によれば、すべてのサービスメッシュはCNI 実装ではありません。サービスメッシュは、すべての Kubernetes クラスタに仕様に準拠したプライマリ CNI 実装が存在することを必要とし、その上に構築されます。
このプライマリ CNI 実装は、クラウドプロバイダー(AKS、GKE、EKS はすべて独自のものを提供しています)または Calico や Cilium のようなサードパーティ製の CNI 実装によって提供される場合があります。サービスメッシュの中には、独自のプライマリ CNI 実装をバンドルして提供し、その機能のために明示的に要求するものもあります。
基本的に、mTLS を使用してポッド間のトラフィックを保護し、サービスメッシュレイヤーで高度な認証と承認ポリシーを適用する前に、機能する Kubernetes クラスタと機能する CNI 実装が必要です。これにより、基本的なネットワーク経路が設定され、クラスタ内のポッド間(およびノード間)でパケットが送受信できるようになります。
一部のサービスメッシュは独自のプライマリ CNI 実装を提供し、それを要求する場合もありますが、同じクラスタ内で 2 つのプライマリ CNI 実装を並行して実行することは可能です(たとえば、クラウドプロバイダーが提供するものとサードパーティ製のものの両方)。しかし、実際には、これにより、多くの互換性の問題、予期しない動作、機能の制限、および各 CNI 実装が内部的に使用するメカニズムのばらつきによるいくつかの非互換性が発生します。
これを回避するために、Istio プロジェクトは、独自のプライマリ CNI 実装を提供したり、要求したり、あるいは「優先」CNI 実装を要求したりすることはせず、代わりに、可能な限り幅広い CNI 実装のエコシステムとの CNI チェーンをサポートし、マネージドオファリング、クロスベンダーサポート、および広範な CNCF エコシステムとのコンポーザビリティを最大限に確保することを選択しています。
アンビエントアルファにおけるトラフィックリダイレクト
istio-cni コンポーネントは、サイドカーデータプレーンモードにおけるオプションコンポーネントであり、一般的に、メッシュにポッドをデプロイするユーザーにとってのNET_ADMIN
および NET_RAW
機能の必要性の除去に使用されます。istio-cni
はアンビエントデータプレーンモードでは必須コンポーネントです。istio-cni
コンポーネントはプライマリ CNI 実装ではありません。これは、クラスタに既に存在するプライマリ CNI 実装を拡張するノードエージェントです。
アンビエントメッシュにポッドが追加されるたびに、istio-cni
コンポーネントは、ノードレベルのネットワークネームスペースを介して、ポッドとポッドのノードで実行されているztunnel間のすべての着信トラフィックと発信トラフィックのトラフィックリダイレクトを設定します。サイドカーメカニズムとアンビエントアルファメカニズムの主な違いは、後者では、ポッドトラフィックがポッドネットワークネームスペースから、共存する ztunnel ポッドネットワークネームスペースにリダイレクトされ、その途中でホストネットワークネームスペースを通過する必要があることです。このためのトラフィックリダイレクトルールのほとんどはここで実装されていました。
複数の現実世界の Kubernetes 環境(独自のデフォルト CNI を持っています)でより広範囲にテストした結果、アルファ開発中に実施していたように、ホストネットワークネームスペースでポッドトラフィックをキャプチャしてリダイレクトすることは、私たちの要件を満たすことができないことが明らかになりました。これらの多様な環境全体で、一般的な方法で目標を達成することは、このアプローチでは単に実行可能ではありませんでした。
ホストネットワークネームスペースでトラフィックをリダイレクトすることの基本的な問題は、まさにこの場所が、クラスタのプライマリ CNI 実装がトラフィックルーティング/ネットワークルールを構成しなければならない場所であることです。これにより、避けられない競合が発生しました。最も重要なのは
- プライマリ CNI 実装の基本的なホストレベルのネットワーク構成が、Istio の CNI 拡張からのホストレベルのアンビエントネットワーク構成と干渉し、トラフィックの混乱やその他の競合を引き起こす可能性があることです。
- ユーザーがプライマリ CNI 実装によって適用されるネットワークポリシーをデプロイした場合、Istio CNI 拡張がデプロイされると、そのネットワークポリシーが適用されない可能性があります(プライマリ CNI 実装が NetworkPolicy をどのように適用するかに依存します)。
一部のプライマリ CNI 実装に対してはケースバイケースで対応できる可能性もありましたが、普遍的な CNI サポートに持続的に取り組むことはできませんでした。eBPF を検討しましたが、eBPF 実装はいずれも基本的な問題を抱えていることに気づきました。現在、任意の eBPF プログラムを安全にチェーン/拡張する標準化された方法がないため、このアプローチでは、eBPF 以外の CNI をサポートするのも困難な可能性があります。
課題への対処
新しい解決策が必要でした。ノードのネットワークネームスペースで何らかのリダイレクションを行うと、互換性の要件を損なわない限り、避けられない競合が発生します。
サイドカーモードでは、サイドカーとアプリケーションポッドの両方がポッドのネットワークネームスペース内で動作するため、トラフィックリダイレクトの設定は簡単です。これにより、閃きが生まれました。サイドカーを模倣し、アプリケーションポッドのネットワークネームスペースでリダイレクトを設定するのはどうでしょうか?
これは「単純な」考えのように聞こえますが、そもそもどのように可能なのでしょうか?アンビエントの重要な要件は、ztunnel がアプリケーションポッドの外側、Istio システムネームスペースで実行される必要があることです。いくつかの調査の後、1 つのネットワークネームスペースで実行されている Linux プロセスが、別のネットワークネームスペース内でリスニングソケットを作成して所有できることがわかりました。これは Linux ソケット API の基本的な機能です。ただし、これを運用可能にし、すべてのポッドライフサイクルシナリオを網羅するためには、ztunnel と istio-cni
ノードエージェントの両方にアーキテクチャの変更を加える必要がありました。
この新しいアプローチがアクセス可能なすべての Kubernetes プラットフォームで機能することをプロトタイプを作成して十分に検証した後、この作業に自信を持ち、この新しいトラフィックリダイレクトモデルをアップストリームに貢献することにしました。これは、主要なクラウドプロバイダーと CNI との高度な互換性を備えて構築された、ワークロードポッドと ztunnel ノードプロキシコンポーネント間のポッド内トラフィックリダイレクトメカニズムです。
重要なイノベーションは、ポッドのネットワークネームスペースを共存する ztunnel に提供することで、ztunnel はポッドの外側で実行されながらも、ポッドのネットワークネームスペース内でリダイレクトソケットを開始できます。このアプローチにより、ztunnel とアプリケーションポッド間のトラフィックリダイレクトは、今日のサイドカーとアプリケーションポッドと非常に似ており、ノードネットワークネームスペースで動作する Kubernetes プライマリ CNI には完全に透過的です。ネットワークポリシーは、CNI が eBPF を使用するかに関わらず、プライマリ CNI によって引き続き適用および管理でき、競合は発生しません。
ポッド内トラフィックリダイレクトの技術的な詳細
まず、Kubernetes でポッド間をパケットがどのように移動するかについての基本事項を説明します。
Linux、Kubernetes、および CNI - ネットワークネームスペースとは何か、そしてなぜ重要なのか?
Linux では、コンテナとは、分離された Linux ネームスペース内で実行される 1 つ以上の Linux プロセスです。Linux ネームスペースとは、そのネームスペース内で実行されているプロセスが何が表示できるかを制御するカーネルフラグのことです。たとえば、ip netns add my-linux-netns
コマンドで新しい Linux ネットワークネームスペースを作成し、その中でプロセスを実行した場合、そのプロセスは、そのネットワークネームスペース内で作成されたネットワークルールしか見ることができません。それ以外では作成されたネットワークルールは見えません。それでも、そのマシンで実行されているすべてのものは、1 つの Linux ネットワークスタックを共有しています。
Linux ネームスペースは、概念的には Kubernetes ネームスペースと非常によく似ています。アクティブなプロセスを整理および分離し、特定のネームスペース内のものが何が表示でき、どのようなルールが適用されるかを制御できる論理ラベルです。単に、はるかに低いレベルで動作するだけです。
ネットワーク名前空間内で実行されているプロセスが、別の何か宛てに送信されるTCPパケットを作成する場合、そのパケットは最初にローカルネットワーク名前空間内のローカルルールによって処理され、その後、ローカルネットワーク名前空間を出て、別の名前空間に入ります。
たとえば、メッシュがインストールされていないプレーンなKubernetesでは、ポッドがパケットを作成して別のポッドに送信する場合、パケットは(ネットワークの設定方法によっては)
- 送信元ポッドのネットワーク名前空間内のルールによって処理されます。
- 送信元ポッドのネットワーク名前空間を離れ、ノードのネットワーク名前空間までバブルアップし、そこでその名前空間内のルールによって処理されます。
- そこから、最終的にターゲットポッドのネットワーク名前空間(およびその中のルール)にリダイレクトされます。
Kubernetesでは、コンテナランタイムインターフェース(CRI)はLinuxカーネルとの通信、新しいポッドのネットワーク名前空間の作成、およびそれらの中でプロセスの開始を担当します。その後、CRIはコンテナネットワーキングインターフェース(CNI)を呼び出しますが、これはさまざまなLinuxネットワーク名前空間のネットワークルールを接続し、新しいポッドから出て入るパケットが目的の場所に到達できるようにする役割を担います。CNIがこれを実現するためにどのようなトポロジやメカニズムを使用するかは、Kubernetesやコンテナランタイムにとってほとんど重要ではありません。パケットが目的の場所に到達する限り、Kubernetesは動作し、すべてがうまくいきます。
以前のモデルを廃止した理由
Istioアンビエントメッシュでは、各ノードにKubernetes DaemonSetとして実行される少なくとも2つのコンテナがあります。
- メッシュトラフィックのプロキシ処理とL4ポリシーの適用を行う効率的なztunnel。
- 新しいポッドと既存のポッドをアンビエントメッシュに追加する処理を行う`istio-cni`ノードエージェント。
以前のアンビエントメッシュの実装では、アプリケーションポッドは次のようにアンビエントメッシュに追加されました。
- `istio-cni`ノードエージェントは、名前空間に`istio.io/dataplane-mode=ambient`というラベルが付けられた既存または新しく開始されたKubernetesポッドを検出し、アンビエントメッシュに含める必要があることを示します。
- `istio-cni`ノードエージェントは、ホストネットワーク名前空間でネットワークリダイレクションルールを設定します。これにより、アプリケーションポッドに出入りするパケットはインターセプトされ、関連するプロキシポート(15008、15006、または15001)でそのノードのztunnelにリダイレクトされます。
つまり、アンビエントメッシュ内のポッドによって作成されたパケットは、その送信元ポッドを離れ、ノードのホストネットワーク名前空間に入り、理想的にはインターセプトされてそのノードのztunnel(独自のネットワーク名前空間で実行されている)にリダイレクトされ、宛先ポッドにプロキシされます。復路も同様です。
このモデルは、初期のアンビエントメッシュアルファ実装のプレースホルダーとしては十分に機能しましたが、前述のように、根本的な問題があります。CNIの実装は多数あり、Linuxでは、パケットをあるネットワーク名前空間から別のネットワーク名前空間へ送る方法に、根本的に異なり互換性のない方法が多数存在します。トンネルを使用したり、オーバーレイネットワークを使用したり、ホストネットワーク名前空間を経由したり、バイパスしたりできます。Linuxユーザー空間ネットワークスタックを経由することも、それをスキップしてカーネル空間スタックでパケットをやり取りすることもできます。考えられるすべての方法について、それを利用するCNI実装が存在する可能性があります。
つまり、以前のリダイレクションアプローチでは、アンビエントが単純に動作しないCNI実装が多数ありました。ホストネットワーク名前空間パケットリダイレクションへの依存性があるため、ホストネットワーク名前空間を介してパケットをルーティングしないCNIは、異なるリダイレクション実装を必要とします。そして、これを行うCNIについても、避けられない、解決できない可能性のあるホストレベルルールの競合の問題が発生します。CNIの前でインターセプトしますか、それとも後でインターセプトしますか?どちらか一方を行うと、一部のCNIが壊れてしまう可能性があり、それを期待していない場合はどうなりますか?NetworkPolicyはホストネットワーク名前空間で適用する必要があるため、NetworkPolicyはどこで、いつ適用されますか?すべての一般的なCNIを特別に処理するための多くのコードが必要ですか?
Istioアンビエントトラフィックリダイレクション:新しいモデル
新しいアンビエントモデルでは、アプリケーションポッドは次のようにアンビエントメッシュに追加されます。
- `istio-cni`ノードエージェントは、名前空間に`istio.io/dataplane-mode=ambient`というラベルが付けられたKubernetesポッド(既存のものまたは新しく開始されたもの)を検出し、アンビエントメッシュに含める必要があることを示します。
- アンビエントメッシュに追加する必要がある新しいポッドが開始された場合、CRIによってCNIプラグイン(`istio-cni`エージェントによってインストールおよび管理される)がトリガーされます。このプラグインは、ノードの`istio-cni`エージェントに新しいポッドイベントをプッシュし、エージェントがリダイレクションを正常に構成するまでポッドの起動をブロックするために使用されます。CNIプラグインは、Kubernetesポッド作成プロセスの可能な限り早い段階でCRIによって呼び出されるため、initコンテナなどのものを使用せずに、起動中にトラフィックがエスケープするのを防ぐのに十分早くトラフィックリダイレクションを確立できます。
- 既に実行されているポッドがアンビエントメッシュに追加された場合、新しいポッドイベントがトリガーされます。`istio-cni`ノードエージェントのKubernetes APIウォッチャーがこれを検出し、同様の方法でリダイレクションが構成されます。
- `istio-cni`ノードエージェントはポッドのネットワーク名前空間に入り、ポッドに出入りするパケットがインターセプトされ、既知のポート(15008、15006、15001)でリスニングしているノードローカルのztunnelプロキシインスタンスに透過的にリダイレクトされるように、ポッドネットワーク名前空間内でネットワークリダイレクションルールを設定します。
- `istio-cni`ノードエージェントは、Unixドメインソケットを介してノードztunnelに、ポッドのネットワーク名前空間内でローカルプロキシリスニングポート(15008、15006、および15001で)を確立する必要があることを通知し、ポッドのネットワーク名前空間を表す低レベルのLinuxファイルディスクリプタをztunnelに提供します。
- 通常、ソケットはLinuxネットワーク名前空間内で、そのネットワーク名前空間内で実際に実行されているプロセスによって作成されますが、ターゲットネットワーク名前空間が作成時にわかっている場合、Linuxの低レベルソケットAPIを利用して、あるネットワーク名前空間で実行されているプロセスが別のネットワーク名前空間でリスニングソケットを作成できるようにすることは完全に可能です。
- ノードローカルのztunnelは、内部的に新しいプロキシインスタンスとリスニングポートセットをスピンアップし、新しく追加されたポッド専用にします。
- ポッド内リダイレクトルールが適用され、ztunnelがリスニングポートを確立すると、ポッドはメッシュに追加され、これまでと同様にトラフィックがノードローカルのztunnelを介して流れ始めます。
アプリケーションポッドがアンビエントメッシュに追加される流れを示す基本的な図を次に示します。
ポッドがアンビエントメッシュに正常に追加されると、メッシュ内のポッドとの間のトラフィックは、常にIstioで行われているように、デフォルトでmTLSを使用して完全に暗号化されます。
トラフィックは暗号化されたトラフィックとしてポッドのネットワーク名前空間に出入りします。ポッド内で実行されているユーザーアプリケーションは、どちらにも認識していませんが、アンビエントメッシュ内のすべてのポッドはメッシュポリシーを適用し、トラフィックを安全に暗号化できるようになります。
新しいモデルでアンビエントメッシュ内のポッド間で暗号化されたトラフィックがどのように流れるかを示す図を次に示します。
そして、これまでと同様に、メッシュ外の暗号化されていないプレーンテキストトラフィックは、必要に応じて処理およびポリシー適用を行うことができます。
新しいアンビエントトラフィックリダイレクション:これにより得られるもの
新しいアンビエントキャプチャモデルの最終的な結果は、すべてのトラフィックキャプチャとリダイレクションがポッドのネットワーク名前空間内で行われることです。ノード、CNI、その他のすべてにとって、ポッド内にサイドカープロキシが存在するように見えますが、**ポッド内にサイドカープロキシはまったく実行されていません**。CNI実装の役割は、パケットをポッドに**送受信**することです。設計上、そしてCNI仕様によって、それ以降のパケットに何が起こるかについては気にしません。
このアプローチにより、さまざまなCNIとNetworkPolicyの実装との競合が自動的に解消され、すべての主要なCNIにわたるすべての主要なマネージドKubernetesオファリングとのIstioアンビエントメッシュの互換性が大幅に向上します。
まとめ
さまざまなKubernetesプラットフォームとCNIで変更をテストするための、私たちの素晴らしいコミュニティからの多大な努力、そしてIstioメンテナからの多くのレビューラウンドのおかげで、ztunnelとistio-cniのPRがこの機能を実装し、Istio 1.21にマージされ、アンビエントでデフォルトで有効になったことをお知らせできることを嬉しく思います。そのため、Istioユーザーは、Istio 1.21以降で、任意のCNIを使用して任意のKubernetesプラットフォームでアンビエントメッシュを実行できるようになりました。GKE、AKS、EKS、およびそれらが提供するすべてのCNI実装、CalicoやCiliumなどのサードパーティのCNI、OpenShiftなどのプラットフォームでテストを行い、確かな結果を得ています。
ztunnelとユーザーのアプリケーションポッド間の革新的なポッド内トラフィックリダイレクションアプローチにより、Istioアンビエントメッシュをあらゆる場所で実行できるように進められたことを大変嬉しく思っています。アンビエントベータへの最大の技術的なハードルが解決されたことで、Istioコミュニティの皆様と一緒にアンビエントメッシュをすぐにベータ版にしたいと考えています。アンビエントメッシュのベータ版の進捗について詳しくは、IstioのSlackの#ambientと#ambient-devチャンネルに参加するか、毎週水曜日に開催されるアンビエントコントリビューター会議に参加するか、アンビエントメッシュベータプロジェクトボードを確認して、何か修正してください!