A3 Ultra、A4、A4X などの一部のアクセラレータ最適化マシンには、これらのマシンの MRDMA インターフェースに加えて、2 つのホスト ネットワーク インターフェースがあります。ホストでは、これらは個別の CPU ソケットと不均一メモリアクセス(NUMA)ノードに接続されている Titanium IPU です。これらの IPU は、Google 仮想 NIC(gVNIC)として VM 内で使用可能であり、チェックポイント、トレーニング データの読み込み、モデルの読み込みなどのストレージ アクティビティや、その他の一般的なネットワーキングのニーズに対応するネットワーク帯域幅を提供します。gVNIC の NUMA トポロジを含むマシンの NUMA トポロジは、ゲスト オペレーティング システム(OS)に表示されます。
このドキュメントでは、これらのマシンで 2 つの gVNIC を使用する場合のベスト プラクティスについて説明します。
概要
一般に、複数のホスト NIC をどのように使用するかにかかわらず、次の構成を使用することをおすすめします。
- ネットワーク設定: 各 gVNIC には一意の VPC ネットワークが必要です。VPC の設定では、次の点を考慮してください。
- 各 VPC ネットワークに大きな最大伝送単位(MTU)を使用します。8896 はサポートされている最大 MTU であり、推奨される選択肢です。システムが受信側で受信データ パケットをドロップするため、一部のワークロードの入力パフォーマンスが低下する可能性があります。この問題は、ethtool ツールを使用して確認できます。このシナリオでは、TCP MSS、インターフェース MTU、VPC MTU を調整して、ページキャッシュからの効率的なデータ割り当てを可能にすると、受信レイヤ 2 フレームが 2 つの 4 KB バッファに収まるようになります。
- アプリケーション設定
- アプリケーションを NUMA に合わせて調整します。同じ NUMA ノードから CPU コア、メモリ割り当て、ネットワーク インターフェースを使用します。特定の NUMA ノードまたはネットワーク インターフェースを使用するためにアプリケーションの専用インスタンスを実行している場合は、
numactl
などのツールを使用して、アプリケーションの CPU とメモリリソースを特定の NUMA ノードに接続できます。
- アプリケーションを NUMA に合わせて調整します。同じ NUMA ノードから CPU コア、メモリ割り当て、ネットワーク インターフェースを使用します。特定の NUMA ノードまたはネットワーク インターフェースを使用するためにアプリケーションの専用インスタンスを実行している場合は、
- オペレーティング システムの設定
- TCP セグメンテーション オフロード(TSO)と大規模な受信オフロード(LRO)を有効にします。
- 各 gVNIC インターフェースで、その割り込みリクエスト(IRQ)がインターフェースと同じ NUMA ノードで処理され、割り込みがコア全体に分散されるように SMP アフィニティが設定されていることを確認します。Google 提供のゲスト OS イメージを実行している場合、このプロセスは
google_set_multiqueue
スクリプトを使用して自動的に行われます。 - RFS、RPS、XPS などの設定を評価して、ワークロードに役立つかどうかを確認します。
- A4X の場合、Nvidia は自動 NUMA スケジューリングを無効にすることを推奨しています。
- これらのマシンでは、Linux カーネル ボンディングは gVNIC でサポートされていません。
複数のホスト NIC を使用するパターン
このセクションでは、Google Cloudで複数のホスト NIC を使用する一般的なパターンについて説明します。
サポートされているデプロイ パス | |||||
---|---|---|---|---|---|
パターン | サポートされているプロセス レイアウト | GCE(全般) | GKE | SLURM | 注 |
特定のインターフェースを使用するようにアプリケーションを変更する | インターフェースごとのプロセス シャード | ✅ | ✅ | ✅ | アプリケーションのコード変更が必要 |
両方のインターフェースを使用するようにアプリケーションを変更する | デュアル インターフェース プロセス | ✅ | ✅ | ✅ | アプリケーションのコード変更が必要 |
特定のアプリケーションに専用のネットワーク名前空間を使用する | インターフェースごとのプロセス シャード | ✅ | ✅(特権コンテナのみ) | ⛔ | |
コンテナ全体のトラフィックを単一のインターフェースにマッピングする | 1 つのインターフェースにマッピングされたすべてのコンテナ トラフィック | ✅ | ✅ | ⛔ | |
VPC をピアリングし、システムがインターフェース間でセッションをロードバランスできるようにする | デュアル インターフェース プロセス | ✅* | ✅* | ✅* | NUMA アラインメントが困難または不可能。Linux カーネル 6.16 以降が必要* |
ネットワーク間でトラフィックをシャーディングする | デュアル インターフェース プロセス インターフェースごとにプロセス シャード | ✅* | ✅* | ✅* | デュアル インターフェース プロセスを実行している場合は、NUMA に合わせるためにコードの変更が必要になることがあります。 |
SNAT を使用して送信元インターフェースを選択する | デュアル インターフェース プロセス インターフェースごとにプロセス シャード | ✅ | ✅(セットアップには管理者権限が必要です) | ✅(セットアップには管理者権限が必要です) | 正しく構成するのが難しい場合がある |
* このオプションは一般的にはおすすめしませんが、x86(A3 Ultra と A4)プラットフォームの限定的なワークロードには役立つ場合があります。
特定のインターフェースを使用するようにアプリケーションを変更する
要件:
- この方法では、アプリケーションのコードを変更する必要があります。
- 次の 1 つ以上のメソッドの権限が必要です。
bind()
は、特権ソースポートを使用する場合にのみ特別な権限を必要とします。SO_BINDTODEVICE
:CAP_NET_RAW
権限が必要です。
- この方法では、ルートを確立して非対称ルーティングを防ぐために、カーネル ルーティング テーブルの変更が必要になることがあります。
概要
このパターンでは、次の操作を行います。
- 次のいずれかのオプションを使用して、アプリケーションのソースコードにネットワーク インターフェース バインディングを追加します。
bind()
を使用して、ソケットを特定の送信元 IP アドレスにバインドするSO_BINDTODEVICE
ソケット オプションを使用して、ソケットを特定のネットワーク インターフェースにバインドする
- 必要に応じてカーネル ルーティング テーブルを変更して、送信元ネットワーク インターフェースから宛先アドレスへのルートが存在するようにします。また、非対称ルーティングを防ぐためにルートが必要になることもあります。追加のネットワーク インターフェースのルーティングを構成するの説明に従って、ポリシー ルーティングを構成することをおすすめします。
numactl
コマンドを使用してアプリケーションを実行することもできます。このアプローチでは、選択したネットワーク インターフェースと同じ NUMA ノードにあるメモリと CPU を使用します。
上記の手順を完了すると、アプリケーションのインスタンスは特定のネットワーク インターフェースを使用して実行されます。
両方のインターフェースを使用するようにアプリケーションを変更する
要件:
- この方法では、アプリケーションのコードを変更する必要があります。
- 次の 1 つ以上のメソッドに対する権限が必要です。
bind()
は、特権ソースポートを使用する場合にのみ特別な権限を必要とします。SO_BINDTODEVICE
:CAP_NET_RAW
権限が必要です。
- この方法では、ルートを確立して非対称ルーティングを防ぐために、カーネル ルーティング テーブルの変更が必要になることがあります。
概要
このパターンを実装するには、次の操作を行います。
- 次のいずれかのオプションを使用して、アプリケーションのソースコードにネットワーク インターフェース バインディングを追加します。
bind()
システムコールを使用して、ソケットを特定の送信元 IP アドレスにバインドするSO_BINDTODEVICE
ソケット オプションを使用して、ソケットを特定のネットワーク インターフェースにバインドする
- アプリケーションがクライアントとして機能している場合は、送信元ネットワーク インターフェースごとに個別のクライアント ソケットを作成する必要があります。
- 必要に応じてカーネル ルーティング テーブルを変更して、送信元ネットワーク インターフェースから宛先アドレスへのルートが存在するようにします。また、非対称ルーティングを防ぐためにルートが必要になることもあります。追加のネットワーク インターフェースのルーティングを構成するの説明に従って、ポリシー ルーティングを構成することをおすすめします。
- ネットワーク アクティビティを、gVNIC インターフェースと同じ NUMA ノードで実行されるスレッドに分割することをおすすめします。スレッドに特定の NUMA ノードをリクエストする一般的な方法の 1 つは、
pthread_setaffinity_np
を呼び出すことです。- アプリケーションは複数の NUMA ノードのリソースを利用するため、
numactl
の使用を避けるか、numactl
コマンドにアプリケーションで使用されるすべてのネットワーク インターフェースの NUMA ノードが含まれるようにします。
- アプリケーションは複数の NUMA ノードのリソースを利用するため、
特定のアプリに専用のネットワーク名前空間を使用する
要件:
CAP_SYS_ADMIN
機能が必要です。- GKE Autopilot と互換性がありません。
- GKE を使用している場合は、特権コンテナが必要です。
このセクションでは、セカンダリ ネットワーク インターフェースを使用するネットワーク Namespace を作成するために使用できるパターンについて説明します。ワークロードに適したパターンは、特定のシナリオによって異なります。仮想スイッチまたは IPvlan を使用するアプローチは、複数のアプリケーションが異なるネットワーク名前空間からセカンダリ インターフェースを使用する必要がある場合に適しています。
概要: セカンダリ インターフェースを専用のネットワーク名前空間に移動する
このパターンでは、ネットワーク Namespace を作成し、セカンダリ gVNIC インターフェースを新しい Namespace に移動してから、この Namespace からアプリケーションを実行します。このパターンは、仮想スイッチを使用する場合よりも設定とチューニングが複雑にならない可能性があります。ただし、新しいネットワーク名前空間外のアプリケーションはセカンダリ gVNIC にアクセスできません。
次の例は、eth1 を second という新しいネットワーク Namespace に移動するために使用できる一連のコマンドを示しています。
ip netns add second
ip link set eth1 netns second
ip netns exec second ip addr add ${ETH1_IP}/${PREFIX} dev eth1
ip netns exec second ip link set dev eth1 up
ip netns exec second ip route add default via ${GATEWAY_IP} dev eth1
ip netns exec second <command>
このコマンドを実行すると、<command> 式がネットワーク名前空間内で実行され、eth1 インターフェースが使用されます。
新しいネットワーク Namespace 内で実行されているアプリケーションは、セカンダリ gVNIC を使用するようになりました。numactl
コマンドを使用して、選択したネットワーク インターフェースと同じ NUMA ノードにあるメモリと CPU を使用してアプリケーションを実行することもできます。
概要: セカンダリ インターフェースに仮想スイッチとネットワーク Namespace を使用する このパターンでは、ネットワーク Namespace からセカンダリ gVNIC を使用するように仮想スイッチを設定します。
大まかな手順は次のとおりです。
- 仮想イーサネット(veth)デバイスのペアを作成します。各デバイスの最大伝送単位(MTU)を、セカンダリ gVNIC の MTU と一致するように調整します。
- 次のコマンドを実行して、IPv4 で IP 転送が有効になっていることを確認します。sysctl -w net.ipv4.ip_forward=1
- veth ペアの一方の端を新しいネットワーク Namespace に移動し、もう一方の端をルート Namespace に残します。
- veth デバイスからセカンダリ gVNIC インターフェースにトラフィックをマッピングします。これを行う方法はいくつかありますが、VM のセカンダリ インターフェースの IP エイリアス範囲を作成し、この範囲の IP アドレスを Namespace の子インターフェースに割り当てることをおすすめします。
- 新しいネットワーク名前空間からアプリケーションを実行します。
numactl
コマンドを使用すると、選択したネットワーク インターフェースと同じ NUMA ノードにあるメモリと CPU を使用してアプリケーションを実行できます。
ゲストとワークロードの設定によっては、veth デバイスを作成する代わりに、セカンダリ gVNIC にリンクされた IPvlan インターフェースで IPvlan ドライバを使用することもできます。
コンテナ全体のトラフィックを単一のインターフェースにマッピングする
要件:
- アプリケーションは、コンテナ ネットワーキングにネットワーク名前空間を使用するコンテナ(GKE、Docker、Podman など)内で実行する必要があります。ホスト ネットワークは使用できません。
GKE、Docker、Podman などの多くのコンテナ テクノロジーは、コンテナのトラフィックを分離するために専用のネットワーク Namespace を使用します。このネットワーク名前空間は、直接変更することも、コンテナ テクノロジーのツールを使用してトラフィックを別のネットワーク インターフェースにマッピングすることもできます。
GKE では、Kubernetes 内部通信にプライマリ インターフェースが必要です。ただし、次の GKE Pod マニフェストに示すように、Pod のデフォルト ルートを変更してセカンダリ インターフェースを使用できます。
metadata:
…
annotations:
networking.gke.io/default-interface: 'eth1'
networking.gke.io/interfaces: |
[
{"interfaceName":"eth0","network":"default"},
{"interfaceName":"eth1","network":"secondary-network"},
]
このアプローチでは、デフォルトのネットワーク インターフェースと CPU またはメモリ間の NUMA アライメントは保証されません。
VPC をピアリングし、システムがインターフェース間でセッションをロードバランスできるようにする
要件:
- プライマリ gVNIC とセカンダリ gVNIC の VPC 間で VPC ピアリングを確立する必要があります。
- 単一の宛先 IP とポートに送信する場合、送信元インターフェース間で TCP セッションをロードバランスするには、Linux カーネル バージョン 6.16 が必要です。
- ネットワーキング スタックがクロスソケット メモリ転送を生成しても、ワークロードはパフォーマンス要件を満たすことができます。
概要
場合によっては、アプリケーション内またはアプリケーションのインスタンス間でネットワーク接続をシャーディングすることが困難なことがあります。このシナリオでは、クロス NUMA またはクロスソケット転送に影響を受けない A3U または A4 VM で実行されている一部のアプリケーションでは、2 つのインターフェースを代替可能として扱うと便利です。
この方法の 1 つは、fib_multipath_hash_policy sysctl とマルチパス ルートを使用することです。
PRIMARY_GW=192.168.1.1 # gateway of nic0
SECONDARY_GW=192.168.2.1 # gateway of nic1
PRIMARY_IP=192.168.1.15 # internal IP for nic0
SECONDARY_IP=192.168.2.27 # internal IP nic1
sysctl -w net.ipv4.fib_multipath_hash_policy=1 # Enable L4 5-tuple ECMP hashing
ip route add <destination-network/subnet-mask> nexthop via ${PRIMARY_GW} nexthop
via ${SECONDARY_GW}
ネットワーク間でトラフィックをシャーディングする
要件:
- VM の
nic0
とnic1
は、別々の VPC とサブネットにあります。このパターンでは、宛先アドレスがnic0
とnic1
の VPC にシャーディングされている必要があります。
概要
デフォルトでは、Linux カーネルは nic0
のサブネットと nic1
のサブネットのルートを作成します。これにより、トラフィックは宛先ごとに適切なネットワーク インターフェースを介してルーティングされます。
たとえば、nic0
がサブネット subnet-a
を含む VPC net1
を使用し、nic1
がサブネット subnet-b
を含む VPC net2
を使用するとします。デフォルトでは、subnet-a
のピア IP アドレスへの通信には nic0
が使用され、subnet-b
のピア IP アドレスへの通信には nic1
が使用されます。たとえば、このシナリオは、net1
に接続されたピアのシングル NIC VM のセットと、net2
に接続されたセットで発生する可能性があります。
SNAT を使用して送信元インターフェースを選択する
要件:
CAP_NET_ADMIN
は、アプリケーションの実行には必要ありませんが、初期 iptables ルールの設定には必要です。- ルールを他の重要な iptables ルールやルーティング構成と組み合わせて使用する場合は、ルールを慎重に評価する必要があります。
注:
- NIC バインディングは、接続の作成時にのみ正しく設定されます。スレッドが別の NUMA ノードに関連付けられた CPU に移動すると、接続でクロス NUMA ペナルティが発生します。そのため、このソリューションは、スレッドを特定の CPU セットにバインドするメカニズムがある場合に最も効果的です。
- このマシンから発信された接続のみが特定の NIC にバインドされます。受信接続は、宛先アドレスに一致する NIC に関連付けられます。
概要
ネットワーク Namespace の使用やアプリケーションの変更が難しいシナリオでは、NAT を使用して送信元インターフェースを選択できます。iptables などのツールを使用して、フローの送信元 IP を書き換え、cgroup、ユーザー、CPU などの送信アプリケーションのプロパティに基づいて特定のインターフェースの IP に一致させることができます。
次の例では、CPU ベースのルールを使用しています。最終的に、特定の CPU で実行されているスレッドから発信されたフローは、その CPU の対応する NUMA ノードに接続されている gVNIC によって転送されます。
# --- Begin Configuration ---
OUTPUT_INTERFACE_0="enp0s19" # CHANGEME: NIC0
OUTPUT_INTERFACE_1="enp192s20" # CHANGEME: NIC1
CPUS_0=($(seq 0 55; seq 112 167)) # CHANGEME: CPU IDs for NIC0
GATEWAY_0="10.0.0.1" # CHANGEME: Gateway for NIC0
SNAT_IP_0="10.0.0.2" # CHANGEME: SNAT IP for NIC0
CONNMARK_0="0x1"
RT_TABLE_0="100"
CPUS_1=($(seq 56 111; seq 168 223)) # CHANGEME: CPU IDs for NIC1
GATEWAY_1="10.0.1.1" # CHANGEME: Gateway for NIC1
SNAT_IP_1="10.0.1.2" # CHANGEME: SNAT IP for NIC1
CONNMARK_1="0x2"
RT_TABLE_1="101"
# --- End Configuration ---
# This informs which interface to use for packets in each table.
ip route add default via "$GATEWAY_0" dev "$OUTPUT_INTERFACE_0" table "$RT_TABLE_0"
ip route add default via "$GATEWAY_1" dev "$OUTPUT_INTERFACE_1" table "$RT_TABLE_1"
# This is not required for connections we originate, but replies to
# connections from peers need to know which interface to egress from.
# Add it before the fwmark rules to implicitly make sure fwmark takes precedence.
ip rule add from "$SNAT_IP_0" table "$RT_TABLE_0"
ip rule add from "$SNAT_IP_1" table "$RT_TABLE_1"
# This informs which table to use based on the packet mark set in OUTPUT.
ip rule add fwmark "$CONNMARK_0" table "$RT_TABLE_0"
ip rule add fwmark "$CONNMARK_1" table "$RT_TABLE_1"
# Relax reverse path filtering.
# Otherwise, we will drop legitimate replies to the SNAT IPs.
sysctl -w net.ipv4.conf."$OUTPUT_INTERFACE_0".rp_filter=2
sysctl -w net.ipv4.conf."$OUTPUT_INTERFACE_1".rp_filter=2
# Mark packets/connections with a per-nic mark based on the source CPU.
# The `fwmark` rules will then use the corresponding routing table for this traffic.
for cpu_id in "${CPUS_0[@]}"; do
iptables -t mangle -A OUTPUT -m state --state NEW -m cpu --cpu "$cpu_id" -j CONNMARK --set-mark "$CONNMARK_0"
iptables -t mangle -A OUTPUT -m state --state NEW -m cpu --cpu "$cpu_id" -j MARK --set-mark "$CONNMARK_0"
done
for cpu_id in "${CPUS_1[@]}"; do
iptables -t mangle -A OUTPUT -m state --state NEW -m cpu --cpu "$cpu_id" -j CONNMARK --set-mark "$CONNMARK_1"
iptables -t mangle -A OUTPUT -m state --state NEW -m cpu --cpu "$cpu_id" -j MARK --set-mark "$CONNMARK_1"
done
# For established connections, restore the connection mark.
# Otherwise, we will send the packet to the wrong NIC, depending on existing
# routing rules.
iptables -t mangle -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j CONNMARK --restore-mark
# These rules NAT the source address after the packet is already destined to
# egress the correct interface. This lets replies to this flow target the correct NIC,
# and may be required to be accepted into the VPC.
iptables -t nat -A POSTROUTING -m mark --mark "$CONNMARK_0" -j SNAT --to-source "$SNAT_IP_0"
iptables -t nat -A POSTROUTING -m mark --mark "$CONNMARK_1" -j SNAT --to-source "$SNAT_IP_1"