You need to enable JavaScript to run this app.
导航

VKE 集群中使用 RDMA 资源

最近更新时间2024.03.22 10:14:18

首次发布时间2023.08.10 20:05:28

容器服务支持通过组件使用 RDMA 资源,以消除传统网络通信带给计算任务的瓶颈。本文为您介绍 VKE 集群如何使用 RDMA 资源。

说明

邀测·申请试用】:VKE 集群中使用 RDMA 资源 与其依赖的 kubelet 自定义参数 功能均处于邀测阶段,如需使用,请提交申请。

背景信息

RDMA (Remote Direct Memroy Access,远程直接内存访问)是一种高性能网络协议,能够减少了CPU 占用,减少内存带宽瓶颈,提高带宽利用率。主要具有以下优势:

  • Zero Copy:应用程序可以在不涉及网络软件堆栈的情况下执行数据传输,数据能够被直接发送到缓冲区或者直接从缓冲区接收,而不需要被复制到网络层。
  • Kernel Bypass:RDMA 提供一个专有的 Verbs interface,应用程序可以直接从用户空间执行数据传输,不需要在内核态与用户态之间执行上下文切换。
  • CPU Offload:应用程序可以访问远程主机内存,不消耗远程主机中的任何 CPU。远程内存机器将在没有远程进程(或 CPU)的任何干预情况下被读取,远程 CPU 中的缓存不会被所访问的内存内容填充。

使用限制

  • RDMA 与 mGPU 不能同时使用。

  • RDMA 目前提供 exclusive(独占)、shared(共享)、shared-multi(1:N 共享)3 种模式。不同模式的使用限制如下:

模式说明使用限制
exclusive(独占)将 RDMA 物理网卡直接透传给容器集群中的 Pod 使用,适用于物理设备无法虚拟化切割的场景。exclusive 模式暂不支持在 vePFS 场景中使用。
shared(共享)将 RDMA 物理网卡进行虚拟化切割,创建成 1 张共享虚拟网卡,提供给容器集群中的 Pod 使用。shared 模式中,每张 RDMA 物理网卡仅允许创建 1 张共享虚拟网卡。

shared-multi(1:N 共享)

将 RDMA 物理网卡进行虚拟化切切割,按需创建成多张共享虚拟网卡,提供给容器集群中的 Pod 使用。

shared-multi 模式不建议与 GPU 一起使用。

  • RDMA 设备仅支持在 VPC-CNI 容器网络模型场景中使用,且集群网络组件vpc-cni要求使用 v1.7.1 及以上版本。

  • 集群 Kubernetes 版本要求 v1.24.15-vke.12 及以上的 1.24 版本;v1.20.15-vke.18 及以上的 1.20 版本。可前往控制台的集群基本信息页面查看,详细介绍参见:如何查看集群的 Kubernetes 版本?

操作步骤:新建资源(推荐)

推荐新建容器服务集群和节点池使用 RDMA 资源,详细使用步骤如下。

步骤一:创建集群并准备资源

说明

本步骤仅针对使用 RDMA 资源的关键配置进行详细介绍,包括:创建节点池、添加节点标签、配置 Kubelet 自定义参数、安装 RDMA 组件。创建集群的操作步骤和详细配置参见:创建集群

  1. 登录 容器服务控制台,在左侧导航栏选择 集群,进入集群管理页面。
  2. 单击 创建集群,跳转到集群创建页面的 集群配置 步骤,完成后单击 下一步:节点池配置

alt

集群配置步骤的关键配置项如下,其他参数按需配置即可。

配置项说明

容器网络模型

配置集群的容器网络(CNI)方案,本场景必须选择 VPC-CNI。

  • Flannel:独立的 Underlay 容器网络模型,配合 VPC 的全局路由能力,实现集群高性能的网络体验。

  • VPC-CNI:基于私有网络的弹性网卡 ENI 实现的 Underlay 容器网络模型,具有较高的网络通信性能。

  1. 在节点池配置步骤,新建节点池并完成节点标签、Kubelet 自定义参数等关键配置,完成后单击 下一步:组件配置

alt

节点池配置步骤的关键配置项如下,其他参数按需配置即可。

配置项说明
Worker 节点为确保集群可以正常使用,本场景选择 立即创建
节点来源节点的来源,本场景选择 创建节点
计算规格计算规格用于定义节点 CPU、内存、GPU、RDMA 等参数,本场景需要选择具备 RDMA 设备的计算规格,例如:高性能计算 GPU 型。

节点标签

对应 Kubernetes 中的 Label,能够为节点定义不同的属性,方便批量筛选等需求,可为节点池中的节点批量添加相同标签。
本场景通过手动添加vke.node.rdma.mode: {value} 标签方式,为节点或节点池配置 RDMA 使用模式,支持:exclusive(独占)、shared(共享)、shared-multi(1:N 共享)。

