

 **帮助改进此页面** 

要帮助改进本用户指南，请选择位于每个页面右侧窗格中的**在 GitHub 上编辑此页面**链接。

# 使用 Elastic Fabric Adapter 在 Amazon EKS 上运行机器学习训练
<a name="node-efa"></a>

本主题介绍如何将 Elastic Fabric Adapter（EFA）与部署在 Amazon EKS 集群中的容器组（pod）集成。Elastic Fabric Adapter (EFA) 是 Amazon EC2 实例的网络接口，使您能够在AWS上运行要求大规模高级别节点间通信的应用程序。其量身定制的操作系统旁路硬件接口增强了实例间通信的性能，这对扩缩这些应用程序至关重要。借助 EFA，采用消息传递接口 (MPI) 的高性能计算 (HPC) 应用程序和采用 NVIDIA Collective Communications Library (NCCL) 的 Machine Learning (ML) 应用程序可以扩展到数千个 CPU 或 GPU。因此，您同时获得了本地 HPC 集群的应用程序性能，以及AWS云的按需弹性和灵活度。将 EFA 与 Amazon EKS 集群上运行的应用程序集成，可以缩短完成大规模分布式培训工作负载的时间，而无需向集群添加更多实例。有关 EFA 的更多信息，请参阅 [Elastic Fabric Adapter](https://aws.amazon.com/hpc/efa/)。

## 具有 EFA 的实例类型
<a name="efa-instances"></a>

*AWS EFA Kubernetes 设备插件* 支持所有具有 EFA 的 Amazon EC2 实例类型。要查看具有 EFA 的所有实例类型的列表，请参阅《Amazon EC2 用户指南》中的[支持的实例类型](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/efa.html#efa-instance-types)**。但是，要快速运行机器学习应用程序，我们建议除了 EFA 之外，实例还应具备 nVidia GPU、[AWS Inferentia](https://aws.amazon.com/machine-learning/inferentia/) 芯片或 [AWS Trainium](https://aws.amazon.com/machine-learning/trainium/) 芯片等硬件加速芯片。要查看具有硬件加速芯片和 EFA 的实例类型列表，请参阅《Amazon EC2 用户指南》中的[加速型计算](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/efa.html#efa-instance-types)**。

在比较实例类型以便进行选择时，应注意实例类型可用的 EFA 网卡数量以及加速卡数量、CPU 大小和内存大小。您最多可以为每张网卡分配一个 EFA。一个 EFA 计为一个网络接口。要查看每种具有 EFA 的实例类型有多少 EFA 可用，请参阅《Amazon EC2 用户指南》中的[网卡](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-eni.html#network-cards)列表**。

## 仅限 EFA 和 EFA 的接口
<a name="efa-only-interfaces"></a>

*Elastic Fabric Adapter（EFA）* 是一种网络接口，它结合了弹性网络适配器（ENA）和操作系统旁路接口的功能，由 AWS 可扩展的可靠数据报（SRD）协议提供支持。EFA 功能允许应用程序直接与硬件通信，实现低延迟传输。您可以选择使用*仅限 EFA* 的接口仅访问 EFA 功能，从而将通信限制在同一可用区内的接口。

要创建可以具有仅限 EFA 的接口的节点，您必须使用自定义 EC2 启动模板并将 `InterfaceType` 设置为 `efa-only`。在您的自定义启动模板中，您不能将网卡 `0` 设置为仅限 EFA 的接口，因为该接口是 EC2 实例的主网卡和网络接口。您必须拥有 VPC CNI `1.18.5` 或更高版本才能使用仅限 EFA 的接口。如果您使用 Amazon Linux 2，则必须是 `v20240928` 或更高 ami 版本才能使用仅限 EFA 的接口。

以下步骤指导您使用 `eksctl` 创建具有 nVidia GPU 和 EFA 接口的节点的 EKS 集群。您不能使用 `eksctl` 创建使用仅限 EFA 的接口的节点和节点组。

## 先决条件
<a name="efa-prereqs"></a>
+ 现有 Amazon EKS 集群。如果您没有现有集群，请参考[开始使用 Amazon EKS](getting-started.md) 创建一个集群。您的集群必须部署在至少具有一个私有子网的 VPC 中，该子网应当具有足够多的可用 IP 地址以便在其中部署节点。私有子网必须具有由外部设备（如 NAT 网关）提供的出站 Internet 访问。

  如果您计划使用 `eksctl` 创建您的节点组，`eksctl` 还可以为您创建集群。
+ 在您的设备或 AWS CloudShell 上安装和配置 AWS 命令行界面（AWS CLI）的版本 `2.12.3` 或更高版本，或版本 `1.27.160` 或更高版本。要查看当前版本，请使用 `aws --version | cut -d / -f2 | cut -d ' ' -f1`。`yum`、`apt-get` 或适用于 macOS 的 Homebrew 等软件包管理器通常比 AWS CLI 的最新版本落后几个版本。要安装最新版本，请参阅《AWS 命令行界面用户指南》**中的[安装](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html)和[使用 aws configure 快速配置](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html#cli-configure-quickstart-config)。AWS CloudShell 中安装的 AWS CLI 版本也可能比最新版本落后几个版本。要对其进行更新，请参阅《AWS CloudShell 用户指南》**中的[将 AWS CLI 安装到您的主目录](https://docs.aws.amazon.com/cloudshell/latest/userguide/vm-specs.html#install-cli-software)。
+ 您的设备或 AWS CloudShell 上安装了 `kubectl` 命令行工具。该版本可以与集群的 Kubernetes 版本相同，或者最多早于或晚于该版本一个次要版本。例如，如果您的集群版本为 `1.29`，则可以将 `kubectl` 的 `1.28`、`1.29` 或 `1.30` 版本与之配合使用。要安装或升级 `kubectl`，请参阅 [设置 `kubectl` 和 `eksctl`](install-kubectl.md)。
+ 必须先安装适用于 Kubernetes 的 Amazon VPC CNI 插件的 `1.7.10` 或更高版本，才能启动支持多个 Elastic Fabric Adapter 的 Worker 节点（例如 `p4d` 或 `p5`）。有关更新适用于 Kubernetes 的 Amazon VPC CNI 插件版本的更多信息，请参阅[使用 Amazon VPC CNI 将 IP 分配给容器组（pod）](managing-vpc-cni.md)。
+ 对于 p6-b200 实例，您必须使用 EFA 设备插件版本 0.5.6 或更高版本。

**重要**  
在 Kubernetes 中采用 EFA 时需要考虑的一个重要因素，是将 Huge Pages 作为集群中的资源进行配置和管理。有关更多信息，请参阅 Kubernetes 文档中的[管理 Huge Pages](https://kubernetes.io/docs/tasks/manage-hugepages/scheduling-hugepages/)。安装了 EFA 驱动程序的 Amazon EC2 实例会预先分配 5128 个 2MiB 的 Huge Pages，您可以请求将其作为资源在作业规范中使用。

## 创建节点组
<a name="efa-create-nodegroup"></a>

下面的程序通过一个包含 EFA 接口和 GPUDirect RDMA 且受 `p4d.24xlarge` 支持的节点组，帮助您创建节点组，并使用 EFA 针对多节点 NCCL 性能运行一个 NVIDIA Collective Communications Library (NCCL) 示例测试。该示例可用作使用 EFA 在 Amazon EKS 上进行分布式深度学习培训的模板。

1. 确定您想在其中部署节点的 AWS 区域中有哪些支持 EFA 的 Amazon EC2 实例类型可用。将 *region-code* 替换为您想要在其中部署节点组的 AWS 区域。

   ```
   aws ec2 describe-instance-types --region region-code \
       --filters Name=network-info.efa-supported,Values=true \
       --query "InstanceTypes[*].[InstanceType]" --output text
   ```

   部署节点时，您想要部署的实例类型必须在集群所在的 AWS 区域中可用。

1.  确定您想要部署的实例类型在哪些可用区中可用。本教程使用了 `p5.48xlarge` 实例类型，必须在上一步所指定的 AWS 区域的输出中返回该实例类型。在生产集群中部署节点时，请将 *p5.48xlarge* 替换为上一步中返回的任何实例类型。

   ```
   aws ec2 describe-instance-type-offerings --region region-code \
       --location-type availability-zone --filters Name=instance-type,Values=p4d.24xlarge,p5.48xlarge \
       --query 'InstanceTypeOfferings[*].Location' --output text
   ```

   示例输出如下。

   ```
   us-west-2a    us-west-2c    us-west-2b
   ```

   记下返回的可用区，以供后续步骤使用。将节点部署到集群时，VPC 的子网必须在输出返回的其中一个可用区中具有可用的 IP 地址。

1. 使用 `eksctl` 创建节点组。您需要在设备或 AWS CloudShell 上安装 `0.215.0` 版或更高版本的 `eksctl` 命令行工具。要安装或更新 `eksctl`，请参阅 `eksctl` 文档中的 [Installation](https://eksctl.io/installation)。

   1. 将以下内容复制到名为 *efa-cluster.yaml* 的文件中。将 example values 替换为您自己的值。您可以将 `p5.48xlarge` 替换为不同的实例，但是如果您进行替换，请确保 `availabilityZones` 的值为第 1 步中针对实例类型返回的可用区。

      ```
      apiVersion: eksctl.io/v1alpha5
      kind: ClusterConfig
      
      metadata:
        name: my-efa-cluster
        region: region-code
        version: "1.XX"
      
      iam:
        withOIDC: true
      
      availabilityZones: ["us-west-2a", "us-west-2c"]
      
      managedNodeGroups:
        - name: my-efa-ng
          instanceType: p5.48xlarge
          minSize: 1
          desiredCapacity: 2
          maxSize: 3
          availabilityZones: ["us-west-2a"]
          volumeSize: 300
          privateNetworking: true
          efaEnabled: true
      ```

   1. 在现有集群中创建托管节点组。

      ```
      eksctl create nodegroup -f efa-cluster.yaml
      ```

      如果您没有现有集群，可以运行以下命令来创建集群和节点组。

      ```
      eksctl create cluster -f efa-cluster.yaml
      ```
**注意**  
此示例中使用的实例类型具有 GPU，因此在使用 Amazon Linux 2 时，`eksctl` 会在每个实例上为您自动安装 NVIDIA Kubernetes 设备插件。这对于 Bottlerocket 来说不是必需的，因为 NVIDIA 设备插件内置于 Bottlerocket 的 EKS NVIDIA 变体中。如果在节点组配置中，`efaEnabled` 设置为 `true`，`eksctl` 还将在节点上自动部署 EFA 设备插件。

### 将 Bottlerocket 与 EFA 结合使用
<a name="efa-bottlerocket"></a>

Bottlerocket AMI 版本 1.28.0 及更高版本提供对 EFA 的官方支持。要将 Bottlerocket 用于启用 EFA 的节点，请在配置中指定 `amiFamily: Bottlerocket`。如果需要使用自定义 AMI ID，则必须使用标准 `nodeGroups` 而不是 `managedNodeGroups`。

以下为配置示例：

```
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: my-efa-bottlerocket-cluster
  region: region-code
  version: "1.XX"

iam:
  withOIDC: true

availabilityZones: ["us-west-2a", "us-west-2c"]

managedNodeGroups:
  - name: my-efa-bottlerocket-ng
    instanceType: p5.48xlarge
    minSize: 1
    desiredCapacity: 2
    maxSize: 3
    availabilityZones: ["us-west-2a"]
    volumeSize: 300
    privateNetworking: true
    efaEnabled: true
    amiFamily: Bottlerocket
    bottlerocket:
      enableAdminContainer: true
      settings:
        kernel:
          sysctl:
            "vm.nr_hugepages": "3000"  # Configures 3000 * 2Mi = 6000Mi hugepages
```

上方的 `vm.nr_hugepages` sysctl 设置用于配置 2Mi 大页的数量。在此示例中，3000 表示 3000 \$1 2Mi = 6000Mi 的大页。

### 验证 EFA 设备插件是否安装
<a name="verify-efa-device-plugin"></a>

创建带有 `efaEnabled: true` 的节点组时，`eksctl` 会自动为您部署 EFA Kubernetes 设备插件。您可以通过以下方式验证该设备插件是否已安装且运行正常：

1. 检查 DaemonSet 的状态：

   ```
   kubectl get daemonsets -n kube-system
   ```

   示例输出：

   ```
   NAME                                  DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
   aws-efa-k8s-device-plugin-daemonset   2         2         2       2            2           <none>          6m16s
   ...
   ```

   在此示例中，EFA 设备插件 DaemonSet 在两个节点上运行。两者均处于就绪状态且可用。

1. 接下来，验证由 DaemonSet 创建的容器组（pod）：

   ```
   kubectl get pods -n kube-system -l name=aws-efa-k8s-device-plugin
   ```

   示例输出：

   ```
   NAME                                        READY   STATUS    RESTARTS   AGE
   aws-efa-k8s-device-plugin-daemonset-d68bs   1/1     Running   0          6m16s
   aws-efa-k8s-device-plugin-daemonset-w4l8t   1/1     Running   0          6m16s
   ```

   EFA 设备插件容器组（pod）处于“正在运行”状态，这表明该插件已成功部署且可进行操作。

1. 验证资源注册：

   您可以通过描述节点来确认 `vpc.amazonaws.com/efa` 资源是否已注册到 kubelet：

   ```
   kubectl describe nodes
   ```

   如果 EFA 资源已正确注册，则该资源将在节点的“容量”和“可分配资源”下列出。例如：

   ```
   Capacity:
     ...
     vpc.amazonaws.com/efa:  4
   Allocatable:
     ...
     vpc.amazonaws.com/efa:  4
   ```

   此输出确认了节点可以识别 EFA 资源，从而使其可供请求该资源的容器组（pod）使用。

## （可选）测试 EFA 的性能
<a name="efa-application"></a>

建议您测试 EFA 安装。您可以使用 GitHub 上 `aws-samples/awsome-distributed-training` 存储库中的 [NCCL Tests](https://github.com/aws-samples/awsome-distributed-training/tree/main/micro-benchmarks/nccl-tests)。[NCCL Tests](https://github.com/NVIDIA/nccl-tests) 使用 Nvidia 集合通信库来评估网络性能。以下步骤会在 Amazon EKS 上提交 NCCL 测试。

1. 部署 Kubeflow MPI Operator：

   对于 NCCL 测试，您可以应用 Kubeflow MPI Operator。MPI Operator 可以轻松地在 Kubernetes 上运行 Allreduce 模式的分布式训练。有关更多信息，请参阅 GitHub 上的 [MPI Operator](https://github.com/kubeflow/mpi-operator)。

1. 运行多节点 NCCL 性能测试以验证 GPUDirectRDMA/EFA：

   要通过 EFA 使用 GPUDirectRDMA 验证 NCCL 性能，请运行标准 NCCL 性能测试。有关更多信息，请参阅 GitHub 上的官方 [NCCL 测试](https://github.com/NVIDIA/nccl-tests.git)存储库。

   完成以下步骤以运行双节点 NCCL 性能测试。在示例 NCCL 测试作业中，每个 Worker 节点会请求八个 GPU、5210Mi 的 `hugepages-2Mi`、四个 EFA 和 8000Mi 的内存，这意味着每个 Worker 节点都会使用一个 `p5.48xlarge` 实例的所有资源。

   1. 创建 MPIJob 清单：

      将以下内容复制到名为 `nccl-tests.yaml` 的文件中：

      ```
      apiVersion: kubeflow.org/v2beta1
      kind: MPIJob
      metadata:
        name: nccl-tests
      spec:
        runPolicy:
          cleanPodPolicy: Running
          backoffLimit: 20
        slotsPerWorker: 8
        mpiReplicaSpecs:
          Launcher:
            replicas: 1
            template:
               spec:
                restartPolicy: OnFailure
                containers:
                - image: public.ecr.aws/hpc-cloud/nccl-tests:latest
                  imagePullPolicy: IfNotPresent
                  name: test-nccl-launcher
                  env:
                   - name: PATH
                     value: $PATH:/opt/amazon/efa/bin:/usr/bin
                  command:
                  - /opt/amazon/openmpi/bin/mpirun
                  - --allow-run-as-root
                  - --tag-output
                  - -np
                  - "16"
                  - -N
                  - "8"
                  - --bind-to
                  - none
                  - -x
                  - PATH
                  - -x
                  - LD_LIBRARY_PATH
                  - -x
                  - NCCL_DEBUG=INFO
                  - -x
                  - NCCL_BUFFSIZE=8388608
                  - -x
                  - NCCL_P2P_NET_CHUNKSIZE=524288
                  - -x
                  - NCCL_TUNER_PLUGIN=/opt/amazon/ofi-nccl/lib/x86_64-linux-gnu/libnccl-ofi-tuner.so
                  - --mca
                  - pml
                  - ^cm,ucx
                  - --mca
                  - btl
                  - tcp,self
                  - --mca
                  - btl_tcp_if_exclude
                  - lo,docker0,veth_def_agent
                  - /opt/nccl-tests/build/all_reduce_perf
                  - -b
                  - "8"
                  - -e
                  - "16G"
                  - -f
                  - "2"
                  - -g
                  - "1"
                  - -c
                  - "1"
                  - -n
                  - "100"
          Worker:
            replicas: 2
            template:
              spec:
                nodeSelector:
                  node.kubernetes.io/instance-type: "p5.48xlarge"
                containers:
                - image: public.ecr.aws/hpc-cloud/nccl-tests:latest
                  imagePullPolicy: IfNotPresent
                  name: nccl-tests-worker
                  volumeMounts:
                  - name: shmem
                    mountPath: /dev/shm
                  resources:
                    limits:
                      nvidia.com/gpu: 8
                      hugepages-2Mi: 5120Mi
                      vpc.amazonaws.com/efa: 32
                      memory: 32000Mi
                    requests:
                      nvidia.com/gpu: 8
                      hugepages-2Mi: 5120Mi
                      vpc.amazonaws.com/efa: 32
                      memory: 32000Mi
                volumes:
                - name: shmem
                  hostPath:
                    path: /dev/shm
      ```

   1. 应用 NCCL-tests MPIJob：

      通过应用清单提交 `MPIJob`。这将创建两个 `p5.48xlarge` Amazon EC2 实例。

      ```
      kubectl apply -f nccl-tests.yaml
      ```

      示例输出如下。

      ```
      mpijob.kubeflow.org/nccl-tests created
      ```

   1. 验证作业是否启动了容器组（pod）：

      查看正在运行的容器组（pod）。

      ```
      kubectl get pods
      ```

      示例输出如下。

      ```
      NAME                             READY   STATUS     RESTARTS   AGE
      nccl-tests-launcher-nbql9    0/1     Init:0/1   0          2m49s
      nccl-tests-worker-0          1/1     Running    0          2m49s
      nccl-tests-worker-1          1/1     Running    0          2m49s
      ```

      MPI Operator 创建一个启动程序容器组（pod）和 2 个工作线程容器组（pod）（每个节点上一个）。

   1. 使用日志来验证作业是否成功运行：

      查看 `nccl-tests-launcher` 容器组（pod）的日志。将 *nbql9* 替换为输出中的值。

      ```
      kubectl logs -f nccl-tests-launcher-nbql9
      ```

如果测试成功完成，则可以部署使用 Nvidia 集合通信库的应用程序。