使用 GPUDirect-TCPX 和多网络功能最大限度地提高 GPU 网络带宽


本页面介绍如何在 Standard 模式下的 Google Kubernetes Engine (GKE) 集群中最大限度地提高高性能 GPU 工作负载的网络带宽和吞吐量。本页面适用于为机器学习 (ML) 工作负载提供支持的机器学习工程师和平台管理员。您应该已熟悉网络接口卡 (NIC) 和 TCP 等网络技术,以及 NVIDIA Collective Communications Library (NCCL) 等加速器技术。

人工智能 (AI)、机器学习和高性能计算 (HPC) 应用需要通过缩短作业完成时间来获得极大的加速,从而优化性能。例如,用于对话式 AI 和图像生成的机器学习模型需要强大的可伸缩性和计算能力。

Google Cloud GPU 超级计算机简介

Google Cloud 提供专为可伸缩的大型模型构建的加速器优化超级计算机。这种机器具有以下优势:

  • 每个机器搭载八个 NVIDIA H100 GPU。
  • 主 NIC 可支持最高 200 Gbps 的带宽。
  • 最多可具有四个次要 NIC,每个 NIC 可支持最高 200 Gbps 的 GPU 数据传输带宽。

如需查看完整的优势列表,请参阅 Compute Engine 文档中的 A3 机器系列

您的 GKE 工作负载必须使用单个节点上所有可用的 GPU 和所有可用的次要 NIC,并使用大部分的可用带宽。本文档中所述的解决方案非常适合需要高性能、高吞吐量和低延迟的工作负载。

最大限度地提高带宽所必需的特性和功能

如需最大限度地提高 GPU 超级计算机节点中的网络带宽,请使用以下所有功能:

  • GPUDirect-TCPX:减少与 GPU 传输数据包载荷所需的开销,与不使用 GPUDirect-TCPX 的 GPU 相比,可大幅提高吞吐量。
  • gVNIC:支持 GPUDirect-TCPX 功能,例如数据包标头拆分、流导向和缓冲区管理。使用 GPUDirect-TCPX 需要 gVNIC。如需详细了解 gVNIC,请参阅提高 GPU 节点的网络流量速度
  • 多网络:将次要 NIC 添加到加速器优化的机器。对于 A3 机器,添加四个额外的 NIC。为避免冲突,每个 NIC 都与其专属的 VPC 中的单独子网相关联。如需详细了解多网络支持,请参阅设置 Pod 的多网络支持
  • 布置政策:使用资源布置政策将特定工作负载的所有 GPU 节点放置在物理位置彼此靠近的服务器上,以最大限度地缩短延迟时间。如需了解详情,请参阅为 GKE 节点定义紧凑布置

流程概览

如需使用所有这些功能,您需要完成以下任务:

  1. 创建 Virtual Private Cloud (VPC) 和子网
  2. 创建 GKE 环境
    1. 创建启用多网络的集群
    2. 创建具有以下特征的节点池:
      1. 启用了 gVNIC
      2. 为每个次要 NIC 指定了多网络子网
      3. 节点使用搭载 H100 GPU(4 个次要 NIC 和 8 个 GPU)的 A3 机器系列
      4. 安装了最新 NVIDIA 驱动程序
  3. 安装 GPUDirect-TCPX 二进制文件和 NCCL 插件
  4. 部署测试工作负载以验证 GPUDirect-TCPX 设置

准备工作

在开始之前,请确保您已执行以下任务:

  • 启用 Google Kubernetes Engine API。
  • 启用 Google Kubernetes Engine API
  • 如果您要使用 Google Cloud CLI 执行此任务,请安装初始化 gcloud CLI。 如果您之前安装了 gcloud CLI,请运行 gcloud components update 以获取最新版本。
  • 确保您有足够的 H100 GPU 配额。如需申请更多配额,请参阅 GPU 配额

