在 Batch 中使用多实例任务来运行消息传递接口 (MPI) 应用程序

使用多实例任务可在多个计算节点上同时运行 Azure Batch 任务。 这些任务可在 Batch 中实现高性能计算方案,例如消息传递接口 (MPI) 应用程序。 本文介绍如何使用 Azure.Compute.Batch 库执行多实例任务。

注意

虽然本文中的示例侧重于Azure。计算.Batch、MS-MPI 和Windows计算节点,此处讨论的多实例任务概念适用于其他平台和技术(例如,Linux 节点上的 Python 和 Intel MPI)。

多实例任务概述

在 Batch 中,每个任务通常是在单个计算节点上执行 --将多个任务提交给作业,Batch 服务将每个任务安排在节点上执行。 但是,可以通过配置任务的“多实例设置”,告知批处理改为创建一个主要任务和多个子任务,并在多个节点上执行它们。

展示多实例设置概述的示意图。

将带有多实例设置的任务提交给作业时,Batch 会执行多实例任务特有的几个步骤:

  1. 批处理服务根据多实例设置创建一个主要任务和多个子任务。 任务(主要任务和所有子任务)的总数与用户在多实例设置中指定的实例(计算节点)数相符。
  2. 批处理将其中一个计算节点指定为节点,将主要任务安排在主节点上执行。 将子任务安排在已分配给多实例任务的剩余计算节点上执行,一个节点一个子任务。
  3. 主要任务和所有子任务会下载在多实例设置中指定的任何通用资源文件
  4. 下载通用资源文件之后,主任务和子任务将执行多实例设置中指定的协调命令。 通常使用协调命令准备节点,以便执行任务。 该操作可能包括启动后台服务(例如 Microsoft MPIsmpd.exe)及验证节点是否能够处理节点间消息。
  5. 在主要任务和所有子任务成功完成协调命令以后,主要任务会在主节点上执行应用程序命令。 应用程序命令是多实例任务本身的命令行,只由主要任务执行。 在基于 MS-MPI 的解决方案中,你可以在此处使用 mpiexec.exe 执行已启用 MPI 的应用程序。

注意

尽管它在功能上不同,但“多实例任务”不是 BatchStartTaskBatchJobPreparationTask 等唯一任务类型。 多实例任务只是一个标准的 Batch 任务(Azure.Compute.Batch 中的 BatchTask),其多实例设置已配置好。 在本文中,我们将它称为多实例任务

多实例任务的要求

多实例任务需要使用启用了节点间通信且禁用了并发任务执行的池。 若要禁用并发任务执行,请将 BatchAccountPoolData.TaskSlotsPerNode 属性设置为 1。

注意

Batch 限制已启用节点间通信的池的大小。

此代码片段演示如何使用 Azure.ResourceManager.Batch 库为多实例任务创建池。

ArmClient armClient = new ArmClient(new DefaultAzureCredential());

ResourceIdentifier batchAccountResourceId =
    BatchAccountResource.CreateResourceIdentifier("subscriptionId", "resourceGroupName", "accountName");
BatchAccountResource batchAccount = armClient.GetBatchAccountResource(batchAccountResourceId);

BatchAccountPoolCollection poolCollection = batchAccount.GetBatchAccountPools();

BatchAccountPoolData poolData = new BatchAccountPoolData()
{
    VmSize = "standard_d1_v2",
    DeploymentConfiguration = new BatchDeploymentConfiguration()
    {
        VmConfiguration = new BatchVmConfiguration(
            imageReference: new BatchImageReference()
            {
                Publisher = "MicrosoftWindowsServer",
                Offer = "WindowsServer",
                Sku = "2019-datacenter-core",
                Version = "latest"
            },
            nodeAgentSkuId: "batch.node.windows amd64")
    },
    ScaleSettings = new BatchAccountPoolScaleSettings()
    {
        FixedScale = new BatchAccountFixedScaleSettings() { TargetDedicatedNodes = 3 }
    },

    // Multi-instance tasks require inter-node communication, and those nodes
    // must run only one task at a time.
    InterNodeCommunication = InterNodeCommunicationState.Enabled,
    TaskSlotsPerNode = 1
};

