Uso de tareas de instancias múltiples para ejecutar aplicaciones de la Interfaz de paso de mensajes (MPI) en Batch

Las tareas de instancias múltiples le permiten ejecutar una tarea de Azure Batch en varios nodos de proceso al mismo tiempo. Estas tareas permiten escenarios de informática de alto rendimiento como las aplicaciones de interfaz de paso de mensajes (MPI) en Batch. En este artículo, aprenderá cómo ejecutar tareas de múltiples instancias mediante la biblioteca Azure.Compute.Batch.

Nota

Aunque los ejemplos de este artículo se centran en Azure. Compute.Batch, MS-MPI y Windows nodos de proceso, los conceptos de tareas de varias instancias que se describen aquí son aplicables a otras plataformas y tecnologías (Python e Intel MPI en nodos de Linux, por ejemplo).

Información general de las tareas de instancias múltiples

En Batch, cada tarea se ejecuta normalmente en un solo nodo de proceso: se envían varias tareas a un trabajo y el servicio Batch programa la ejecución de cada tarea en un nodo. No obstante, si establece la configuración de instancias múltiples en una tarea, le estará diciendo a Batch que cree una tarea principal y varias tareas secundarias que se ejecutarán después en varios nodos.

Diagrama que muestra información general de la configuración de instancias múltiples.

Al enviar una tarea con configuración de instancias múltiples a un trabajo, el servicio Batch realiza varios pasos que son específicos de las tareas de instancias múltiples:

  1. El servicio Batch crea una tarea principal y varias tareas secundarias en función de la configuración de instancias múltiples definida. El número total de tareas (principales y todas las subtareas) coincide con el número de instancias (nodos de proceso) especificado en la configuración de múltiples instancias.
  2. Batch designa uno de los nodos de proceso como el maestro, y programa la tarea principal para que se ejecute en el maestro. Programa las subtareas para que se ejecuten en el resto de los nodos de proceso asignados a la tarea de múltiples instancias, una subtarea por nodo.
  3. Estas tareas, tanto la principal como las subtareas, descargan los archivos de recursos comunes que se especifican en la configuración de múltiples instancias.
  4. Cuando se han descargado los archivos de recursos comunes, la tarea principal y las subtareas ejecutan el comando de coordinación que se especifica en la configuración de instancias múltiples. El comando de coordinación normalmente se utiliza para preparar los nodos para ejecutar la tarea. Esto puede incluir iniciar servicios en segundo plano (como los de smpd.exe) y comprobar que los nodos están listos para procesar los mensajes entre nodos.
  5. La tarea principal ejecuta el comando de aplicación en el nodo maestro después de que la principal y todas las subtareas hayan completado correctamente el comando de coordinación. El comando de la aplicación es la línea de comandos de la tarea de múltiples instancias, y solo es ejecutado por la tarea principal. En una solución basada en MS-MPI, se trata del lugar donde ejecuta la aplicación habilitada para MPI mediante mpiexec.exe.

Nota

Aunque es funcionalmente distinto, la "tarea de varias instancias" no es un tipo de tarea único como BatchStartTask o BatchJobPreparationTask. La tarea multiinstancia es simplemente una tarea Batch estándar (BatchTask en Azure.Compute.Batch) en la que se ha configurado la opción de multiinstancia. En este artículo, nos referiremos a ella como tarea de instancias múltiples.

Requisitos para tareas multi-instancia

Las tareas de múltiples instancias requieren un grupo con la comunicación ente nodos habilitada y la ejecución simultánea de tareas deshabilitada. Para deshabilitar la ejecución simultánea de tareas, establezca la propiedad BatchAccountPoolData.TaskSlotsPerNode en 1.

Nota

Batch limita el tamaño de un grupo que tiene habilitada la comunicación entre nodos.

Este fragmento de código muestra cómo crear una agrupación para tareas de múltiples instancias mediante la biblioteca 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
};

Nota

Si intenta ejecutar una tarea de varias instancias en un grupo con la comunicación entre nodos deshabilitada o con un valor taskSlotsPerNode superior a 1, la tarea nunca se programa, sino que permanece indefinidamente en estado "activo".