使用要求

  • GPUDirect-TCPX 在 GKE 1.27 版或更高版本上受支持,并且需要:
    • 对于 GKE 1.27 版,请使用 GKE 补丁 1.27.7-gke.1121000 版或更高版本。
    • 对于 GKE 1.28 版,请使用 GKE 补丁 1.28.8-gke.1095000 版或更高版本。
    • 对于 GKE 1.29 版,请使用 GKE 补丁 1.29.3-gke.1093000 版或更高版本。
  • 您的 GPU 节点必须使用 535 或更高版本的 NVIDIA 驱动程序。
  • 您必须使用 GKE Dataplane V2。

限制

存在以下限制:

  • 您无法在 Autopilot 集群中使用 GPUDirect-TCPX
  • 您只能在 GKE 1.27 版或更高版本上并且通过以下补丁版本来使用 GPUDirect-TCPX:
    • 对于 GKE 1.27 版,请使用 GKE 补丁 1.27.7-gke.1121000 版或更高版本。
    • 对于 GKE 1.28 版,请使用 GKE 补丁 1.28.8-gke.1095000 版或更高版本。
    • 对于 GKE 1.29 版,请使用 GKE 补丁 1.29.3-gke.1093000 版或更高版本。
  • 您无法将 GPUDirect-TCPX 与多实例 GPUGPU 分时搭配使用
  • 您无法使用 NCCL FastSocket
  • 您的环境必须支持在 Pod 规范中设置 hostNetwork: true
  • 如需将本地 SSD 用于 Pod 存储,您必须明确指定要挂接到底层 A3 虚拟机的本地 SSD 的确切数量,为此,对于临时存储请使用 --ephemeral-storage-local-ssd=count=SSD_COUNT 标志,对于块存储访问请使用 --local-nvme-ssd-block=count=SSD_COUNT 标志。如果省略此标志,您将无法在 Pod 中使用本地 SSD。仅当您想要将本地 SSD 用于数据访问时才需要这些标志。

    GKE 中支持的机器大小为 a3-highgpu-8g,相应的本地 SSD 数量为 16

创建 VPC 和子网

在项目中为将要添加到节点的每个虚拟 NIC 创建单独的 VPC 网络。每个 VPC 都必须具有一个子网和一条允许内部网络流量的防火墙规则。为了最大限度地提高带宽,我们建议您创建四个新网络。

  1. 更新项目中的默认 VPC 子网,以便为 Pod 和 Service 添加次要 IP 地址范围:

    gcloud compute networks subnets update DEFAULT_NETWORK \
        --region=REGION \
        --add-secondary-ranges="CLUSTER_NAME-pods=POD_IP_ADDRESS_RANGE,CLUSTER_NAME-services=SERVICE_IP_ADDRESS_RANGE"
    

    替换以下内容:

    • DEFAULT_NETWORK:项目中默认子网的名称。
    • REGION:默认子网的区域。
    • CLUSTER_NAME:GKE 集群的名称。
    • POD_IP_ADDRESS_RANGE:要使用的集群中 Pod 的 IP 地址范围(采用 CIDR 表示法)。例如 10.64.0.0/19
    • SERVICE_IP_ADDRESS_RANGE:要使用的集群中 Service 的 IP 地址范围(采用 CIDR 表示法)。必须与 Pod 范围不同。例如 10.65.0.0/19
  2. 在项目中为 GPUDirect-TCPX 创建 VPC 网络,每个 VPC 网络都具有一个子网和一条防火墙规则:

    for N in $(seq 1 4); do
    gcloud compute networks create PROJECT_ID-net-$N \
        --subnet-mode=custom \
        --mtu=8244
    
    gcloud compute networks subnets create PROJECT_ID-sub-$N \
        --network=PROJECT_ID-net-$N \
        --region=REGION \
        --range=SUBNET_RANGE
    
    gcloud compute firewall-rules create PROJECT_ID-internal-$N \
      --network=PROJECT_ID-net-$N \
      --action=ALLOW \
      --rules=tcp:0-65535,udp:0-65535,icmp \
      --source-ranges=SOURCE_RANGE
    done
    

    替换以下内容:

    • PROJECT_ID:您的 Google Cloud 项目 ID。
    • REGION:每个子网的 Compute Engine 区域。
    • SUBNET_RANGE:每个子网的 IP 地址范围,采用 CIDR 表示法。此示例命令将针对四个子网进行迭代,因此请使用变量来更改每个子网的 IP 地址。例如,指定 192.168.$N.0/24,从而使第一个子网使用 192.168.1.0/24,第二个子网使用 192.168.2.0/24,以此类推。
    • SOURCE_RANGE:允许入站流量的防火墙规则的来源 IP 地址范围(采用 CIDR 表示法)。例如 192.168.0.0/16
  3. 验证网络已创建:

    gcloud compute networks list
    