注意

如果你尝试在禁用了节点间通信的池中运行多实例任务,或者在 taskSlotsPerNode 值大于 1 的池中运行该任务,则该任务始终不会被调度——它会无限期地保持在“active”状态。

启用了 InterComputeNodeCommunication 的池将不允许自动取消预配节点。

使用 StartTask 安装 MPI

若要通过多实例任务运行 MPI 应用程序,首先需在池中的计算节点上安装 MPI 实现(例如 MS-MPI 或 Intel MPI)。 这是使用 BatchAccountPoolStartTask 的好时机,每当节点加入池或重启时,它都执行。 此代码片段将启动任务添加到池定义中,该定义将 MS-MPI 安装包指定为 资源文件。 资源文件下载到节点之后,将执行启动任务的命令行。 在本示例中,命令行执行 MS-MPI 的无人参与安装。

// Add a start task to the pool which we use for installing MS-MPI on
// the nodes as they join the pool (or when they are restarted).
poolData.StartTask = new BatchAccountPoolStartTask()
{
    CommandLine = "cmd /c MSMpiSetup.exe -unattend -force",
    UserIdentity = new BatchUserIdentity()
    {
        AutoUser = new BatchAutoUserSpecification() { ElevationLevel = BatchUserAccountElevationLevel.Admin }
    },
    WaitForSuccess = true,
};
poolData.StartTask.ResourceFiles.Add(new BatchResourceFile()
{
    HttpUri = new Uri("https://mystorageaccount.blob.core.chinacloudapi.cn/mycontainer/MSMpiSetup.exe"),
    FilePath = "MSMpiSetup.exe"
});

// Create the fully configured pool.
ArmOperation<BatchAccountPoolResource> pool = await poolCollection.CreateOrUpdateAsync(
    WaitUntil.Completed, "MultiInstanceSamplePool", poolData);

远程直接内存访问 (RDMA)

在批处理池中为计算节点选择支持 RDMA 的大小(例如 A9)时,MPI 应用程序可以使用 Azure 的高性能、低延迟的远程直接内存访问 (RDMA) 网络。

Azure 中虚拟机的大小(用于 VirtualMachineConfiguration 池)或 云服务的大小(用于 CloudServicesConfiguration 池)中,查找标示为“支持 RDMA”的大小。

注意

若要充分利用 Linux 计算节点上的 RDMA,必须使用节点上的 Intel MPI

使用 Azure.Compute.Batch 创建多实例任务

现在我们已经介绍了池要求和 MPI 包的安装,接下来让我们创建多实例任务。 在此代码片段中,我们将创建标准 BatchTaskCreateOptions,然后配置其 MultiInstanceSettings 属性。 如前所述,多实例任务不是独特的任务类型,而只是已配置多实例设置的标准 Batch 任务。

// Create the multi-instance task. Its command line is the "application command"
// and will be executed *only* by the primary, and only after the primary and
// subtasks execute the CoordinationCommandLine.
BatchTaskCreateOptions myMultiInstanceTask = new BatchTaskCreateOptions(
    id: "mymultiinstancetask",
    commandLine: "cmd /c mpiexec.exe -wdir %AZ_BATCH_TASK_SHARED_DIR% MyMPIApplication.exe")
{
    // Configure the task's MultiInstanceSettings. The CoordinationCommandLine will be executed by
    // the primary and all subtasks.
    MultiInstanceSettings = new MultiInstanceSettings(
        @"cmd /c start cmd /c ""%MSMPI_BIN%\smpd.exe"" -d")
    {
        NumberOfInstances = numberOfNodes
    }
};