说明

除 ecs.hpcg1ve.21xlarge 和华北 ecs.hpcpni2.28xlarge 机型外,均推荐使用 shared 模式,且优先推荐使用 shared。

Kubelet 自定义参数

针对时延敏感性较高的业务,提供 Kubelet 自定义参数,用于配置 NUMA 亲和策略,可将为 Container (或 Pod) 分配的资源对齐到一个 (或多个) NUMA 上,从而提高业务的性能。
本场景要求添加自定义参数topologyManagerPolicy: best-effort,确保 GPU 和 RDMA 分配在同一个 NUMA 下。

  1. 在组件配置步骤,要求勾选rdma-device-plugin组件,其他组件按需配置即可。完成后单击 下一步:确认配置

alt

  1. 确认新建集群信息和计费准确无误后,阅读 《容器服务专用服务条款》 并勾选同意,单击页面 确定 ,开始创建集群。

步骤二:使用 RDMA 资源

上述新建集群完成后,即可在创建工作负载时使用 RDMA 资源,本场景以无状态工作负载为例进行介绍。

  1. 登录 容器服务控制台,单击目标集群名称进入集群管理页面。
  2. 在左侧导航栏选择 工作负载 > 无状态负载,进入无状态负载管理页面。
  3. 单击 创建无状态负载,在容器配置步骤的资源配额页签,按需配置 RDMA 卡数。工作负载其他配置参见:创建无状态负载

说明

为了能够发挥最佳性能,推荐 RDMA 卡数配置为:2 或 4。

alt

Yaml 方式使用 RDMA 资源的说明如下

  • 若 Pod Container 需要使用 RDMA 设备,可在 resource 中添加设备vke.volcengine.com/rdma,同时在 Pod 的 Annotation 中配置对应数量的 pod networks。大部分情况下,容器需要同时申请 IPC_LOCK 特权。示例如下:

说明

以下示例以自动创建 1 张网卡设备为例,如需创建多张网卡设备,要求将k8s.volcengine.com/pod-networks中的如下代码块复制 N 份。

{
  "cniConf":{
      "name":"rdma"
  }
}
apiVersion: v1
kind: Pod
metadata:
  annotations:
    k8s.volcengine.com/pod-networks: |
      [
        {
          "cniConf":{
              "name":"rdma"
          }
        }
      ]
spec:
  containers:
    resources:
      limits:
        vke.volcengine.com/rdma: 1
    securityContext:
      capabilities:
        add:
        - IPC_LOCK
  • 共享模式下容器内运行 NCCL 需要显示指定 NCCL_IB_GID_INDEX 环境变量,可执行source nccl_env.sh命令获取 RDMA 环境变量。动态获取 NCCL 环境变量的参考示例脚本nccl_env.sh内容如下。
#!/usr/bin/env bash

devs=()
ports=()
idxes=()
rdmaIdx=""
rdmaRate=""
rdmaErr=""

function scan_devices() {
    allocDevs=()
    for d in $(ls /sys/class/infiniband/) ; do
      allocDevs+=($d)
    done
    for d in "${allocDevs[@]}" ; do
      for p in $(ls /sys/class/infiniband/$d/ports/) ; do
        for g in $(ls /sys/class/infiniband/$d/ports/$p/gids/) ; do
        gid=$(cat /sys/class/infiniband/$d/ports/$p/gids/$g);
        if [ $(echo $gid | cut -d ":" -f -1) != "0000" ] ; then
          continue
        fi
        if [ $gid = 0000:0000:0000:0000:0000:0000:0000:0000 ] ; then
          continue
        fi

        RoCEVer=$(cat /sys/class/infiniband/$d/ports/$p/gid_attrs/types/$g 2>/dev/null| grep -o "[Vv].*")
        if [ ${RoCEVer,,} != "v2" ]; then
          continue
        fi

        # device not Up
        ethDev=$(cat /sys/class/infiniband/$d/ports/$p/gid_attrs/ndevs/$g 2>/dev/null)
        if [[ ! -f /sys/class/net/$ethDev/dev_id ]] || [[ ! -f /sys/class/net/$ethDev/carrier ]] || (( ! $(cat /sys/class/net/$ethDev/carrier 2> /dev/null) == 1 )); then
          continue
        fi

        devs+=($d)
        ports+=($p)
        idxes+=($g)
        done #g (gid)
      done #p (port)
    done #d (dev)
    if [[ -z $devs ]]; then
      echo "Not found RDMA device, list devices error"
      return 1
    fi
}