Los grupos con InterComputeNodeCommunication habilitado no permitirán automáticamente el desaprovisionamiento del nodo.

Uso de StartTask para instalar MPI

Para ejecutar aplicaciones MPI con una tarea de instancias múltiples, primero debe instalar una implementación de MPI (por ejemplo, MS-MPI o Intel MPI) en los nodos de proceso del grupo. Este es un buen momento para usar un BatchAccountPoolStartTask, que se ejecuta cada vez que un nodo se une a un grupo o se reinicia. Este fragmento de código agrega una tarea de inicio a la definición del grupo que especifica el paquete de configuración de MS-MPI como un archivo de recursos. La línea de comandos de la tarea de inicio se ejecuta una vez que el archivo de recurso se ha descargado en el nodo. En este caso, la línea de comandos realiza una instalación desatendida de 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.windows.net/mycontainer/MSMpiSetup.exe"),
    FilePath = "MSMpiSetup.exe"
});

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

Acceso directo a memoria remota (RDMA)

Cuando elige un tamaño con capacidad RDMA como A9 para los nodos de cómputo del grupo de Batch, su aplicación MPI puede aprovechar la red de acceso directo a memoria remota (RDMA) de alto rendimiento y baja latencia de Azure.

Busque los tamaños especificados como compatibles con RDMA en Tamaños de máquinas virtuales de Azure (para grupos de VirtualMachineConfiguration) o Tamaños de Cloud Services (para grupos de CloudServicesConfiguration).

Nota

Para sacar el máximo provecho a RDMA en los nodos de proceso de Linux, debe utilizar Intel MPI en esos nodos.

Cree una tarea de varias instancias con Azure. Compute.Batch

Ahora que hemos analizado los requisitos de grupo y la instalación del paquete MPI, vamos a crear la tarea de instancias múltiples. En este fragmento de código, creamos un batchTaskCreateOptions estándar y, a continuación, configuramos su propiedad MultiInstanceSettings . Como se mencionó anteriormente, la tarea de instancias múltiples no es un tipo de tarea distinto, sino una tarea de Batch estándar configurada con la opción de instancias múltiples.

// 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.windows.net/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);

Tarea principal y subtareas

Cuando se crea la configuración de instancias múltiples para una tarea, se especifica el número de nodos de proceso que ejecutarán la tarea. Cuando se envía la tarea a un trabajo, el servicio Batch crea una tarea principal y suficientes subtareas que, juntas, coinciden con el número de nodos especificado.

A estas tareas se les asigna a un identificador entero del intervalo de 0 a numberOfInstances - 1. La tarea con el identificador 0 es la tarea principal, y todos los demás identificadores son subtareas. Por ejemplo, si crea la siguiente configuración de instancias múltiples para una tarea, la tarea principal tendrá un identificador de 0, y las subtareas tendrán los identificadores del 1 al 9.

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

Nodo maestro

Cuando se envía una tarea de múltiples instancias, el servicio Batch designa uno de los nodos de proceso como el nodo "maestro" y programa la tarea principal para que se ejecute en el nodo maestro. Las subtareas se programan para ejecutarse en el resto de los nodos asignados a la tarea de múltiples instancias.

comando de coordinación

El comando de coordinación ejecuta tanto tareas principales como subtareas.

La invocación del comando de coordinación se bloquea: el servicio Batch no ejecuta el comando de aplicación hasta que el comando de coordinación se ha devuelto correctamente para todas las subtareas. Por lo tanto, el comando de coordinación debe iniciar los servicios en segundo plano necesarios, comprobar que están listos para utilizarse y luego cerrarse. Por ejemplo, este comando de coordinación para una solución que utiliza la versión 7 de MS-MPI inicia el servicio SMPD en el nodo y luego se cierra:

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

Observe el uso de start en este comando de coordinación. Esto es necesario porque la aplicación smpd.exe no devuelve resultados inmediatamente después de la ejecución. Sin el uso del comando start, este comando de coordinación no devolvería resultados y, por tanto, impediría que se ejecutara el comando de aplicación.