myMultiInstanceTask.MultiInstanceSettings.CommonResourceFiles.Add(new ResourceFile()
{
    HttpUri = new Uri("https://mystorageaccount.blob.core.chinacloudapi.cn/mycontainer/MyMPIApplication.exe"),
    FilePath = "MyMPIApplication.exe"
});

// Submit the task to the job. Batch will take care of splitting it into subtasks and
// scheduling them for execution on the nodes.
await myBatchClient.CreateTaskAsync("mybatchjob", myMultiInstanceTask);

主要任务和子任务

创建任务的多实例设置时,需要指定用于执行任务的计算节点数目。 将任务提交到作业时,Batch 服务会创建一个任务以及足够数量的子任务,使它们合起来与您指定的节点数相匹配。

这些任务被分配的整数 ID 范围为 0 到 numberOfInstances - 1。 ID 为 0 的任务是主要任务,其他所有 ID 都是子任务。 例如,如果你为任务创建以下多实例设置,则主要任务的 ID 为 0,子任务的 ID 为 1 到 9。

int numberOfNodes = 10;
myMultiInstanceTask.MultiInstanceSettings = new MultiInstanceSettings("coord-cmd")
{
    NumberOfInstances = numberOfNodes
};

主节点

当用户提交多实例任务时,批处理服务会将其中一个计算节点指定为“主”节点,并将主要任务安排在主节点上执行。 子任务安排在已分配给多实例任务的剩余节点上执行。

协调命令

协调命令由主任务和子任务共同执行。

对协调命令的调用是阻塞式的——在所有子任务的协调命令都成功返回之前,Batch 不会执行应用程序命令。 因此,协调命令应该启动任何所需的后台服务,确认它们已准备好可供使用,并退出。 例如,在使用 MS-MPI 第 7 版的方案中,此协调命令在节点上启动 SMPD 服务,并退出:

cmd /c start cmd /c ""%MSMPI_BIN%\smpd.exe"" -d

请注意此协调命令中使用 start。 这是必需的,因为 smpd.exe 应用程序不会在执行后立即返回。 如果不使用 start 命令,此协调命令就不返回,因此将阻止执行应用程序命令。

应用程序命令

主要任务及所有子任务完成执行协调命令之后,只有主要任务执行多实例任务的命令行。 我们将此命令行称为应用程序命令,以便与协调命令区分开来。

对于 MS-MPI 应用程序,请使用应用程序命令通过 mpiexec.exe 执行已启用 MPI 的应用程序。 例如,以下是使用 MS-MPI 第 7 版的方案所执行的应用程序命令:

cmd /c ""%MSMPI_BIN%\mpiexec.exe"" -c 1 -wdir %AZ_BATCH_TASK_SHARED_DIR% MyMPIApplication.exe

注意

由于 MS-MPI 的 mpiexec.exe 默认使用 CCP_NODES 变量(请参阅环境变量),上述示例应用程序命令行已排除该变量。

环境变量

Batch 会在分配给某个多实例任务的计算节点上创建几个专用于多实例任务的环境变量。 协调命令行和应用程序命令行可以引用这些环境变量,就像其所执行的脚本和程序一样。

以下环境变量由多实例任务所使用的批处理服务创建:

  • CCP_NODES
  • AZ_BATCH_NODE_LIST
  • AZ_BATCH_HOST_LIST
  • AZ_BATCH_MASTER_NODE
  • AZ_BATCH_TASK_SHARED_DIR
  • AZ_BATCH_IS_CURRENT_NODE_MASTER

如需这些环境变量以及其他 Batch 计算节点环境变量的完整详细信息(包括内容和可见性),请参阅计算节点环境变量

提示

Batch Linux MPI 代码示例介绍了如何使用这些环境变量中的部分变量。

资源文件

多实例任务需要考虑两组资源文件:所有任务(主要任务和子任务)下载的通用资源文件,以及为多实例任务本身指定的资源文件只有主要任务下载)。