function export_nccl_env() {
  scan_devices
  if [[ -z $devs ]]; then
    echo "Not found RDMA device, get max rate device error"
    return 1
  fi
  for ((i=0; i<${#devs[@]}; i+=1)); do
    rate=$(cat /sys/class/infiniband/${devs[i]}/ports/${ports[i]}/rate 2>/dev/null|cut -f 1 -d " ")
    if [[ -z "$rdmaRate" ]] || [[ "$rate" -gt "$rdmaRate" ]]; then
      rdmaDevs=(${devs[i]})
      rdmaPorts=(${ports[i]})
      rdmaIdx=${idxes[i]}
      rdmaErr=""
      rdmaRate=$rate
    elif [[ "$rate" == "$rdmaRate" ]]; then
      rdmaDevs+=(${devs[i]})
      rdmaPorts+=(${ports[i]})
      if [[ "$rdmaIdx" != "${idxes[i]}" ]]; then
        rdmaErr="rdma devs index inconsistent"
      fi
    fi
  done

  if [[ -n $rdmaErr ]]; then
      echo $rdmaErr
      return 1
  fi

  hca=()
  for ((i=0; i<${#rdmaDevs[@]}; i+=1)); do
    hca+=("${rdmaDevs[i]}:${rdmaPorts[i]}")
  done
  IFS=","
  export NCCL_IB_HCA=${hca[*]}
  unset IFS

  export NCCL_IB_GID_INDEX=$rdmaIdx
}

export_nccl_env

操作步骤:存量资源

已经创建容器服务集群和节点池的情况下,可参考以下步骤,直接在存量 VKE 集群和节点池中使用 RDMA 资源。

步骤一:安装 RDMA 设备插件

  1. 登录 容器服务控制台,单击目标集群名称进入集群管理页面。
  2. 在左侧导航栏选择 组件管理,查找 网络 页签中的 rdma-device-plugin 组件。
  3. 单击组件右上角 ...中的 安装,为集群安装 RDMA 设备插件。

alt

步骤二:准备节点资源

需要准备支持 RDMA 设备的节点(例如:高性能计算 GPU 型),可新建或使用存量符合要求的节点。并通过手动添加vke.node.rdma.mode: {value} 标签方式为节点或节点池配置 RDMA 使用模式,支持:exclusive(独占)、shared(共享)、shared-multi(1:N 共享)。

注意

  • 除 ecs.hpcg1ve.21xlarge 和华北 ecs.hpcpni2.28xlarge 机型外,均推荐使用 shared 模式,且优先推荐使用 shared。
  • 若修改标签切换 RDMA 使用模式,请确认当前存量业务已经运行结束,并重启节点后生效。

方式一:存量节点添加标签

  1. 登录 容器服务控制台,单击目标集群名称进入集群管理页面。
  2. 在左侧导航栏选择 节点,单击目标节点右侧操作列...中的 标签管理
  3. 单击 添加标签,为存量节点添加上述标签,完成后单击弹框 确定 保存配置。

alt

Yaml 示例如下:

type: Node
label:
    vke.node.rdma.mode: shared
 status:
   capacity:
     vke.volcengine.com/rdma: 4

方式二:节点池添加节点标签

  1. 登录 容器服务控制台,单击目标集群名称进入集群管理页面。
  2. 在左侧导航栏选择 节点池,创建节点池或单击目标集群右侧操作列中的 编辑,以节点池为单位添加节点标签,完成后单击 确定 保存配置。

alt

步骤三:修改节点 kubelet 拓扑管理策略

通过节点池自定义参数配置将 RDMA 节点拓扑管理策略修改为topologyManagerPolicy: best-effort,确保 GPU 和 RDMA 分配在同一个 NUMA 下。

警告

在存量节点池中修改 kubelet 自定义参数时,默认仅对新增节点生效,不对存量节点生效。如果需要对存量节点生效,则需要执行存量节点参数同步操作,详情请参见 存量节点参数同步

  1. 登录 容器服务控制台,单击目标集群名称进入集群管理页面。
  2. 在左侧导航栏选择 节点池,进入节点池管理页面。
  3. 单击目标节点池名称,在 Kubelet 自定义参数 页签,单击右上角 编辑 ,手动添加参数,完成后单击 确定 保存配置。

alt

Yaml 示例如下:

topologyManagerPolicy: best-effort

步骤四:使用 RDMA 资源

上述配置完成后,即可在创建工作负载时使用 RDMA 资源,本场景以无状态工作负载为例进行介绍。

  1. 登录 容器服务控制台,单击目标集群名称进入集群管理页面。
  2. 在左侧导航栏选择 工作负载 > 无状态负载,进入无状态负载管理页面。
  3. 单击 创建无状态负载,在容器配置步骤的资源配额页签,按需配置 RDMA 卡数。工作负载其他配置参见:创建无状态负载

说明

为了能够发挥最佳性能,推荐 RDMA 卡数配置为:2 或 4。

alt

Yaml 方式使用 RDMA 资源的说明如下

  • 若 Pod Container 需要使用 RDMA 设备,可在 resource 中添加设备vke.volcengine.com/rdma,同时在 Pod 的 Annotation 中配置对应数量的 pod networks。大部分情况下,容器需要同时申请 IPC_LOCK 特权。示例如下:

说明

以下示例以自动创建 1 张网卡设备为例,如需创建多张网卡设备,要求将k8s.volcengine.com/pod-networks中的如下代码块复制 N 份。

{
  "cniConf":{
      "name":"rdma"
  }
}
apiVersion: v1
kind: Pod
metadata:
  annotations:
    k8s.volcengine.com/pod-networks: |
      [
        {
          "cniConf":{
              "name":"rdma"
          }
        }
      ]
spec:
  containers:
    resources:
      limits:
        vke.volcengine.com/rdma: 1
    securityContext:
      capabilities:
        add:
        - IPC_LOCK
  • 共享模式下容器内运行 NCCL 需要显示指定 NCCL_IB_GID_INDEX 环境变量,可执行source nccl_env.sh命令获取 RDMA 环境变量。动态获取 NCCL 环境变量的参考示例脚本nccl_env.sh内容如下。
#!/usr/bin/env bash

devs=()
ports=()
idxes=()
rdmaIdx=""
rdmaRate=""
rdmaErr=""

function scan_devices() {
    allocDevs=()
    for d in $(ls /sys/class/infiniband/) ; do
      allocDevs+=($d)
    done
    for d in "${allocDevs[@]}" ; do
      for p in $(ls /sys/class/infiniband/$d/ports/) ; do
        for g in $(ls /sys/class/infiniband/$d/ports/$p/gids/) ; do
        gid=$(cat /sys/class/infiniband/$d/ports/$p/gids/$g);
        if [ $(echo $gid | cut -d ":" -f -1) != "0000" ] ; then
          continue
        fi
        if [ $gid = 0000:0000:0000:0000:0000:0000:0000:0000 ] ; then
          continue
        fi

        RoCEVer=$(cat /sys/class/infiniband/$d/ports/$p/gid_attrs/types/$g 2>/dev/null| grep -o "[Vv].*")
        if [ ${RoCEVer,,} != "v2" ]; then
          continue
        fi

        # device not Up
        ethDev=$(cat /sys/class/infiniband/$d/ports/$p/gid_attrs/ndevs/$g 2>/dev/null)
        if [[ ! -f /sys/class/net/$ethDev/dev_id ]] || [[ ! -f /sys/class/net/$ethDev/carrier ]] || (( ! $(cat /sys/class/net/$ethDev/carrier 2> /dev/null) == 1 )); then
          continue
        fi

        devs+=($d)
        ports+=($p)
        idxes+=($g)
        done #g (gid)
      done #p (port)
    done #d (dev)
    if [[ -z $devs ]]; then
      echo "Not found RDMA device, list devices error"
      return 1
    fi
}

function export_nccl_env() {
  scan_devices
  if [[ -z $devs ]]; then
    echo "Not found RDMA device, get max rate device error"
    return 1
  fi
  for ((i=0; i<${#devs[@]}; i+=1)); do
    rate=$(cat /sys/class/infiniband/${devs[i]}/ports/${ports[i]}/rate 2>/dev/null|cut -f 1 -d " ")
    if [[ -z "$rdmaRate" ]] || [[ "$rate" -gt "$rdmaRate" ]]; then
      rdmaDevs=(${devs[i]})
      rdmaPorts=(${ports[i]})
      rdmaIdx=${idxes[i]}
      rdmaErr=""
      rdmaRate=$rate
    elif [[ "$rate" == "$rdmaRate" ]]; then
      rdmaDevs+=(${devs[i]})
      rdmaPorts+=(${ports[i]})
      if [[ "$rdmaIdx" != "${idxes[i]}" ]]; then
        rdmaErr="rdma devs index inconsistent"
      fi
    fi
  done

  if [[ -n $rdmaErr ]]; then
      echo $rdmaErr
      return 1
  fi

  hca=()
  for ((i=0; i<${#rdmaDevs[@]}; i+=1)); do
    hca+=("${rdmaDevs[i]}:${rdmaPorts[i]}")
  done
  IFS=","
  export NCCL_IB_HCA=${hca[*]}
  unset IFS

  export NCCL_IB_GID_INDEX=$rdmaIdx
}

export_nccl_env

结果验证

可根据 RDMA 监控数据中提供的指标确认分配的 RDMA 资源是否已经被使用,RDMA 监控查看方法参见:RDMA 网络监控查看实例 GPU/RDMA 监控数据