Comando de aplicación

Una vez que la tarea principal y todas las subtareas han terminado de ejecutar el comando de coordinación, solola tarea principal ejecuta la línea de comandos de la tarea de instancias múltiples. Llamaremos a este el comando de aplicación para distinguirlo del comando de coordinación.

Para las aplicaciones de MS-MPI, use el comando de aplicación para ejecutar la aplicación habilitada para MPI con mpiexec.exe. Por ejemplo, este es un comando de aplicación para una solución mediante la versión 7 de MS-MPI:

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

Nota

Dado que mpiexec.exe de MS-MPI usa la variable CCP_NODES de manera predeterminada (consulte Variables de entorno), la línea de comandos de aplicación del ejemplo anterior la excluye.

Variables de entorno

Batch crea varias variables de entorno específicas de las tareas de múltiples instancias en los nodos de proceso asignados a una tarea de múltiples instancias. Las líneas de comando de coordinación y de la aplicación pueden hacer referencia a estas variables de entorno, como los scripts y programas que se ejecutan.

Las siguientes variables de entorno las crea el servicio Batch para su uso por las tareas de múltiples instancias:

  • 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 obtener detalles completos sobre estas y las demás variables de entorno del nodo de proceso de Batch, incluido su contenido y la visibilidad, consulte Variables de entorno del nodo de proceso.

Sugerencia

El ejemplo de código de Linux MPI de Batch contiene un ejemplo de cómo se pueden usar varias de estas variables de entorno.

Archivos de recursos

Hay dos conjuntos de archivos de recursos que se deben tener en cuenta para las tareas de múltiples instancias: archivos de recursos comunes que descargan todas las tareas (tanto principales como subtareas) y archivos de recursos especificados para la propia tarea de múltiples instancias, que descarga solo la tarea principal.

Puede especificar uno o más archivos de recursos comunes en la configuración de instancias múltiples de una tarea. La tarea principal y todas las subtareas descargan estos archivos de recursos comunes desde el Azure Storage en el directorio compartido de tareas de cada nodo. Puede tener acceso al directorio compartido de tareas desde las líneas de comandos de coordinación y aplicación mediante la variable de entorno AZ_BATCH_TASK_SHARED_DIR . La ruta de acceso AZ_BATCH_TASK_SHARED_DIR es idéntica en todos los nodos asignados a la tarea de múltiples instancias, por lo que puede compartir un único comando de coordinación entre el principal y todas las subtareas. Batch no "comparte" el directorio en el sentido de acceso remoto, pero puede usarlo como un punto de montaje o compartición, tal como se mencionó anteriormente en el consejo sobre las variables de entorno.

Los archivos de recursos que especifique para la propia tarea de múltiples instancias se descargan en el directorio de trabajo de la tarea, AZ_BATCH_TASK_WORKING_DIR, de forma predeterminada. Como se mencionó, a diferencia de los archivos de recursos comunes, solo la tarea principal descarga los archivos de recursos especificados para la propia tarea de múltiples instancias.

Importante

Utilice siempre las variables de entorno AZ_BATCH_TASK_SHARED_DIR y AZ_BATCH_TASK_WORKING_DIR para hacer referencia a estos directorios en las líneas de comando. No intente construir las rutas de acceso manualmente.

Duración de la tarea

La duración de la tarea principal controla la duración de toda la tarea de instancias múltiples. Cuando se cierra la tarea principal, todas las subtareas se terminan. El código de salida del primario es el código de salida de la tarea, y por lo tanto, se utiliza para determinar el éxito o fracaso de la tarea con el propósito de intentar nuevamente.

Si se produce un error en alguna de las subtareas, por ejemplo, se cierra con un código de error distinto de cero, la tarea de instancias múltiples entera dará error. Entonces la tarea de instancias múltiples se termina y se reintenta, hasta su límite de reintento.

Cuando se elimina una tarea de instancias múltiples, el servicio Batch también elimina la tarea principal y todas las subtareas. Todos los directorios de subtarea y sus archivos se eliminan de los nodos de proceso, igual que en el caso de una tarea estándar.