可以在任务的多实例设置中指定一个或多个通用资源文件。 主要任务及所有子任务从 Azure 存储将这些通用资源文件下载到每个节点的任务共享目录。 可以使用 AZ_BATCH_TASK_SHARED_DIR 环境变量从应用程序命令和协调命令行访问任务共享目录。 AZ_BATCH_TASK_SHARED_DIR 路径在所有分配给多实例任务的节点上都是相同的,因此可在主要任务和所有子任务之间共享单个协调命令。 就远程访问而言,Batch 并不会“共享”该目录,但如前面关于环境变量的提示中提到的那样,你可以将其用作挂载点或共享点。

默认情况下,为多实例任务本身指定的资源文件下载到任务的工作目录 AZ_BATCH_TASK_WORKING_DIR。 如前所述,与常见的资源文件不同,只有主任务会下载为多实例任务本身指定的资源文件。

重要

在命令行中,请始终使用环境变量 AZ_BATCH_TASK_SHARED_DIRAZ_BATCH_TASK_WORKING_DIR 来引用这些目录。 请勿尝试手动构造路径。

任务生存期

主要任务的生存期控制整个多实例任务的生存期。 当主要任务退出时,所有子任务就会终止。 主容器的退出代码就是任务的退出代码,因此会据此判断任务是成功还是失败,以决定是否重试。

如果任何子任务失败,例如退出时返回代码不是零,则整个多实例任务失败。 然后终止并重试多实例任务,直到到达重试限制为止。

删除多实例任务时,Batch 服务也会删除主要任务和所有子任务。 所有子任务目录及其文件从计算节点中删除,如同在标准任务中一样。

多实例任务的 BatchTaskConstraints(例如 MaxTaskRetryCountMaxWallClockTimeRetentionTime 属性)与标准任务一样会得到遵守,并且适用于主任务和所有子任务。 但如果你将多实例任务添加到作业之后更改了“RetentionTime”属性,此更改仅应用于主要任务,所有子任务将继续使用原来的“RetentionTime”。

如果最近的任务是多实例任务的一部分,计算节点的最近任务列表会显示子任务的 ID。

获取有关子任务的信息

若要使用 Azure.Compute.Batch 库获取有关子任务的信息,请调用 BatchClient.GetTaskSubTasks 方法。 此方法返回所有子任务的相关信息,以及已执行任务的计算节点的相关信息。 你可以根据此信息判断每个子任务的根目录、池 ID、当前状态、退出代码等。 可以将此信息与 BatchClient.GetNodeFile 方法结合使用来获取子任务的文件。 请注意,此方法不会返回主要任务 (ID 0) 的相关信息。

注意

除非另有说明,否则,Azure.Compute.Batch 中对多实例 BatchTask 本身进行操作的方法适用于主任务。 例如,在多实例任务上调用 GetTaskFiles 该方法时,只返回主任务的文件。

以下代码段演示如何获取子任务信息,以及从它们执行所在的节点请求文件的内容。

// Obtain the multi-instance task from the Batch service
BatchTask myMultiInstanceTask = await batchClient.GetTaskAsync("mybatchjob", "mymultiinstancetask");
_ = myMultiInstanceTask;

