Einige accelerator-optimierte Maschinen, darunter A3 Ultra, A4 und A4X, haben zusätzlich zu den MRDMA-Schnittstellen auf diesen Maschinen zwei Host-Netzwerkschnittstellen. Auf dem Host sind das Titanium-IPUs, die in separate CPU-Sockets und NUMA-Knoten (Non-Uniform Memory Access) eingesteckt sind. Diese IPUs sind in der VM verfügbar und bieten als Google Virtual NICs (gVNICs) Netzwerkbandbreite für Speicheraktivitäten wie das Erstellen von Checkpoints, das Laden von Trainingsdaten und Modellen sowie andere allgemeine Netzwerkanforderungen. Die NUMA-Topologie der Maschine, einschließlich der gVNICs, ist für das Gastbetriebssystem sichtbar.
In diesem Dokument werden Best Practices für die Verwendung der beiden gVNICs auf diesen Maschinen beschrieben.
Übersicht
Im Allgemeinen empfehlen wir die folgenden Konfigurationen, unabhängig davon, wie Sie mehrere Host-NICs verwenden möchten:
- Netzwerkeinstellungen:Jede gVNIC muss ein eindeutiges VPC-Netzwerk haben. Berücksichtigen Sie bei der Einrichtung einer VPC Folgendes:
- Verwenden Sie für jedes VPC-Netzwerk eine große maximale Übertragungseinheit (MTU). 8.896 ist die maximal unterstützte MTU und wird empfohlen. Die Ingress-Leistung für einige Arbeitslasten kann verlangsamt werden, da das System eingehende Datenpakete auf der Empfängerseite verwirft. Mit dem Tool „ethtool“ können Sie prüfen, ob dieses Problem vorliegt. In diesem Fall kann es hilfreich sein, die TCP-MSS, die MTU der Schnittstelle oder die VPC-MTU anzupassen, um eine effiziente Datenzuweisung aus dem Seitencache zu ermöglichen. So kann der eingehende Layer-2-Frame in zwei 4-KB-Puffer passen.
- Anwendungseinstellungen
- Richten Sie die Anwendung an NUMA aus. Verwenden Sie CPU-Kerne, Arbeitsspeicherzuweisungen und eine Netzwerkschnittstelle vom selben NUMA-Knoten. Wenn Sie eine dedizierte Instanz der Anwendung ausführen, um einen bestimmten NUMA-Knoten oder eine bestimmte Netzwerkschnittstelle zu verwenden, können Sie mit Tools wie
numactl
die CPU- und Speicherressourcen der Anwendung an einen bestimmten NUMA-Knoten anhängen.
- Richten Sie die Anwendung an NUMA aus. Verwenden Sie CPU-Kerne, Arbeitsspeicherzuweisungen und eine Netzwerkschnittstelle vom selben NUMA-Knoten. Wenn Sie eine dedizierte Instanz der Anwendung ausführen, um einen bestimmten NUMA-Knoten oder eine bestimmte Netzwerkschnittstelle zu verwenden, können Sie mit Tools wie
- Betriebssystemeinstellungen
- Aktivieren Sie TSO (TCP Segmentation Offload) und LRO (Large Receive Offload).
- Achten Sie bei jeder gVNIC-Schnittstelle darauf, dass die SMP-Affinität so eingerichtet ist, dass die Interruptanfragen (IRQs) auf demselben NUMA-Knoten wie die Schnittstelle verarbeitet werden und die Interrupts auf die Kerne verteilt werden. Wenn Sie ein von Google bereitgestelltes Gastbetriebssystem-Image verwenden, erfolgt dieser Vorgang automatisch über das
google_set_multiqueue
-Skript. - Prüfen Sie, ob Einstellungen wie RFS, RPS und XPS für Ihre Arbeitslast hilfreich sein könnten.
- Für A4X empfiehlt Nvidia, die automatische NUMA-Planung zu deaktivieren.
- Die Linux-Kernel-Bonding-Funktion wird für die gVNICs auf diesen Maschinen nicht unterstützt.
Muster für die Verwendung mehrerer Host-NICs
In diesem Abschnitt werden allgemeine Muster für die Verwendung mehrerer Host-NICs aufGoogle Cloudbeschrieben.
Unterstützte Bereitstellungspfade | |||||
---|---|---|---|---|---|
Muster | Unterstütztes Prozesslayout | GCE (allgemein) | GKE | SLURM | Hinweise |
Anwendung so ändern, dass sie eine bestimmte Schnittstelle verwendet | Shard pro Schnittstelle verarbeiten | ✅ | ✅ | ✅ | Erfordert Codeänderungen an der Anwendung |
Anwendung so ändern, dass beide Schnittstellen verwendet werden | Dual-Interface-Prozess | ✅ | ✅ | ✅ | Erfordert Codeänderungen an der Anwendung |
Dedizierten Netzwerk-Namespace für bestimmte Anwendungen verwenden | Shard pro Schnittstelle verarbeiten | ✅ | ✅ (nur privilegierte Container) | ⛔ | |
Traffic eines gesamten Containers einer einzelnen Schnittstelle zuordnen | Der gesamte Container-Traffic wird einer Schnittstelle zugeordnet. | ✅ | ✅ | ⛔ | |
VPCs per Peering verbinden und das System Sitzungen über Schnittstellen hinweg ausgleichen lassen | Dual-Interface-Prozess | ✅* | ✅* | ✅* | Schwierig oder unmöglich, NUMA-kompatibel zu sein. Erfordert Linux-Kernel 6.16 oder höher* |
Traffic auf mehrere Netzwerke aufteilen | Prozess mit zwei Schnittstellen – Prozess-Shard pro Schnittstelle | ✅* | ✅* | ✅* | Erfordert möglicherweise Codeänderungen für die NUMA-Ausrichtung, wenn ein Dual-Interface-Prozess ausgeführt wird. |
SNAT verwenden, um die Quellschnittstelle auszuwählen | Prozess mit zwei Schnittstellen – Prozess-Shard pro Schnittstelle | ✅ | ✅ (Einrichtung erfordert Administratorberechtigungen) | ✅ (Einrichtung erfordert Administratorberechtigungen) | Die richtige Konfiguration kann schwieriger sein. |
* Diese Option wird im Allgemeinen nicht empfohlen, kann aber für begrenzte Arbeitslasten auf x86-Plattformen (A3 Ultra und A4) nützlich sein.
Anwendung so ändern, dass sie eine bestimmte Schnittstelle verwendet
Voraussetzungen:
- Für diese Methode sind Codeänderungen an Ihrer Anwendung erforderlich.
- Erfordert Berechtigungen für eine oder mehrere der folgenden Methoden:
- Für
bind()
sind nur dann spezielle Berechtigungen erforderlich, wenn ein privilegierter Quellport verwendet wird. SO_BINDTODEVICE
: Erfordert die BerechtigungCAP_NET_RAW
.
- Für
- Bei dieser Methode müssen Sie möglicherweise die Kernel-Routingtabelle ändern, um Routen einzurichten und asymmetrisches Routing zu verhindern.
Übersicht
Mit diesem Muster führen Sie Folgendes aus:
- Fügen Sie dem Quellcode Ihrer Anwendung die Bindung der Netzwerkschnittstelle hinzu. Verwenden Sie dazu eine der folgenden Optionen:
- Mit
bind()
kann ein Socket an eine bestimmte Quell-IP-Adresse gebunden werden. - Verwenden Sie die Socket-Option
SO_BINDTODEVICE
, um einen Socket an eine bestimmte Netzwerkschnittstelle zu binden.
- Mit
- Ändern Sie die Kernel-Routingtabelle nach Bedarf, um sicherzustellen, dass eine Route von der Quellnetzwerkschnittstelle zur Zieladresse vorhanden ist. Außerdem sind möglicherweise Routen erforderlich, um asymmetrisches Routing zu verhindern. Wir empfehlen, das Richtlinienrouting zu konfigurieren, wie unter Routing für eine zusätzliche Netzwerkschnittstelle konfigurieren beschrieben.
- Sie können Ihre Anwendung auch mit dem Befehl
numactl
ausführen. Bei diesem Ansatz verwenden Sie den Arbeitsspeicher und die CPUs, die sich auf demselben NUMA-Knoten wie die ausgewählte Netzwerkschnittstelle befinden.
Nachdem Sie die vorherigen Schritte ausgeführt haben, werden Instanzen Ihrer Anwendung über eine bestimmte Netzwerkschnittstelle ausgeführt.
Anwendung so ändern, dass beide Schnittstellen verwendet werden
Voraussetzungen:
- Für diese Methode sind Codeänderungen an Ihrer Anwendung erforderlich.
- Sie benötigen Berechtigungen für eine oder mehrere der folgenden Methoden:
- Für
bind()
sind nur dann spezielle Berechtigungen erforderlich, wenn ein privilegierter Quellport verwendet wird. SO_BINDTODEVICE
: Erfordert die BerechtigungCAP_NET_RAW
.
- Für
- Bei dieser Methode müssen Sie möglicherweise die Kernel-Routingtabelle ändern, um Routen einzurichten und asymmetrisches Routing zu verhindern.
Übersicht
So implementieren Sie dieses Muster:
- Fügen Sie dem Quellcode Ihrer Anwendung die Bindung der Netzwerkschnittstelle hinzu. Verwenden Sie dazu eine der folgenden Optionen:
- Mit dem Systemaufruf
bind()
einen Socket an eine bestimmte Quell-IP-Adresse binden - Verwenden Sie die Socket-Option
SO_BINDTODEVICE
, um einen Socket an eine bestimmte Netzwerkschnittstelle zu binden.
- Mit dem Systemaufruf
- Wenn Ihre Anwendung als Client fungiert, müssen Sie für jede Quellnetzwerkschnittstelle einen separaten Client-Socket erstellen.
- Ändern Sie die Kernel-Routingtabelle nach Bedarf, um sicherzustellen, dass eine Route von der Quellnetzwerkschnittstelle zur Zieladresse vorhanden ist. Außerdem benötigen Sie möglicherweise Routen, um asymmetrisches Routing zu verhindern. Wir empfehlen, das Richtlinienrouting wie unter Routing für eine zusätzliche Netzwerkschnittstelle konfigurieren beschrieben zu konfigurieren.
- Wir empfehlen, die Netzwerkaktivität in Threads aufzuteilen, die auf demselben NUMA-Knoten wie die gVNIC-Schnittstelle ausgeführt werden. Eine gängige Methode zum Anfordern eines bestimmten NUMA-Knotens für einen Thread ist der Aufruf von
pthread_setaffinity_np
.- Da die Anwendung Ressourcen auf mehreren NUMA-Knoten verwendet, sollten Sie
numactl
nicht verwenden oder dafür sorgen, dass Ihrnumactl
-Befehl die NUMA-Knoten aller Netzwerkschnittstellen enthält, die von Ihrer Anwendung verwendet werden.
- Da die Anwendung Ressourcen auf mehreren NUMA-Knoten verwendet, sollten Sie
Dedizierten Netzwerk-Namespace für bestimmte Anwendungen verwenden
Voraussetzungen:
- Erfordert
CAP_SYS_ADMIN
-Funktion. - Nicht mit GKE Autopilot kompatibel.
- Wenn Sie GKE verwenden, benötigen Sie einen privilegierten Container.
In diesem Abschnitt werden Muster beschrieben, mit denen Sie einen Netzwerk-Namespace erstellen können, der eine sekundäre Netzwerkschnittstelle verwendet. Das richtige Muster für Ihre Arbeitslast hängt von Ihrem jeweiligen Szenario ab. Die Ansätze mit virtuellem Switch oder IPvlan eignen sich besser für Fälle, in denen mehrere Anwendungen die sekundäre Schnittstelle aus verschiedenen Netzwerk-Namespaces verwenden müssen.
Übersicht: Sekundäre Schnittstelle in einen dedizierten Netzwerk-Namespace verschieben
Bei diesem Muster wird ein Netzwerk-Namespace erstellt, die sekundäre gVNIC-Schnittstelle in den neuen Namespace verschoben und die Anwendung dann in diesem Namespace ausgeführt. Dieses Muster ist möglicherweise einfacher einzurichten und zu optimieren als die Verwendung eines virtuellen Switches. Anwendungen außerhalb des neuen Netzwerk-Namespace können jedoch nicht auf die sekundäre gVNIC zugreifen.
Das folgende Beispiel zeigt eine Reihe von Befehlen, mit denen eth1 in den neuen Netzwerk-Namespace „second“ verschoben werden kann.
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>
Wenn dieser Befehl ausgeführt wird, wird der Ausdruck <command> im Netzwerk-Namespace ausgeführt und verwendet die eth1-Schnittstelle.
Anwendungen, die im neuen Netzwerk-Namespace ausgeführt werden, verwenden jetzt das sekundäre gVNIC. Mit dem Befehl numactl
können Sie Ihre Anwendung auch mit dem Arbeitsspeicher und den CPUs ausführen, die sich auf demselben NUMA-Knoten wie die ausgewählte Netzwerkschnittstelle befinden.
Übersicht: Virtuellen Switch und Netzwerk-Namespace für eine sekundäre Schnittstelle verwenden Bei diesem Muster wird ein virtueller Switch eingerichtet, um die sekundäre gVNIC aus einem Netzwerk-Namespace zu verwenden.
Es müssen allgemein folgende Schritte ausgeführt werden:
- Erstellen Sie ein Paar virtueller Ethernet-Geräte (veth). Passen Sie die maximale Übertragungseinheit (Maximum Transmission Unit, MTU) auf jedem der Geräte an die MTU des sekundären gVNIC an.
- Führen Sie den folgenden Befehl aus, um sicherzustellen, dass die IP-Weiterleitung für IPv4 aktiviert ist: sysctl -w net.ipv4.ip_forward=1
- Verschieben Sie ein Ende des veth-Paars in einen neuen Netzwerk-Namespace und lassen Sie das andere Ende im Root-Namespace.
- Ordnen Sie Traffic vom veth-Gerät der sekundären gVNIC-Schnittstelle zu. Dafür gibt es mehrere Möglichkeiten. Wir empfehlen jedoch, einen IP-Aliasbereich für die sekundäre Schnittstelle der VM zu erstellen und der untergeordneten Schnittstelle im Namespace eine IP-Adresse aus diesem Bereich zuzuweisen.
- Führen Sie die Anwendung über den neuen Netzwerk-Namespace aus. Mit dem Befehl
numactl
können Sie Ihre Anwendung mit Arbeitsspeicher und CPUs ausführen, die sich auf demselben NUMA-Knoten wie die ausgewählte Netzwerkschnittstelle befinden.
Je nach Gast- und Arbeitslastkonfiguration können Sie alternativ den IPvlan-Treiber mit einer IPvlan-Schnittstelle verwenden, die mit dem sekundären gVNIC verknüpft ist, anstatt die veth-Geräte zu erstellen.
Traffic eines gesamten Containers einer einzelnen Schnittstelle zuordnen
Voraussetzungen:
- Ihre Anwendung muss in einem Container ausgeführt werden, der einen Netzwerk-Namespace für die Containernetzwerkfunktionen verwendet, z. B. GKE, Docker oder Podman. Sie können das Hostnetzwerk nicht verwenden.
Viele Containertechnologien wie GKE, Docker und Podman verwenden einen dedizierten Netzwerk-Namespace für einen Container, um den Traffic zu isolieren. Dieser Netzwerk-Namespace kann dann direkt oder mithilfe der Tools der Containertechnologie geändert werden, um den Traffic einer anderen Netzwerkschnittstelle zuzuordnen.
Für die Kubernetes-interne Kommunikation ist in GKE die primäre Schnittstelle erforderlich. Die Standardroute im Pod kann jedoch so geändert werden, dass die sekundäre Schnittstelle verwendet wird, wie im folgenden GKE-Pod-Manifest gezeigt.
metadata:
…
annotations:
networking.gke.io/default-interface: 'eth1'
networking.gke.io/interfaces: |
[
{"interfaceName":"eth0","network":"default"},
{"interfaceName":"eth1","network":"secondary-network"},
]
Bei diesem Ansatz ist keine NUMA-Ausrichtung zwischen der Standardnetzwerkschnittstelle und CPUs oder Arbeitsspeicher garantiert.
VPCs per Peering verbinden und das System Sitzungen über Schnittstellen hinweg ausgleichen lassen
Voraussetzungen:
- VPC-Peering muss zwischen den VPCs der primären und sekundären gVNICs eingerichtet werden.
- Die Linux-Kernelversion 6.16 ist erforderlich, um TCP-Sitzungen über Quellschnittstellen hinweg auszugleichen, wenn sie an eine einzelne Ziel-IP-Adresse und einen einzelnen Zielport gesendet werden.
- Die Arbeitslast kann Ihre Leistungsanforderungen weiterhin erfüllen, wenn der Netzwerk-Stack sockelübergreifende Speicherübertragungen generiert.
Übersicht
In einigen Fällen ist es schwierig, Netzwerkverbindungen innerhalb einer Anwendung oder zwischen Instanzen einer Anwendung zu sharden. In diesem Szenario kann es für einige Anwendungen, die auf A3U- oder A4-VMs ausgeführt werden und nicht empfindlich auf NUMA- oder Socket-übergreifende Übertragungen reagieren, sinnvoll sein, die beiden Schnittstellen als austauschbar zu behandeln.
Eine Möglichkeit hierfür ist die Verwendung des Sysctl-Parameters „fib_multipath_hash_policy“ und einer Multipath-Route:
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}
Traffic auf mehrere Netzwerke aufteilen
Voraussetzungen:
nic0
undnic1
auf der VM befinden sich in separaten VPCs und Subnetzen. Für dieses Muster müssen die Zieladressen auf die VPCs vonnic0
undnic1
verteilt werden.
Übersicht
Standardmäßig erstellt der Linux-Kernel Routen für das Subnetz von nic0
und das Subnetz von nic1
, über die der Traffic nach Ziel über die entsprechende Netzwerkschnittstelle weitergeleitet wird.
Angenommen, nic0
verwendet die VPC net1
mit dem Subnetz subnet-a
und nic1
verwendet die VPC net2
mit dem Subnetz subnet-b
. Standardmäßig wird für die Kommunikation mit Peer-IP-Adressen in subnet-a
nic0
und für die Kommunikation mit Peer-IP-Adressen in subnet-b
nic1
verwendet. Dieses Szenario kann beispielsweise bei einer Gruppe von Peer-VMs mit einer einzelnen NIC auftreten, die mit net1
verbunden sind, und einer Gruppe, die mit net2
verbunden ist.
SNAT zum Auswählen der Quellschnittstelle verwenden
Voraussetzungen:
CAP_NET_ADMIN
ist für die Einrichtung der ersten iptables-Regeln erforderlich, nicht jedoch für die Ausführung der Anwendung.- Sie müssen Regeln sorgfältig prüfen, wenn Sie sie in Kombination mit anderen nicht trivialen iptables-Regeln oder Routingkonfigurationen verwenden.
Hinweis:
- Die NIC-Bindung ist nur zum Zeitpunkt der Verbindungsherstellung korrekt. Wenn ein Thread zu einer CPU verschoben wird, die einem anderen NUMA-Knoten zugeordnet ist, wird die Verbindung durch NUMA-übergreifende Strafen beeinträchtigt. Daher ist diese Lösung am nützlichsten, wenn es einen Mechanismus gibt, um Threads an bestimmte CPU-Sets zu binden.
- Nur Verbindungen, die von diesem Computer ausgehen, werden an eine bestimmte Netzwerkkarte gebunden. Eingehende Verbindungen werden der NIC zugeordnet, die der Zieladresse entspricht.
Übersicht
In Szenarien, in denen es schwierig ist, Netzwerk-Namespaces zu verwenden oder Anwendungsänderungen vorzunehmen, können Sie NAT verwenden, um eine Quellschnittstelle auszuwählen. Sie können Tools wie iptables verwenden, um die Quell-IP für einen Flow so umzuschreiben, dass sie der IP einer bestimmten Schnittstelle entspricht. Dies kann auf Grundlage einer Eigenschaft der sendenden Anwendung erfolgen, z. B. cgroup, Nutzer oder CPU.
Im folgenden Beispiel werden CPU-basierte Regeln verwendet. Das Endergebnis ist, dass ein Flow, der von einem Thread stammt, der auf einer beliebigen CPU ausgeführt wird, von der gVNIC übertragen wird, die an den entsprechenden NUMA-Knoten dieser CPU angehängt ist.
# --- 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"