创建 GKE 环境

创建一个使用多网络(预览版)的新 GKE 集群,并创建一个 GPU 节点池,该节点池使用搭载 H100 GPU 并具有四个额外 NIC 的 A3 机器。您无法通过更新现有集群来使用多网络。

  1. 创建集群:

    gcloud container clusters create CLUSTER_NAME \
        --location=LOCATION \
        --cluster-version=VERSION \
        --enable-dataplane-v2 --enable-ip-alias \
        --enable-multi-networking \
        --no-enable-autoupgrade \
        --cluster-secondary-range-name=CLUSTER_NAME-pods \
        --services-secondary-range-name=CLUSTER_NAME-services
    

    替换以下内容:

    • CLUSTER_NAME:新集群的名称。
    • LOCATION:集群的 Compute Engine 区域。
    • VERSION:集群的 GKE 版本。必须是“要求”部分中所述的受支持版本。

    此命令还明确指定了您在上一部分中创建的集群中 Pod 和 Service 的次要 IP 地址。

  2. 在与您创建的 VPC 网络和子网相对应的集群中创建 Network 和 GKENetworkParamSet 资源:

    kubectl apply -f - <<EOF
    apiVersion: networking.gke.io/v1
    kind: Network
    metadata:
      name: vpc1
    spec:
      parametersRef:
        group: networking.gke.io
        kind: GKENetworkParamSet
        name: vpc1
      type: Device
    ---
    apiVersion: networking.gke.io/v1
    kind: Network
    metadata:
      name: vpc2
    spec:
      parametersRef:
        group: networking.gke.io
        kind: GKENetworkParamSet
        name: vpc2
      type: Device
    ---
    apiVersion: networking.gke.io/v1
    kind: Network
    metadata:
      name: vpc3
    spec:
      parametersRef:
        group: networking.gke.io
        kind: GKENetworkParamSet
        name: vpc3
      type: Device
    ---
    apiVersion: networking.gke.io/v1
    kind: Network
    metadata:
      name: vpc4
    spec:
      parametersRef:
        group: networking.gke.io
        kind: GKENetworkParamSet
        name: vpc4
      type: Device
    ---
    apiVersion: networking.gke.io/v1
    kind: GKENetworkParamSet
    metadata:
      name: vpc1
    spec:
      vpc: PROJECT_ID-net-1
      vpcSubnet: PROJECT_ID-sub-1
      deviceMode: NetDevice
    ---
    apiVersion: networking.gke.io/v1
    kind: GKENetworkParamSet
    metadata:
      name: vpc2
    spec:
      vpc: PROJECT_ID-net-2
      vpcSubnet: PROJECT_ID-sub-2
      deviceMode: NetDevice
    ---
    apiVersion: networking.gke.io/v1
    kind: GKENetworkParamSet
    metadata:
      name: vpc3
    spec:
      vpc: PROJECT_ID-net-3
      vpcSubnet: PROJECT_ID-sub-3
      deviceMode: NetDevice
    ---
    apiVersion: networking.gke.io/v1
    kind: GKENetworkParamSet
    metadata:
      name: vpc4
    spec:
      vpc: PROJECT_ID-net-4
      vpcSubnet: PROJECT_ID-sub-4
      deviceMode: NetDevice
    EOF
    

    这些资源会告诉 GKE 以直通模式为 GPU 流量配置 NIC。GKE 不会为此流量应用使用 eBPF 的内置网络编程。

  3. 为 H100 GPU 创建节点池:

    gcloud container node-pools create NODE_POOL_NAME \
        --cluster=CLUSTER_NAME \
        --location=LOCATION \
        --machine-type=a3-highgpu-8g \
        --accelerator=type=nvidia-h100-80gb,count=8,gpu-driver-version=LATEST \
        --additional-node-network=network=PROJECT_ID-net-1,subnetwork=PROJECT_ID-sub-1 \
        --additional-node-network=network=PROJECT_ID-net-2,subnetwork=PROJECT_ID-sub-2 \
        --additional-node-network=network=PROJECT_ID-net-3,subnetwork=PROJECT_ID-sub-3 \
        --additional-node-network=network=PROJECT_ID-net-4,subnetwork=PROJECT_ID-sub-4 \
        --enable-gvnic \
        --no-enable-autoupgrade \
        [--ephemeral-storage-local-ssd=count=16]
    

    NODE_POOL_NAME 替换为节点池的名称。

    如果此命令失败,则您的项目中可能没有足够的 H100 GPU 配额。确保您拥有配额,然后重试命令。

  4. 获取集群中的节点列表:

    kubectl get nodes
    
  5. 验证每个 GPU 节点都有八个 GPU:

    kubectl describe node NODE_NAME
    

    输出类似于以下内容:

    Capacity:
      ...
      nvidia.com/gpu:             8
    Allocatable:
      ...
      nvidia.com/gpu:             8
    