// Now iterate over the subtasks for the task and print their stdout and stderr
// output if the subtask has completed
await foreach (BatchSubtask subtask in batchClient.GetSubTasksAsync("mybatchjob", "mymultiinstancetask"))
{
    Console.WriteLine("subtask: {0}", subtask.Id);
    Console.WriteLine("exit code: {0}", subtask.ExitCode);

    if (subtask.State == BatchSubtaskState.Completed)
    {
        BatchNode node = await batchClient.GetNodeAsync(
            subtask.NodeInfo.PoolId,
            subtask.NodeInfo.NodeId);

        BinaryData stdOut = await batchClient.GetNodeFileAsync(
            subtask.NodeInfo.PoolId, node.Id, $"{subtask.NodeInfo.TaskRootDirectory}/stdout.txt");
        BinaryData stdErr = await batchClient.GetNodeFileAsync(
            subtask.NodeInfo.PoolId, node.Id, $"{subtask.NodeInfo.TaskRootDirectory}/stderr.txt");

        Console.WriteLine("node: {0}:", node.Id);
        Console.WriteLine("stdout.txt: {0}", stdOut);
        Console.WriteLine("stderr.txt: {0}", stdErr);
    }
    else
    {
        Console.WriteLine("\tSubtask {0} is in state {1}", subtask.Id, subtask.State);
    }
}

代码示例

GitHub 上的 MultiInstanceTasks 代码示例演示了如何通过多实例任务在 Batch 计算节点上运行 MS-MPI 应用程序。 请按照下列步骤运行代码示例。

准备工作

  1. 下载并安装 MS-MPI SDK 和可再发行组件安装程序。 安装完成后,你可以验证是否设置了 MS MPI 环境变量。
  2. 生成 MPIHelloWorld 示例 MPI 程序的发行版。 这是将由多实例任务在计算节点上运行的程序。
  3. 创建包含 MPIHelloWorld.exe(在步骤 2 搭建)和 MSMpiSetup.exe(在步骤 1 下载)的 zip 文件。 需在下一步将此 zip 文件作为应用程序包上传。
  4. 通过 Azure 门户创建名为“MPIHelloWorld”的 Batch 应用程序,并将在上一步创建的 zip 文件指定为“1.0”版应用程序包。 有关详细信息,请参阅上载和管理应用程序

提示

生成发行MPIHelloWorld.exe,这样你就不用在应用程序包中加入任何其他依赖项(如 msvcp140d.dllvcruntime140d.dll)。

执行

  1. 从 GitHub 下载 azure-batch-samples .zip 文件

  2. 在 Visual Studio 2019 中打开 MultiInstanceTasks 解决方案MultiInstanceTasks.sln 解决方案文件位于:

    azure-batch-samples\CSharp\ArticleProjects\MultiInstanceTasks\

  3. Microsoft.Azure.Batch.Samples.Common 项目的 AccountSettings.settings 中输入你的 Batch 和存储帐户凭据。

  4. 生成并运行 MultiInstanceTasks 解决方案,在批处理池中的计算节点上执行 MPI 示例应用程序。

  5. 可选:在删除资源前,请先通过 Azure 门户Batch Explorer 检查示例池、作业和任务(“MultiInstanceSamplePool”、“MultiInstanceSampleJob”、“MultiInstanceSampleTask”)。

提示

如未安装 Visual Studio,可前往 Visual Studio Community 免费下载软件。

MultiInstanceTasks.exe 的输出与下面类似:

Creating pool [MultiInstanceSamplePool]...
Creating job [MultiInstanceSampleJob]...
Adding task [MultiInstanceSampleTask] to job [MultiInstanceSampleJob]...
Awaiting task completion, timeout in 00:30:00...

Main task [MultiInstanceSampleTask] is in state [Completed] and ran on compute node [tvm-1219235766_1-20161017t162002z]:
---- stdout.txt ----
Rank 2 received string "Hello world" from Rank 0
Rank 1 received string "Hello world" from Rank 0

---- stderr.txt ----

Main task completed, waiting 00:00:10 for subtasks to complete...

---- Subtask information ----
subtask: 1
        exit code: 0
        node: tvm-1219235766_3-20161017t162002z
        stdout.txt:
        stderr.txt:
subtask: 2
        exit code: 0
        node: tvm-1219235766_2-20161017t162002z
        stdout.txt:
        stderr.txt:

Delete job? [yes] no: yes
Delete pool? [yes] no: yes

Sample complete, hit ENTER to exit...

后续步骤