部分加速器最佳化機器 (包括 A3 Ultra、A4 和 A4X) 除了機器上的 MRDMA 介面外,還有兩個主機網路介面。在主機上,這些是 Titanium IPU,會插入獨立的 CPU 插槽和非一致性記憶體存取 (NUMA) 節點。這些 IPU 可在 VM 內以 Google 虛擬 NIC (gVNIC) 的形式使用,並為儲存活動提供網路頻寬,例如檢查點、載入訓練資料、載入模型,以及其他一般網路需求。訪客作業系統 (OS) 可以看到機器的 NUMA 拓撲,包括 gVNIC 的拓撲。
本文說明在這些機器上使用兩個 gVNIC 的最佳做法。
總覽
一般來說,無論您打算如何使用多個主機 NIC,都建議採用下列設定:
- 網路設定: 每個 gVNIC 都必須有專屬的 VPC 網路。如要設定虛擬私有雲,請考慮下列事項:
- 為每個虛擬私有雲網路使用較大的最大傳輸單元 (MTU)。8896 是支援的 MTU 上限,建議選擇這個值。 系統可能會在接收端捨棄傳入的資料封包,導致部分工作負載的傳入效能降低。您可以使用 ethtool 工具檢查這個問題。在此情境下,調整 TCP MSS、介面 MTU 或 VPC MTU 可能會有幫助,可讓網頁快取有效分配資料,進而讓傳入的第 2 層影格符合兩個 4 KB 緩衝區。
- 應用程式設定
- NUMA 對齊應用程式。使用來自相同 NUMA 節點的 CPU 核心、記憶體配置和網路介面。如果您執行應用程式的專屬執行個體,以使用特定 NUMA 節點或網路介面,可以使用
numactl
等工具,將應用程式的 CPU 和記憶體資源附加至特定 NUMA 節點。
- NUMA 對齊應用程式。使用來自相同 NUMA 節點的 CPU 核心、記憶體配置和網路介面。如果您執行應用程式的專屬執行個體,以使用特定 NUMA 節點或網路介面,可以使用
- 作業系統設定
- 啟用 TCP 區隔卸載 (TSO) 和大型接收卸載 (LRO)。
- 請為每個 gVNIC 介面設定 SMP 親和性,確保介面的中斷要求 (IRQ) 與介面在同一個 NUMA 節點上處理,並將中斷分散到各個核心。如果您執行的是 Google 提供的客體 OS 映像檔,系統會使用
google_set_multiqueue
指令碼自動執行這項程序。 - 評估 RFS、RPS 和 XPS 等設定,看看這些設定是否對您的工作負載有幫助。
- 對於 A4X,Nvidia 建議停用自動 NUMA 排程。
- 這些機器上的 gVNIC 不支援 Linux 核心繫結。
使用多個主機 NIC 的模式
本節概述在Trusted Cloud by S3NS上使用多個主機 NIC 的一般模式。
支援的部署路徑 | |||||
---|---|---|---|---|---|
模式 | 支援的流程版面配置 | GCE (一般) | GKE | SLURM | 附註 |
變更應用程式,使用特定介面 | 每個介面的程序分片 | ✅ | ✅ | ✅ | 需要變更應用程式的程式碼 |
變更應用程式,同時使用這兩個介面 | 雙介面程序 | ✅ | ✅ | ✅ | 需要變更應用程式的程式碼 |
為特定應用程式使用專屬網路命名空間 | 每個介面的程序分片 | ✅ | ✅ (僅限特殊權限容器) | ⛔ | |
將整個容器的流量對應至單一介面 | 所有容器流量都對應至一個介面 | ✅ | ✅ | ⛔ | |
對等互連虛擬私有雲,讓系統在介面間平衡負載工作階段 | 雙介面程序 | ✅* | ✅* | ✅* | 難以或無法進行 NUMA 對齊 需要 Linux 核心 6.16 以上版本* |
在各個網路之間分攤流量 | 雙介面程序:每個介面的程序分片 | ✅* | ✅* | ✅* | 如果執行雙介面程序,可能需要變更程式碼,才能與 NUMA 對齊。 |
使用 SNAT 選擇來源介面 | 雙介面程序:每個介面的程序分片 | ✅ | ✅ (設定時需要管理員權限) | ✅ (設定時需要管理員權限) | 可能較難正確設定 |
* 一般來說,我們不建議使用這個選項,但對於 x86 (A3 Ultra 和 A4) 平台上的有限工作負載,這個選項可能很有用。
變更應用程式以使用特定介面
必要操作:
- 這個方法需要變更應用程式的程式碼。
- 需要下列一或多個方法的權限:
bind()
只有在使用特殊權限來源通訊埠時,才需要特殊權限。SO_BINDTODEVICE
:需要CAP_NET_RAW
權限。
- 這個方法可能需要修改核心轉送表,才能建立路徑並防止非對稱轉送。
高階總覽
使用這個模式,您可以完成下列事項:
- 使用下列其中一個選項,將網路介面繫結新增至應用程式的原始碼:
- 使用
bind()
將通訊端繫結至特定來源 IP 位址 - 使用
SO_BINDTODEVICE
通訊端選項,將通訊端繫結至特定網路介面
- 使用
- 視需要修改核心路由表,確保從來源網路介面到目的地地址的路徑存在。此外,可能需要路徑來避免不對稱轉送。建議您按照「為額外網路介面設定路由」一文所述,設定政策路由。
- 您也可以使用
numactl
指令執行應用程式。這個方法會使用與所選網路介面位於相同 NUMA 節點的記憶體和 CPU。
完成上述步驟後,應用程式執行個體就會使用特定網路介面執行。
變更應用程式,同時使用兩個介面
必要操作:
- 這個方法需要變更應用程式的程式碼。
- 您需要下列一或多個方法的權限:
bind()
只有在使用特殊權限來源通訊埠時,才需要特殊權限。SO_BINDTODEVICE
:需要CAP_NET_RAW
權限。
- 這個方法可能需要修改核心轉送表,才能建立路徑並防止非對稱轉送。
高階總覽
如要實作這個模式,請執行下列操作:
- 使用下列其中一個選項,將網路介面繫結新增至應用程式的原始碼:
- 使用
bind()
系統呼叫,將通訊端繫結至特定來源 IP 位址 - 使用
SO_BINDTODEVICE
通訊端選項,將通訊端繫結至特定網路介面
- 使用
- 如果應用程式是做為用戶端,您需要為每個來源網路介面建立個別的用戶端通訊端。
- 視需要修改核心路由表,確保從來源網路介面到目的地地址的路徑存在。此外,您可能也需要路徑來避免非對稱路徑。建議您按照「為額外網路介面設定路由」一文的說明,設定政策路由。
- 建議您將網路活動分割成執行於與 gVNIC 介面相同 NUMA 節點的執行緒。如要為執行緒要求特定 NUMA 節點,常見做法是呼叫
pthread_setaffinity_np
。- 由於應用程式會使用多個 NUMA 節點上的資源,請避免使用
numactl
,或確保numactl
指令包含應用程式使用的所有網路介面的 NUMA 節點。
- 由於應用程式會使用多個 NUMA 節點上的資源,請避免使用
為特定應用程式使用專屬網路命名空間
必要操作:
- 需要
CAP_SYS_ADMIN
功能。 - 與 GKE Autopilot 不相容。
- 如果使用 GKE,您必須擁有特殊權限容器。
本節說明可用於建立網路命名空間的模式,該命名空間會使用次要網路介面。工作負載適用的模式取決於您的具體情境。使用虛擬交換器或 IPvlan 的方法,更適合多個應用程式需要使用不同網路命名空間的次要介面。
高階總覽:將次要介面移至專屬網路命名空間
這個模式包括建立網路命名空間、將次要 gVNIC 介面移至新命名空間,然後從這個命名空間執行應用程式。與使用虛擬交換器相比,這個模式的設定和調整可能較不複雜。不過,新網路命名空間以外的應用程式將無法存取次要 gVNIC。
下列範例顯示一系列指令,可用於將 eth1 移至名為「second」的新網路命名空間。
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 介面。
在新網路命名空間中執行的應用程式現在會使用次要 gVNIC。您也可以使用 numactl
指令,透過與所選網路介面位於相同 NUMA 節點的記憶體和 CPU,執行應用程式。
高階總覽:使用虛擬交換器和網路命名空間做為次要介面。這個模式涉及建立虛擬交換器設定,以便從網路命名空間使用次要 gVNIC。
高階步驟如下所述:
- 建立虛擬乙太網路 (veth) 裝置配對。調整每部裝置的最大傳輸單位 (MTU),使其與次要 gVNIC 的 MTU 相符。
- 執行下列指令,確保已啟用 IPv4 的 IP 轉送功能:sysctl -w net.ipv4.ip_forward=1
- 將 veth 配對的一端移至新的網路命名空間,另一端則留在根命名空間。
- 將 veth 裝置的流量對應至次要 gVNIC 介面。您可以透過多種方式達成這個目標,但建議您為 VM 的次要介面建立 IP 別名範圍,並從這個範圍將 IP 位址指派給命名空間中的子項介面。
- 從新的網路命名空間執行應用程式。您可以使用
numactl
指令,透過與所選網路介面位於相同 NUMA 節點的記憶體和 CPU 執行應用程式。
視訪客和工作負載設定而定,您也可以使用 IPvlan 驅動程式,並將 IPvlan 介面連結至次要 gVNIC,而不需建立 veth 裝置。
將整個容器的流量對應至單一介面
必要操作:
- 您的應用程式必須在容器內執行,且該容器使用容器網路的網路命名空間,例如 GKE、Docker 或 Podman。您無法使用主機網路。
許多容器技術 (例如 GKE、Docker 和 Podman) 都會為容器使用專屬的網路命名空間,藉此隔離容器的流量。然後,可以直接修改這個網路命名空間,也可以使用容器技術的工具,將流量對應至其他網路介面。
GKE 規定 Kubernetes 內部通訊必須使用主要介面。不過,Pod 中的預設路徑可以變更為使用次要介面,如下列 GKE 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 的虛擬私有雲之間必須建立虛擬私有雲對等互連。
- 如要將 TCP 工作階段的負載平衡分配至來源介面,並傳送至單一目的地 IP 和通訊埠,則必須使用 Linux 核心 6.16 版。
- 即使網路堆疊產生跨插槽記憶體傳輸,工作負載仍可滿足效能需求。
高階總覽
在某些情況下,應用程式內或應用程式執行個體之間的網路連線很難分片。在這種情況下,對於在 A3U 或 A4 VM 上執行的某些應用程式,如果對跨 NUMA 或跨插槽轉移不敏感,則可將這兩個介面視為可互換。
如要達成此目的,其中一種方法是使用 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}
在各個網路之間分片流量
必要操作:
nic0
和nic1
位於不同的虛擬私有雲和子網路。這個模式需要將目的地位址分散到nic0
和nic1
的虛擬私有雲。
高階總覽
根據預設,Linux 核心會為 nic0
的子網路和 nic1
的子網路建立路徑,將流量依目的地透過適當的網路介面轉送。
舉例來說,假設 nic0
使用的 VPC 是 net1
,子網路是 subnet-a
,而 nic1
使用的 VPC 是 net2
,子網路是 subnet-b
。根據預設,與 subnet-a
中對等互連 IP 位址的通訊會使用 nic0
,與 subnet-b
中對等互連 IP 位址的通訊則會使用 nic1
。舉例來說,如果一組對等互連的單一 NIC VM 連線至 net1
,另一組連線至 net2
,就可能發生這種情況。
使用 SNAT 選擇來源介面
必要操作:
- 設定初始 iptables 規則時需要
CAP_NET_ADMIN
,但執行應用程式時則不需要。 - 如果規則與其他非簡單的 iptables 規則或轉送設定搭配使用,請務必仔細評估。
注意:
- NIC 繫結只在建立連線時正確無誤。如果執行緒移至與不同 NUMA 節點相關聯的 CPU,連線就會受到跨 NUMA 的懲罰。因此,如果某種機制可將執行緒繫結至特定 CPU 集,這個解決方案就最實用。
- 只有這部電腦發起的連線會繫結至特定 NIC。 傳入連線會與 NIC 相關聯,並與連線目的地相符。
高階總覽
如果難以使用網路命名空間或進行應用程式變更,可以使用 NAT 選取來源介面。您可以根據傳送應用程式的屬性 (例如 cgroup、使用者或 CPU),使用 iptables 等工具重新編寫流程的來源 IP,以符合特定介面的 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"