BatchTaskConstraints para una tarea de varias instancias, como las propiedades MaxTaskRetryCount, MaxWallClockTime y RetentionTime , se respetan como son para una tarea estándar y se aplican a las subtareas principal y a todas las subtareas. Sin embargo, si cambia la propiedad RetentionTime después de agregar la tarea de instancias múltiples al trabajo, este cambio se aplica solo a la tarea principal, y todas las subtareas siguen usando la propiedad RetentionTime original.

La lista de tareas recientes de un nodo de proceso refleja el identificador de una subtarea si la tarea reciente era parte de una tarea de instancias múltiples.

Obtención de información sobre las subtareas

Para obtener información sobre las subtareas al usar la biblioteca Azure.Compute.Batch, llame al método BatchClient.GetTaskSubTasks. Este método devuelve información sobre todas las subtareas e información sobre el nodo de proceso que ejecuta las tareas. A partir de esta información, puede determinar el directorio raíz de cada subtarea, el identificador de grupo, su estado actual, el código de salida, etc. Puede usar esta información en combinación con el método BatchClient.GetNodeFile para obtener los archivos de la subtarea. Tenga en cuenta que este método no devuelve información de la tarea principal (id. 0).

Nota

A menos que se indique lo contrario, los métodos de Azure.Compute.Batch que operan en la propia BatchTask de varias instancias se aplican solo a la tarea principal. Por ejemplo, cuando se llama al GetTaskFiles método en una tarea de varias instancias, solo se devuelven los archivos de la tarea principal.

El fragmento de código siguiente muestra cómo obtener información de la subtarea y cómo solicitar contenido de archivos de los nodos en los que se ejecuta.

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

Código de ejemplo

En el ejemplo de código MultiInstanceTasks que se encuentra en GitHub se muestra cómo puede usarse una tarea de instancias múltiples para ejecutar una aplicación de MS-MPI en los nodos de proceso de Batch. Siga los pasos a continuación para ejecutar el ejemplo.

Preparación

  1. Descargue los instaladores del SDK de MS-MPI SDK y de Redist e instálelos. Después de la instalación, puede comprobar que se hayan establecido las variables de entorno de MS-MPI.
  2. Construya una versión Release del programa de ejemplo MPIHelloWorld de MPI. La tarea de instancias múltiples ejecutará este programa en los nodos de proceso.
  3. Cree un archivo ZIP que contenga MPIHelloWorld.exe (creado en el paso 2) y MSMpiSetup.exe (descargado en el paso 1). En el siguiente paso, cargará este archivo zip como paquete de aplicación.
  4. Use Azure Portal para crear una aplicación de Batch llamada "MPIHelloWorld" y establezca el archivo zip que creó en el paso anterior como versión "1.0" del paquete de aplicación. Para más información, consulte Carga y administración de aplicaciones.

Sugerencia

Crear una versión de lanzamiento de MPIHelloWorld.exe asegura que no necesites incluir dependencias adicionales (por ejemplo, msvcp140d.dll o vcruntime140d.dll) en tu paquete de aplicación.

Ejecución

  1. Descargue el archivo azure-batch-samples.zip de GitHub.

  2. Abra la solución MultiInstanceTasks en Visual Studio 2019. El archivo de solución de MultiInstanceTasks.sln se encuentra en:

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

  3. Especifique las credenciales de su cuenta de Batch y Storage en AccountSettings.settings en el proyecto Microsoft.Azure.Batch.Samples.Common.

  4. Compile y ejecute la solución MultiInstanceTasks. De este modo, ejecutará la aplicación de ejemplo de MPI en los nodos de proceso de un grupo de Batch.

  5. Opcional: Puede usar Azure Portal o el Batch Explorer para examinar el grupo, el trabajo y la tarea de ejemplo ("MultiInstanceSamplePool", "MultiInstanceSampleJob" y "MultiInstanceSampleTask") antes de eliminar los recursos.

Sugerencia

Si aún no tiene Visual Studio, puede descargar gratis Visual Studio Community.

La salida de MultiInstanceTasks.exe será similar a la siguiente:

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

Pasos siguientes