Utilizar tarefas de várias instâncias para executar aplicações de Interface de Passagem de Mensagens (MPI) no Batch

As tarefas de várias instâncias permitem-lhe executar uma tarefa de Azure Batch em vários nós de computação em simultâneo. Estas tarefas permitem cenários de computação de elevado desempenho, como aplicações de Interface de Passagem de Mensagens (MPI) no Batch. Neste artigo, vai aprender a executar tarefas de várias instâncias com a biblioteca .NET do Batch .

Nota

Embora os exemplos neste artigo se concentrem nos nós de computação do Batch .NET, MS-MPI e Windows, os conceitos de tarefas de várias instâncias abordados aqui são aplicáveis a outras plataformas e tecnologias (MPI Python e Intel em nós do Linux, por exemplo).

Descrição geral da tarefa de várias instâncias

No Batch, cada tarefa é normalmente executada num único nó de computação– submete várias tarefas para uma tarefa e o serviço Batch agenda cada tarefa para execução num nó. No entanto, ao configurar as definições de várias instâncias de uma tarefa, indica ao Batch para, em vez disso, criar uma tarefa primária e várias subtarefas que são depois executadas em vários nós.

Diagrama a mostrar uma descrição geral das definições de várias instâncias.

Quando submete uma tarefa com definições de várias instâncias para uma tarefa, o Batch executa vários passos exclusivos para tarefas de várias instâncias:

  1. O serviço Batch cria uma subtarefaprincipal e várias com base nas definições de várias instâncias. O número total de tarefas (principal mais todas as subtarefas) corresponde ao número de instâncias (nós de computação) que especificar nas definições de várias instâncias.
  2. O Batch designa um dos nós de computação como mestre e agenda a tarefa principal a executar no mestre. Agenda as subtarefas para serem executadas no resto dos nós de computação alocados à tarefa de várias instâncias, uma subtarefa por nó.
  3. As subtarefas primárias e todas as subtarefas transferem todos os ficheiros de recursos comuns que especificar nas definições de várias instâncias.
  4. Depois de os ficheiros de recursos comuns terem sido transferidos, as principais e subtarefas executam o comando de coordenação que especificar nas definições de várias instâncias. Normalmente, o comando de coordenação é utilizado para preparar nós para executar a tarefa. Isto pode incluir o início de serviços em segundo plano (como os MPI dasmpd.exe Microsoft) e a verificação de que os nós estão prontos para processar mensagens entre nós.
  5. A tarefa principal executa o comando da aplicação no nó principal depois de o comando de coordenação ter sido concluído com êxito pelas subtarefas primárias e todas as subtarefas. O comando da aplicação é a linha de comandos da própria tarefa de várias instâncias e é executado apenas pela tarefa primária. Numa solução baseada em MS-MPI , é aqui que executa a aplicação compatível com MPI com mpiexec.exe.

Nota

Embora seja funcionalmente distinta, a "tarefa de várias instâncias" não é um tipo de tarefa exclusivo, como StartTask ou JobPreparationTask. A tarefa de várias instâncias é simplesmente uma tarefa padrão do Batch (CloudTask no Batch .NET) cujas definições de várias instâncias foram configuradas. Neste artigo, referimo-nos a isto como a tarefa de várias instâncias.

Requisitos para tarefas de várias instâncias

As tarefas de várias instâncias requerem um conjunto com comunicação entre nós ativada e com a execução de tarefas simultânea desativada. Para desativar a execução de tarefas simultâneas, defina a propriedade CloudPool.TaskSlotsPerNode como 1.

Nota

O Batch limita o tamanho de um conjunto que tem a comunicação entre nós ativada.

Este fragmento de código mostra como criar um conjunto para tarefas de várias instâncias com a biblioteca .NET do Batch.