安装 GPUDirect-TCPX 并配置 NCCL

本部分介绍如何使用 DaemonSet 安装 GPUDirect-TCPX 二进制文件和特定 NCCL。

  1. 查看 DaemonSet 清单:

    apiVersion: apps/v1
    kind: DaemonSet
    metadata:
      name: nccl-tcpx-installer
      namespace: kube-system
      labels:
        k8s-app: nccl-tcpx-installer
    spec:
      selector:
        matchLabels:
          k8s-app: nccl-tcpx-installer
      updateStrategy:
        type: RollingUpdate
      template:
        metadata:
          labels:
            name: nccl-tcpx-installer
            k8s-app: nccl-tcpx-installer
        spec:
          priorityClassName: system-node-critical
          affinity:
            nodeAffinity:
              requiredDuringSchedulingIgnoredDuringExecution:
                nodeSelectorTerms:
                  - matchExpressions:
                      - key: cloud.google.com/gke-accelerator
                        operator: In
                        values:
                          - nvidia-h100-80gb
          tolerations:
            - operator: "Exists"
          hostNetwork: true
          hostPID: true
          volumes:
            - name: var-lib
              hostPath:
                path: /var/lib
            - name: tcpx
              hostPath:
                path: /var/lib/tcpx
            - name: library-dir-host
              hostPath:
                path: /home/kubernetes/bin
          initContainers:
            - image: us-docker.pkg.dev/gce-ai-infra/gpudirect-tcpx/nccl-plugin-gpudirecttcpx-dev:v3.1.9
              name: nccl-tcpx-installer
              resources:
                requests:
                  cpu: 150m
              securityContext:
                privileged: true
              volumeMounts:
                - name: var-lib
                  mountPath: /var/lib
                - name: library-dir-host
                  mountPath: /usr/local
              command: ["/bin/sh", "-c"]
              args:
                - |
                  set -ex
                  /scripts/container_entry.sh install --install-nccl
                  mkdir -p /usr/local/nvidia/lib64
                  cp -r /var/lib/tcpx/lib64/. /usr/local/nvidia/lib64
                  echo "installation finishes"
          containers:
            - image: "gcr.io/google-containers/pause:2.0"
              name: pause
    

    此 DaemonSet 会执行以下操作:

    1. 在节点上安装 NCCL 库和 GPUDirect-TCPX 二进制文件。
    2. 将库和二进制文件存储在虚拟机上的 /home/kubernetes/bin/nvidia/lib64 目录中。默认情况下,GKE 会将此目录装载到需要使用 NCCL 和 GPUDirect-TCPX 的 GPU 容器中的 /usr/local/nvidia/lib64 路径中。
  2. 部署 DaemonSet:

    kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/container-engine-accelerators/master/gpudirect-tcpx/nccl-tcpx-installer.yaml
    

    NCCL 插件大约需要两分钟才能开始运行。

  3. 验证 DaemonSet Pod 的状态:

    kubectl get pods -n=kube-system -l=name=nccl-tcpx-installer
    

    输出类似于以下内容:

    nccl-tcpx-installer-6c2pv                    1/1     Running   0          2m11s
    nccl-tcpx-installer-qgg82                    1/1     Running   0          2m11s
    

