A3 Ultra, A4, A4X를 비롯한 일부 가속기 최적화 머신에는 이러한 머신의 MRDMA 인터페이스 외에도 호스트 네트워크 인터페이스가 두 개 있습니다. 호스트에서 이러한 항목은 별도의 CPU 소켓과 비균일 메모리 액세스 (NUMA) 노드에 연결된 Titanium IPU입니다. 이러한 IPU는 VM 내에서 Google 가상 NIC(gVNIC)로 제공되며, 체크포인트, 학습 데이터 로드, 모델 로드, 기타 일반적인 네트워킹 요구사항과 같은 스토리지 활동을 위한 네트워크 대역폭을 제공합니다. gVNIC를 포함한 머신의 NUMA 토폴로지가 게스트 운영체제 (OS)에 표시됩니다.
이 문서에서는 이러한 머신에서 두 gVNIC를 사용하는 권장사항을 설명합니다.
개요
일반적으로 여러 호스트 NIC를 사용하는 방법에 관계없이 다음 구성을 사용하는 것이 좋습니다.
- 네트워크 설정: 각 gVNIC에는 고유한 VPC 네트워크가 있어야 합니다. VPC 설정의 경우 다음을 고려하세요.
- 각 VPC 네트워크에 큰 최대 전송 단위 (MTU)를 사용합니다. 8896은 지원되는 최대 MTU이며 권장되는 선택입니다. 수신기 측에서 수신 데이터 패킷이 시스템에 의해 삭제되어 일부 워크로드의 수신 성능이 느려질 수 있습니다. ethtool 도구를 사용하여 이 문제를 확인할 수 있습니다. 이 시나리오에서는 수신되는 레이어 2 프레임이 4KB 버퍼 두 개에 맞도록 페이지 캐시에서 효율적인 데이터 할당을 허용하도록 TCP MSS, 인터페이스 MTU 또는 VPC MTU를 조정하는 것이 도움이 될 수 있습니다.
- 애플리케이션 설정
- 애플리케이션을 NUMA 정렬합니다. 동일한 NUMA 노드의 CPU 코어, 메모리 할당, 네트워크 인터페이스를 사용합니다. 특정 NUMA 노드나 네트워크 인터페이스를 사용하기 위해 애플리케이션의 전용 인스턴스를 실행하는 경우
numactl
와 같은 도구를 사용하여 애플리케이션의 CPU 및 메모리 리소스를 특정 NUMA 노드에 연결할 수 있습니다.
- 애플리케이션을 NUMA 정렬합니다. 동일한 NUMA 노드의 CPU 코어, 메모리 할당, 네트워크 인터페이스를 사용합니다. 특정 NUMA 노드나 네트워크 인터페이스를 사용하기 위해 애플리케이션의 전용 인스턴스를 실행하는 경우
- 운영체제 설정
- TCP 세그먼트 오프로드 (TSO) 및 Large Receive Offload (LRO)를 사용 설정합니다.
- 각 gVNIC 인터페이스에 대해 인터럽트 요청 (IRQ)이 인터페이스와 동일한 NUMA 노드에서 처리되고 인터럽트가 코어에 분산되도록 SMP 어피니티가 설정되어 있는지 확인합니다. Google 제공 게스트 OS 이미지를 실행하는 경우 이 프로세스는
google_set_multiqueue
스크립트를 사용하여 자동으로 실행됩니다. - RFS, RPS, XPS와 같은 설정을 평가하여 워크로드에 도움이 되는지 확인합니다.
- A4X의 경우 Nvidia는 자동 NUMA 스케줄링을 사용 중지할 것을 권장합니다.
- 이러한 머신에서는 gVNIC에 Linux 커널 결합이 지원되지 않습니다.
여러 호스트 NIC 사용 패턴
이 섹션에서는Google Cloud에서 여러 호스트 NIC를 사용하는 일반적인 패턴을 설명합니다.
지원되는 배포 경로 | |||||
---|---|---|---|---|---|
패턴 | 지원되는 프로세스 레이아웃 | GCE (일반) | GKE | SLURM | 참고 |
특정 인터페이스를 사용하도록 애플리케이션 변경 | 인터페이스당 샤드 처리 | ✅ | ✅ | ✅ | 애플리케이션의 코드 변경이 필요함 |
두 인터페이스를 모두 사용하도록 애플리케이션 변경 | 이중 인터페이스 프로세스 | ✅ | ✅ | ✅ | 애플리케이션의 코드 변경이 필요함 |
특정 애플리케이션에 전용 네트워크 네임스페이스 사용 | 인터페이스당 샤드 처리 | ✅ | ✅ (높은 권한을 가진 컨테이너만 해당) | ⛔ | |
전체 컨테이너의 트래픽을 단일 인터페이스에 매핑 | 하나의 인터페이스에 매핑된 모든 컨테이너 트래픽 | ✅ | ✅ | ⛔ | |
VPC를 피어링하고 시스템이 인터페이스 간에 세션의 부하를 분산하도록 허용 | 이중 인터페이스 프로세스 | ✅* | ✅* | ✅* | 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를 사용하여 애플리케이션을 실행할 수 있습니다.
게스트 및 워크로드 설정에 따라 veth 기기를 만드는 대신 보조 gVNIC에 연결된 IPvlan 인터페이스와 함께 IPvlan 드라이버를 사용할 수도 있습니다.
전체 컨테이너의 트래픽을 단일 인터페이스에 매핑
요구사항:
- 애플리케이션은 GKE, Docker, Podman과 같이 컨테이너 네트워킹을 위한 네트워크 네임스페이스를 사용하는 컨테이너 내에서 실행되어야 합니다. 호스트 네트워크를 사용할 수 없습니다.
GKE, Docker, Podman과 같은 많은 컨테이너 기술은 컨테이너의 트래픽을 격리하기 위해 컨테이너 전용 네트워크 네임스페이스를 사용합니다. 그런 다음 이 네트워크 네임스페이스를 직접 수정하거나 컨테이너 기술의 도구를 사용하여 트래픽을 다른 네트워크 인터페이스에 매핑할 수 있습니다.
GKE에서는 Kubernetes 내부 통신을 위해 기본 인터페이스가 있어야 합니다. 하지만 다음 GKE 포드 매니페스트와 같이 포드의 기본 경로를 변경하여 보조 인터페이스를 사용할 수 있습니다.
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의 VPC 간에 VPC 피어링이 설정되어야 합니다.
- 단일 대상 IP 및 포트로 전송하는 경우 소스 인터페이스 간에 TCP 세션의 부하를 분산하려면 Linux 커널 버전 6.16이 필요합니다.
- 네트워킹 스택에서 크로스 소켓 메모리 전송을 생성할 때도 워크로드가 성능 요구사항을 충족할 수 있습니다.
간단한 개요
경우에 따라 애플리케이션 내 또는 애플리케이션 인스턴스 간에 네트워크 연결을 샤딩하기 어려울 수 있습니다. 이 시나리오에서는 교차 NUMA 또는 교차 소켓 전송에 민감하지 않은 A3U 또는 A4 VM에서 실행되는 일부 애플리케이션의 경우 두 인터페이스를 대체 가능한 것으로 취급하는 것이 편리할 수 있습니다.
이를 달성하는 한 가지 방법은 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와 연결됩니다.
간단한 개요
네트워크 네임스페이스를 사용하거나 애플리케이션을 변경하기 어려운 시나리오에서는 NAT를 사용하여 소스 인터페이스를 선택할 수 있습니다. iptables와 같은 도구를 사용하여 cgroup, 사용자, CPU와 같은 전송 애플리케이션의 속성을 기반으로 특정 인터페이스의 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"