CloudPool myCloudPool =
    myBatchClient.PoolOperations.CreatePool(
        poolId: "MultiInstanceSamplePool",
        targetDedicatedComputeNodes: 3
        virtualMachineSize: "standard_d1_v2",
        VirtualMachineConfiguration: new VirtualMachineConfiguration(
        imageReference: new ImageReference(
                        publisher: "MicrosoftWindowsServer",
                        offer: "WindowsServer",
                        sku: "2019-datacenter-core",
                        version: "latest"),
        nodeAgentSkuId: "batch.node.windows amd64");

// Multi-instance tasks require inter-node communication, and those nodes
// must run only one task at a time.
myCloudPool.InterComputeNodeCommunicationEnabled = true;
myCloudPool.TaskSlotsPerNode = 1;

Nota

Se tentar executar uma tarefa de várias instâncias num conjunto com comunicação internade desativada ou com um valor taskSlotsPerNode superior a 1, a tarefa nunca é agendada. Permanece indefinidamente no estado "ativo".

Os conjuntos com InterComputeNodeCommunication ativado não permitirão automaticamente o desaprovisionamento do nó.

Utilizar um StartTask para instalar o MPI

Para executar aplicações MPI com uma tarefa de várias instâncias, primeiro tem de instalar uma implementação MPI (MS-MPI ou Intel MPI, por exemplo) nos nós de computação no conjunto. Esta é uma boa altura para utilizar uma StartTask, que é executada sempre que um nó associa um conjunto ou é reiniciado. Este fragmento de código cria uma StartTask que especifica o pacote de configuração MS-MPI como um ficheiro de recursos. A linha de comandos da tarefa de início é executada depois de o ficheiro de recurso ser transferido para o nó. Neste caso, a linha de comandos efetua uma instalação autónoma do MS-MPI.

// Create a StartTask for the pool which we use for installing MS-MPI on
// the nodes as they join the pool (or when they are restarted).
StartTask startTask = new StartTask
{
    CommandLine = "cmd /c MSMpiSetup.exe -unattend -force",
    ResourceFiles = new List<ResourceFile> { new ResourceFile("https://mystorageaccount.blob.core.windows.net/mycontainer/MSMpiSetup.exe", "MSMpiSetup.exe") },
    UserIdentity = new UserIdentity(new AutoUserSpecification(elevationLevel: ElevationLevel.Admin)),
    WaitForSuccess = true
};
myCloudPool.StartTask = startTask;

// Commit the fully configured pool to the Batch service to actually create
// the pool and its compute nodes.
await myCloudPool.CommitAsync();

Acesso remoto direto à memória (RDMA)

Quando escolhe um tamanho compatível com RDMA , como A9, para os nós de computação no conjunto do Batch, a aplicação MPI pode tirar partido da rede rdma (acesso remoto de memória remota) de elevado desempenho e baixa latência do Azure.

Procure os tamanhos especificados como "com capacidade RDMA" em Tamanhos para máquinas virtuais no Azure (para conjuntos VirtualMachineConfiguration) ou Tamanhos para Serviços Cloud (para conjuntos CloudServicesConfiguration).

Nota

Para tirar partido do RDMA nos nós de computação do Linux, tem de utilizar o Intel MPI nos nós.

Criar uma tarefa de várias instâncias com o Batch .NET

Agora que abordámos os requisitos do conjunto e a instalação do pacote MPI, vamos criar a tarefa de várias instâncias. Neste fragmento, criamos uma CloudTask padrão e, em seguida, configuramos a respetiva propriedade MultiInstanceSettings . Conforme mencionado anteriormente, a tarefa de várias instâncias não é um tipo de tarefa distinto, mas uma tarefa padrão do Batch configurada com definições de várias instâncias.

// 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.
CloudTask myMultiInstanceTask = new CloudTask(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.
myMultiInstanceTask.MultiInstanceSettings =
    new MultiInstanceSettings(numberOfNodes) {
    CoordinationCommandLine = @"cmd /c start cmd /c ""%MSMPI_BIN%\smpd.exe"" -d",
    CommonResourceFiles = new List<ResourceFile> {
    new ResourceFile("https://mystorageaccount.blob.core.windows.net/mycontainer/MyMPIApplication.exe",
                     "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.JobOperations.AddTaskAsync("mybatchjob", myMultiInstanceTask);

Tarefa principal e subtarefas

Quando cria as definições de várias instâncias para uma tarefa, especifica o número de nós de computação que vão executar a tarefa. Quando submete a tarefa a uma tarefa, o serviço Batch cria uma tarefa principal e subtarefas suficientes que, em conjunto, correspondem ao número de nós que especificou.

A estas tarefas é atribuído um ID de número inteiro no intervalo de 0 a numberOfInstances - 1. A tarefa com o ID 0 é a tarefa principal e todos os outros IDs são subtarefas. Por exemplo, se criar as seguintes definições de várias instâncias para uma tarefa, a tarefa principal teria um ID de 0 e as subtarefas teriam IDs de 1 a 9.

int numberOfNodes = 10;
myMultiInstanceTask.MultiInstanceSettings = new MultiInstanceSettings(numberOfNodes);

Nó principal

Quando submete uma tarefa de várias instâncias, o serviço Batch designa um dos nós de computação como o nó "principal" e agenda a tarefa primária a executar no nó principal. As subtarefas estão agendadas para serem executadas no resto dos nós alocados à tarefa de várias instâncias.

Comando coordenação

O comando de coordenação é executado pelas subtarefas primárias e subtarefas.

A invocação do comando de coordenação está a bloquear - o Batch não executa o comando da aplicação até que o comando de coordenação tenha devolvido com êxito para todas as subtarefas. Por conseguinte, o comando de coordenação deve iniciar todos os serviços em segundo plano necessários, verificar se estão prontos para utilização e, em seguida, sair. Por exemplo, este comando de coordenação para uma solução com o MS-MPI versão 7 inicia o serviço SMPD no nó e, em seguida, sai:

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

Repare na utilização de neste comando de start coordenação. Isto é necessário porque a aplicação smpd.exe não devolve imediatamente após a execução. Sem a utilização do comando iniciar, este comando de coordenação não devolveria e, por conseguinte, bloquearia a execução do comando da aplicação.

Comando da aplicação

Assim que a tarefa primária e todas as subtarefas terminarem a execução do comando de coordenação, a linha de comandos da tarefa de várias instâncias é executada apenas pela tarefa primária. Chamamos a isto o comando da aplicação para distingui-lo do comando de coordenação.

Para aplicações MS-MPI, utilize o comando da aplicação para executar a aplicação com MPI ativada com mpiexec.exe. Por exemplo, eis um comando de aplicação para uma solução com a versão 7 do MS-MPI:

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

Nota

Uma vez que MS-MPI mpiexec.exe utiliza a CCP_NODES variável por predefinição (veja Variáveis de ambiente), a linha de comandos da aplicação de exemplo acima exclui-a.

Variáveis de ambiente

O Batch cria várias variáveis de ambiente específicas para tarefas de várias instâncias nos nós de computação alocados a uma tarefa de várias instâncias. As linhas de comandos de coordenação e aplicação podem referenciar estas variáveis de ambiente, assim como os scripts e programas que executam.

As seguintes variáveis de ambiente são criadas pelo serviço Batch para utilização por tarefas de várias instâncias:

  • 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

Para obter detalhes completos sobre estas e as outras variáveis de ambiente de nós de computação do Batch, incluindo os respetivos conteúdos e visibilidade, veja Variáveis de ambiente de nó de computação.

Dica

O exemplo de código MPI do Batch Linux contém um exemplo de como várias destas variáveis de ambiente podem ser utilizadas.

Ficheiros de recursos

Existem dois conjuntos de ficheiros de recursos a considerar para tarefas de várias instâncias: ficheiros de recursos comuns que todas as tarefas transferem (primárias e subtarefas) e os ficheiros de recursos especificados para a própria tarefa de várias instâncias, que apenas a tarefa primária transfere.

Pode especificar um ou mais ficheiros de recursos comuns nas definições de várias instâncias de uma tarefa. Estes ficheiros de recursos comuns são transferidos do Armazenamento do Azure para o diretório partilhado de tarefas de cada nó pelas subtarefas primárias e todas. Pode aceder ao diretório partilhado de tarefas a partir de linhas de comandos de aplicação e coordenação com a variável de AZ_BATCH_TASK_SHARED_DIR ambiente. O AZ_BATCH_TASK_SHARED_DIR caminho é idêntico em todos os nós alocados à tarefa de várias instâncias, pelo que pode partilhar um único comando de coordenação entre as subtarefas primária e todas as subtarefas. O Batch não "partilha" o diretório num sentido de acesso remoto, mas pode utilizá-lo como um ponto de montagem ou partilha, conforme mencionado anteriormente na sugestão sobre variáveis de ambiente.

Os ficheiros de recursos que especificar para a tarefa de várias instâncias em si são transferidos para o diretório de trabalho da tarefa, AZ_BATCH_TASK_WORKING_DIR, por predefinição. Conforme mencionado, ao contrário dos ficheiros de recursos comuns, apenas a tarefa principal transfere os ficheiros de recursos especificados para a própria tarefa de várias instâncias.

Importante

Utilize sempre as variáveis AZ_BATCH_TASK_SHARED_DIR de ambiente e AZ_BATCH_TASK_WORKING_DIR para fazer referência a estes diretórios nas linhas de comandos. Não tente construir os caminhos manualmente.

Duração da tarefa

A duração da tarefa primária controla a duração de toda a tarefa de várias instâncias. Quando a primária sai, todas as subtarefas são terminadas. O código de saída do principal é o código de saída da tarefa e, portanto, é utilizado para determinar o êxito ou a falha da tarefa para fins de repetição.

Se alguma das subtarefas falhar, sair com um código de retorno diferente de zero, por exemplo, toda a tarefa de várias instâncias falhará. Em seguida, a tarefa de várias instâncias é terminada e repetida, até ao limite de repetições.

Quando elimina uma tarefa de várias instâncias, as subtarefas primárias e todas as subtarefas também são eliminadas pelo serviço Batch. Todos os diretórios de subtarefas e os respetivos ficheiros são eliminados dos nós de computação, tal como para uma tarefa padrão.

As propriedades TaskConstraints para uma tarefa de várias instâncias, como as propriedades MaxTaskRetryCount, MaxWallClockTime e RetentionTime , são honradas como são para uma tarefa padrão e aplicam-se às propriedades primária e todas as subtarefas. No entanto, se alterar a propriedadeRetentionTime depois de adicionar a tarefa de várias instâncias à tarefa, esta alteração é aplicada apenas à tarefa primária e todas as subtarefas continuam a utilizar o RetentionTime original.

A lista de tarefas recentes de um nó de computação reflete o ID de uma subtarefa se a tarefa recente fizer parte de uma tarefa de várias instâncias.

Obter informações sobre subtarefas

Para obter informações sobre subtarefas com a biblioteca .NET do Batch, chame o método CloudTask.ListSubtasks . Este método devolve informações sobre todas as subtarefas e informações sobre o nó de computação que executou as tarefas. A partir destas informações, pode determinar o diretório de raiz de cada subtarefa, o ID do conjunto, o estado atual, o código de saída e muito mais. Pode utilizar estas informações em combinação com o método PoolOperations.GetNodeFile para obter os ficheiros da subtarefa. Tenha em atenção que este método não devolve informações para a tarefa primária (ID 0).

Nota

Salvo indicação em contrário, os métodos .NET do Batch que operam no CloudTask de várias instâncias aplicam-se apenas à tarefa primária. Por exemplo, quando chama o método CloudTask.ListNodeFiles numa tarefa de várias instâncias, apenas os ficheiros da tarefa primária são devolvidos.

O fragmento de código seguinte mostra como obter informações de subtarefas, bem como pedir conteúdos de ficheiros a partir dos nós em que foram executados.

// Obtain the job and the multi-instance task from the Batch service
CloudJob boundJob = batchClient.JobOperations.GetJob("mybatchjob");
CloudTask myMultiInstanceTask = boundJob.GetTask("mymultiinstancetask");

// Now obtain the list of subtasks for the task
IPagedEnumerable<SubtaskInformation> subtasks = myMultiInstanceTask.ListSubtasks();

// Asynchronously iterate over the subtasks and print their stdout and stderr
// output if the subtask has completed
await subtasks.ForEachAsync(async (subtask) =>
{
    Console.WriteLine("subtask: {0}", subtask.Id);
    Console.WriteLine("exit code: {0}", subtask.ExitCode);

    if (subtask.State == SubtaskState.Completed)
    {
        ComputeNode node =
            await batchClient.PoolOperations.GetComputeNodeAsync(subtask.ComputeNodeInformation.PoolId,
                                                                 subtask.ComputeNodeInformation.ComputeNodeId);

        NodeFile stdOutFile = await node.GetNodeFileAsync(subtask.ComputeNodeInformation.TaskRootDirectory + "\\" + Constants.StandardOutFileName);
        NodeFile stdErrFile = await node.GetNodeFileAsync(subtask.ComputeNodeInformation.TaskRootDirectory + "\\" + Constants.StandardErrorFileName);
        stdOut = await stdOutFile.ReadAsStringAsync();
        stdErr = await stdErrFile.ReadAsStringAsync();

        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);
    }
});

Exemplo de código

O exemplo de código MultiInstanceTasks no GitHub demonstra como utilizar uma tarefa de várias instâncias para executar uma aplicação MS-MPI em nós de computação do Batch. Siga os passos abaixo para executar o exemplo.

Preparação

  1. Transfira os instaladores ms-MPI SDK e Redist e instale-os. Após a instalação, pode verificar se as variáveis de ambiente MS-MPI foram definidas.
  2. Crie uma versão de versão do programa MPI de exemplo MPI MPI MPI do MPIHelloWorld . Este é o programa que será executado em nós de computação pela tarefa de várias instâncias.
  3. Crie um ficheiro zip que MPIHelloWorld.exe contenha (que criou no passo 2) e MSMpiSetup.exe (que transferiu no passo 1). Irá carregar este ficheiro zip como um pacote de aplicação no próximo passo.
  4. Utilize o portal do Azure para criar uma aplicação do Batch denominada "MPIHelloWorld" e especifique o ficheiro zip que criou no passo anterior como a versão "1.0" do pacote de aplicação. Veja Carregar e gerir aplicações para obter mais informações.

Dica

Criar uma versão de Versão do MPIHelloWorld.exe garante que não tem de incluir dependências adicionais (por exemplo, msvcp140d.dll ou vcruntime140d.dll) no pacote de aplicações.

Execução

  1. Transfira o ficheiro .zip azure-batch-samples a partir do GitHub.

  2. Abra a solução MultiInstanceTasks no Visual Studio 2019. O MultiInstanceTasks.sln ficheiro de solução está localizado em:

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

  3. Introduza as credenciais da conta do Batch e do Storage no AccountSettings.settings projeto Microsoft.Azure.Batch.Samples.Common .

  4. Crie e execute a solução MultiInstanceTasks para executar a aplicação de exemplo de MPI em nós de computação num conjunto do Batch.

  5. Opcional: utilize o portal do Azure ou o Explorador do Batch para examinar o conjunto de exemplo, a tarefa e a tarefa ("MultiInstanceSamplePool", "MultiInstanceSampleJob", "MultiInstanceSampleTask") antes de eliminar os recursos.

Dica

Pode transferir Visual Studio Community gratuitamente se ainda não tiver o Visual Studio.

O resultado de MultiInstanceTasks.exe é semelhante ao seguinte:

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...

Passos seguintes