部署测试工作负载

在本部分中,您将部署一个示例工作负载来验证 NCCL 和 GPUDirect-TCPX 按预期运行。此工作负载包含一个名为 tcpx-daemon 的边车容器,它运行一个服务以使 Pod 能够使用 GPUDirect-TCPX。您必须将此边车容器添加到您自己环境中需要使用 GPUDirect-TCPX 的所有 Pod。如需查看要添加到清单的必填字段的代码段,请参阅本文档中的将 GPUDirect-TCPX 添加到清单

  1. 查看 GitHub 中的 nccl-config-default.yaml ConfigMap 清单。此清单部署初始化 NCCL allgather 测试的脚本并设置特定于 NCCL 的环境变量。
  2. 查看 GitHub 中的 nccl-test.yaml 清单。此清单执行以下操作:

    1. 部署两个 Pod,每个 Pod 都在具有 H100 GPU 的节点中运行。
    2. 在每个 Pod 中部署一个名为 tcpx-daemon 的边车容器,以使这些 Pod 能够使用 GPUDirect-TCPX。
  3. 部署 ConfigMap 和测试工作负载:

    kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/container-engine-accelerators/master/gpudirect-tcpx/nccl-config-default.yaml
    kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/container-engine-accelerators/master/gpudirect-tcpx/nccl-test.yaml
    
  4. 运行以下命令以触发节点的 NCCL all-gather 测试:

    head_pod=$(kubectl get pods --output='custom-columns=POD:.metadata.name' --no-headers | head -n1)
    
    nodes=($(kubectl get pods --output='custom-columns=NODE:.spec.nodeName' --no-headers))
    
    kubectl exec --stdin --tty --container=nccl-test ${head_pod} -- /configs/allgather.sh ${nodes[@]}
    

    输出类似于以下内容:

    #                                                              out-of-place                       in-place
    #       size         count      type   redop    root     time   algbw   busbw #wrong     time   algbw   busbw #wrong
    #        (B)    (elements)                               (us)  (GB/s)  (GB/s)            (us)  (GB/s)  (GB/s)
         1048576         16384     float    none      -1    696.8    1.50    1.41      0    729.0    1.44    1.35      0
         2097152         32768     float    none      -1    776.4    2.70    2.53      0    726.7    2.89    2.71      0
         4194304         65536     float    none      -1    774.3    5.42    5.08      0    805.1    5.21    4.88      0
         8388608        131072     float    none      -1    812.1   10.33    9.68      0    817.6   10.26    9.62      0
        16777216        262144     float    none      -1   1035.2   16.21   15.19      0   1067.8   15.71   14.73      0
        33554432        524288     float    none      -1   1183.3   28.36   26.59      0   1211.8   27.69   25.96      0
        67108864       1048576     float    none      -1   1593.4   42.12   39.49      0   1510.5   44.43   41.65      0
       134217728       2097152     float    none      -1   2127.8   63.08   59.13      0   2312.7   58.03   54.41      0
       268435456       4194304     float    none      -1   3603.0   74.50   69.85      0   3586.2   74.85   70.17      0
       536870912       8388608     float    none      -1   7101.7   75.60   70.87      0   7060.9   76.03   71.28      0
    # Out of bounds values : 0 OK
    # Avg bus bandwidth    : 29.8293
    

使用 NCCL 环境变量提高性能

您可以选择设置特定的环境变量,以提高使用 NCCL 的工作负载的性能。默认情况下,您在部署测试工作负载部分中部署的 nccl-config-default.yaml ConfigMap 会设置一些 NCCL 变量。变量配置存储在 ConfigMap 中的 run-nccl.sh 脚本中。

如需更改 NCCL 环境变量,请部署包含修改变量的更新 ConfigMap 清单。GitHub 中的 nccl-config-latest.yaml 清单包含每个建议的变量以及更新的 run-nccl.sh 脚本。

以下命令使用更新后的 nccl-config-latest.yaml ConfigMap 更新具有默认变量的现有 ConfigMap:

kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/container-engine-accelerators/master/gpudirect-tcpx/nccl-config-latest.yaml

Kubernetes 更新 ConfigMap 大约需要两分钟。

如需检查 NCCL 环境变量,请运行以下命令:

head_pod=$(kubectl get pods --output='custom-columns=POD:.metadata.name' --no-headers | head -n1)

kubectl exec --stdin --tty --container=nccl-test ${head_pod} -- cat /configs/run-nccl.sh

将 GPUDirect-TCPX 添加到清单

本部分提供了为使 Pod 能够使用 PodDirect-TCPX 而必须添加到 Kubernetes 清单的必填字段。

  1. 将以下字段添加到 Pod 规范中:

    spec:
      hostNetwork: true
      dnsPolicy: ClusterFirstWithHostNet
      volumes:
      - name: libraries
        hostPath:
          path: /home/kubernetes/bin/nvidia/lib64
      - name: tcpx-socket
        hostPath:
          path: /run/tcpx
    
  2. 将以下容器添加到清单中以运行 tcpx-daemon 服务:

    - name: tcpx-daemon
      image: us-docker.pkg.dev/gce-ai-infra/gpudirect-tcpx/tcpgpudmarxd-dev:v2.0.9
      command:
        - /tcpgpudmarxd/build/app/tcpgpudmarxd
        - --gpu_nic_preset
        - a3vm
        - --gpu_shmem_type
        - fd
        - --uds_path
        - /run/tcpx
        - --setup_param
        - \"--verbose 128 2 0 \"
      securityContext:
        privileged: true
      volumeMounts:
        - name: libraries
          mountPath: /usr/local/nvidia/lib64
        - name: tcpx-socket
          mountPath: /run/tcpx
      env:
        - name: LD_LIBRARY_PATH
          value: /usr/local/nvidia/lib64
    
  3. 将以下卷装载添加到请求 GPU 的所有容器:

    volumeMounts:
    - name: tcpx-socket
      mountPath: /tmp
    - name: libraries
      mountPath: /usr/local/nvidia/lib64
    
  4. 将以下环境变量添加到每个 GPU 容器:

    env:
    - name: LD_LIBRARY_PATH
      value: /usr/local/nvidia/lib64
    
  5. (可选)添加环境变量以配置 NCCL 选项。如需了解详情,请参阅本文档中的使用 NCCL 环境变量提高性能部分。

完成后的 Pod 规范如下所示:

apiVersion: v1
kind: Pod
metadata:
  name: example-pod
  labels:
    name: example-pod
spec:
  hostNetwork: true
  dnsPolicy: ClusterFirstWithHostNet
  containers:
  - name: tcpx-daemon
    image: us-docker.pkg.dev/gce-ai-infra/gpudirect-tcpx/tcpgpudmarxd-dev:v2.0.9
    command:
      - /tcpgpudmarxd/build/app/tcpgpudmarxd
      - --gpu_nic_preset
      - a3vm
      - --gpu_shmem_type
      - fd
      - --uds_path
      - /run/tcpx
      - --setup_param
      - \"--verbose 128 2 0 \"
    securityContext:
      privileged: true
    volumeMounts:
      - name: libraries
        mountPath: /usr/local/nvidia/lib64
      - name: tcpx-socket
        mountPath: /run/tcpx
    env:
      - name: LD_LIBRARY_PATH
        value: /usr/local/nvidia/lib64
    - name: nccl-test
      image: us-docker.pkg.dev/gce-ai-infra/gpudirect-tcpx/nccl-plugin-gpudirecttcpx:v3.1.2
      imagePullPolicy: Always
      command:
        - /bin/sh
        - -c
        - "while true; do echo hello; sleep 1; done"
      env:
        - name: LD_LIBRARY_PATH
          value: /usr/local/nvidia/lib64
      volumeMounts:
        - name: tcpx-socket
          mountPath: /run/tcpx
        - name: libraries
          mountPath: /usr/local/nvidia/lib64
      resources:
        limits:
          nvidia.com/gpu: 8
  volumes:
    - name: libraries
      hostPath:
        path: /home/kubernetes/bin/nvidia/lib64
    - name: tcpx-socket
      hostPath:
        path: /run/tcpx