Tutorial: Ejecución de una carga de trabajo paralela con Azure Batch mediante la API de .NET

Use Azure Batch para ejecutar aplicaciones de informática de alto rendimiento (HPC) en paralelo y a gran escala, de manera eficaz en Azure. Este tutorial le guía a través de un ejemplo de C# de ejecución de carga de trabajo paralela con Batch. Aprenderá lo que es un flujo de trabajo de aplicación de Batch común y a interactuar con los recursos de Batch y Storage mediante programación. Aprenderá a:

  • Agregar un paquete de aplicación a la cuenta de Batch
  • Autenticarse en las cuentas de Batch y Storage
  • Cargar archivos de entrada en Storage
  • Crear un grupo de nodos de proceso para ejecutar una aplicación
  • Crear un trabajo y tareas para procesar archivos de entrada
  • Supervisar la ejecución de las tareas
  • Recuperación de archivos de salida

En este tutorial, convertiremos archivos multimedia MP4 a formato MP3 en paralelo con la herramienta de código de abierto ffmpeg.

Si no tiene una suscripción a Azure, cree una cuenta gratuita de Azure antes de empezar.

Requisitos previos

Inicio de sesión en Azure

Inicie sesión en Azure Portal en https://portal.azure.com.

Incorporación de un paquete de aplicación

Use Azure Portal para agregar ffmpeg a la cuenta de Batch como paquete de aplicación. Los paquetes de aplicación ayudan a administrar aplicaciones de tareas y a implementarlas en los nodos de proceso del grupo.

  1. En Azure Portal, haga clic en Más servicios>Cuentas de Batch y en el nombre de la cuenta de Batch.
  2. Haga clic en Aplicaciones>Agregar.
  3. Escriba ffmpeg como identificador de aplicación y especifique 4.3.1 como versión de paquete. Seleccione el archivo zip de ffmpeg que ha descargado y haga clic en Aceptar. El paquete de aplicación de ffmpeg se agrega a la cuenta de Batch.

Add application package

Obtención de credenciales de la cuenta

En este ejemplo, debe especificar las credenciales de las cuentas de Batch y Storage. Azure Portal es una manera sencilla de obtener las credenciales necesarias. (también se pueden obtener mediante las API de Azure o las herramientas de línea de comandos).

  1. Seleccione Todos los servicios>Cuentas de Batch y, después, seleccione el nombre de la cuenta de Batch.

  2. Para ver las credenciales de Batch, haga clic en Claves. Copie los valores de cuenta de Batch, URL y Clave de acceso principal en un editor de texto.

  3. Para ver el nombre y las claves de la cuenta de almacenamiento, seleccione Cuenta de almacenamiento. Copie los valores de Nombre de la cuenta de almacenamiento y Key1 en un editor de texto.

Descarga y ejecución del ejemplo

Descarga del ejemplo

Descargue o clone la aplicación de ejemplo desde GitHub. Para clonar el repositorio de la aplicación de ejemplo con un cliente de Git, use el siguiente comando:

git clone https://github.com/Azure-Samples/batch-dotnet-ffmpeg-tutorial.git

Vaya al directorio que contiene el archivo BatchDotNetFfmpegTutorial.sln de la solución de Visual Studio.

Abra el archivo de la solución en Visual Studio y actualice las cadenas de credenciales de Program.cs con los valores obtenidos para las cuentas. Por ejemplo:

// Batch account credentials
private const string BatchAccountName = "mybatchaccount";
private const string BatchAccountKey  = "xxxxxxxxxxxxxxxxE+yXrRvJAqT9BlXwwo1CwF+SwAYOxxxxxxxxxxxxxxxx43pXi/gdiATkvbpLRl3x14pcEQ==";
private const string BatchAccountUrl  = "https://mybatchaccount.mybatchregion.batch.azure.com";

// Storage account credentials
private const string StorageAccountName = "mystorageaccount";
private const string StorageAccountKey  = "xxxxxxxxxxxxxxxxy4/xxxxxxxxxxxxxxxxfwpbIC5aAWA8wDu+AFXZB827Mt9lybZB1nUcQbQiUrkPtilK5BQ==";

Nota

Para simplificar el ejemplo, las credenciales de la cuenta de Batch y Storage aparecen en texto no cifrado. En la práctica, se recomienda restringir el acceso a las credenciales y hacer referencia a ellas en el código mediante las variables de entorno o un archivo de configuración. Para ver ejemplos, consulte el repositorio de ejemplos de código de Azure Batch.

Además, asegúrese de que la referencia del paquete de aplicación de ffmpeg de la solución coincida con el identificador y la versión del paquete de ffmpeg que cargó en la cuenta de Batch. Por ejemplo, ffmpeg y 4.3.1.

const string appPackageId = "ffmpeg";
const string appPackageVersion = "4.3.1";

Compilación y ejecución del proyecto de ejemplo

Compile y ejecute la aplicación en Visual Studio o en la línea de comandos con los comandos dotnet build y dotnet run. Después de ejecutar la aplicación, examine el código para ver qué es lo que hace cada parte de la aplicación. Por ejemplo, en Visual Studio:

  • Haga clic con el botón derecho en la solución en el Explorador de soluciones y en Compilar solución.

  • Si se le solicita, confirme la restauración de los paquetes NuGet. Si necesita descargar los paquetes que faltan, asegúrese de que el Administrador de paquetes NuGet está instalado.

A continuación, ejecútelo. Al ejecutar la aplicación de ejemplo, la salida de la consola es similar a la siguiente. Durante la ejecución, se experimenta una pausa en Monitoring all tasks for 'Completed' state, timeout in 00:30:00... mientras se inician los nodos de proceso del grupo.

Sample start: 11/19/2018 3:20:21 PM

Container [input] created.
Container [output] created.
Uploading file LowPriVMs-1.mp4 to container [input]...
Uploading file LowPriVMs-2.mp4 to container [input]...
Uploading file LowPriVMs-3.mp4 to container [input]...
Uploading file LowPriVMs-4.mp4 to container [input]...
Uploading file LowPriVMs-5.mp4 to container [input]...
Creating pool [WinFFmpegPool]...
Creating job [WinFFmpegJob]...
Adding 5 tasks to job [WinFFmpegJob]...
Monitoring all tasks for 'Completed' state, timeout in 00:30:00...
Success! All tasks completed successfully within the specified timeout period.
Deleting container [input]...

Sample end: 11/19/2018 3:29:36 PM
Elapsed time: 00:09:14.3418742

Vaya a la cuenta de Batch de Azure Portal para supervisar el grupo, los nodos de proceso, el trabajo y las tareas. Por ejemplo, para ver un mapa térmico de los nodos de proceso del grupo, haga clic en Grupos>WinFFmpegPool.

Con las tareas en ejecución, el mapa térmico es similar al siguiente:

Pool heat map

El tiempo de ejecución típico es de aproximadamente 10 minutos al ejecutar la aplicación con la configuración predeterminada. La creación del grupo es lo que más tarda.

Recuperación de archivos de salida

Puede usar Azure Portal para descargar los archivos MP3 de salida generados por las tareas de ffmpeg.

  1. Haga clic en Todos los servicios>Cuentas de almacenamiento y haga clic en el nombre de la cuenta de almacenamiento.
  2. Haga clic en Blobs>salida.
  3. Haga clic con el botón derecho en uno de los archivos MP3 de salida y, después, haga clic en Descargar. Para abrir o guardar el archivo, siga las indicaciones del explorador.

Download output file

Aunque no se muestra en este ejemplo, los archivos también se pueden descargar mediante programación desde los nodos de proceso o desde el contenedor de almacenamiento.

Revisión del código

En las secciones siguientes se desglosan los pasos que lleva a cabo la aplicación de ejemplo para procesar una carga de trabajo en el servicio Batch. Consulte el archivo Program.cs en la solución mientras lee el resto de este artículo, ya que no se explican todas las líneas de código del ejemplo.

Autenticación de los clientes de Blob y Batch

Para interactuar con la cuenta de almacenamiento vinculada, la aplicación utiliza la biblioteca cliente de Azure Storage para .NET. Crea una referencia a la cuenta con CloudStorageAccount, con autenticación de clave compartida. A continuación, crea CloudBlobClient.

// Construct the Storage account connection string
string storageConnectionString = String.Format("DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1}",
                                StorageAccountName, StorageAccountKey);

// Retrieve the storage account
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(storageConnectionString);

CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();

La aplicación crea un objeto BatchClient para crear y administrar los grupos, los trabajos y las tareas en el servicio Batch. El cliente de Batch del ejemplo utiliza la autenticación de clave compartida. Batch también admite la autenticación a través de Azure Active Directory para usuarios individuales o una aplicaciones desatendidas.

BatchSharedKeyCredentials sharedKeyCredentials = new BatchSharedKeyCredentials(BatchAccountUrl, BatchAccountName, BatchAccountKey);

using (BatchClient batchClient = BatchClient.Open(sharedKeyCredentials))
...

Carga de archivos de entrada

La aplicación pasa el objeto blobClient al método CreateContainerIfNotExistAsync para crear un contenedor de almacenamiento para los archivos de entrada (formato MP4) y uno para la salida de la tarea.

CreateContainerIfNotExistAsync(blobClient, inputContainerName);
CreateContainerIfNotExistAsync(blobClient, outputContainerName);

A continuación, los archivos se cargan al contenedor de entrada desde la carpeta InputFiles local. Los archivos de almacenamiento se definen como objetos ResourceFile de Batch para que el servicio los descargue después en nodos de proceso.

En la carga de los archivos participan dos métodos de Program.cs:

  • UploadFilesToContainerAsync: devuelve una colección de objetos ResourceFile y llama internamente a UploadResourceFileToContainerAsync para cargar todos los archivos que se pasan en el parámetro inputFilePaths.
  • UploadResourceFileToContainerAsync: carga los archivos como blobs en el contenedor de entrada. Después de cargar el archivo, obtiene una firma de acceso compartido (SAS) para el blob y devuelve un objeto ResourceFile que lo representa.
string inputPath = Path.Combine(Environment.CurrentDirectory, "InputFiles");

List<string> inputFilePaths = new List<string>(Directory.GetFileSystemEntries(inputPath, "*.mp4",
    SearchOption.TopDirectoryOnly));

List<ResourceFile> inputFiles = await UploadFilesToContainerAsync(
  blobClient,
  inputContainerName,
  inputFilePaths);

Para más información sobre cómo cargar archivos como blobs en una cuenta de almacenamiento con. NET, consulte Carga, descarga y enumeración de blobs mediante .NET.

Creación de un grupo de nodos de proceso

Después, en el ejemplo se crea un grupo de nodos de proceso en la cuenta de Batch con una llamada a CreatePoolIfNotExistAsync. Este método definido usa el método BatchClient.PoolOperations.CreatePool para establecer el número de nodos, el tamaño de la máquina virtual y la configuración del grupo. En este caso, un objeto VirtualMachineConfiguration especifica un valor de ImageReference en una imagen de Windows Server publicada en Azure Marketplace. Batch es compatible con una amplia gama de imágenes de máquina virtual de Azure Marketplace, así como con las imágenes de máquina virtual personalizadas.

El número de nodos y el tamaño de la máquina virtual se establecen mediante constantes definidas. Batch admite nodos especializados y nodos de acceso puntual, y en los grupos puede utilizar ambos. Los nodos dedicados están reservados para el grupo. Los nodos de acceso puntual se ofrecen a precio reducido por la capacidad sobrante de las máquinas virtuales de Azure. Los nodos de acceso puntual dejan de estar disponibles si Azure no tiene capacidad suficiente. En el ejemplo, de forma predeterminada se crea un grupo que contiene solo 5 nodos de acceso puntual con el tamaño Standard_A1_v2.

Nota

Asegúrese de comprobar las cuotas de nodo. Consulte Límites y cuotas del servicio Batch para obtener instrucciones sobre cómo crear una solicitud de cuota.

La aplicación ffmpeg se implementa en los nodos de proceso mediante la incorporación de un valor ApplicationPackageReference a la configuración del grupo.

El método CommitAsync envía el grupo al servicio Batch.

ImageReference imageReference = new ImageReference(
    publisher: "MicrosoftWindowsServer",
    offer: "WindowsServer",
    sku: "2016-Datacenter-smalldisk",
    version: "latest");

VirtualMachineConfiguration virtualMachineConfiguration =
    new VirtualMachineConfiguration(
    imageReference: imageReference,
    nodeAgentSkuId: "batch.node.windows amd64");

pool = batchClient.PoolOperations.CreatePool(
    poolId: poolId,
    targetDedicatedComputeNodes: DedicatedNodeCount,
    targetLowPriorityComputeNodes: LowPriorityNodeCount,
    virtualMachineSize: PoolVMSize,
    virtualMachineConfiguration: virtualMachineConfiguration);

pool.ApplicationPackageReferences = new List<ApplicationPackageReference>
    {
    new ApplicationPackageReference {
    ApplicationId = appPackageId,
    Version = appPackageVersion}};

await pool.CommitAsync();  

Creación de un trabajo

Un trabajo de Batch especifica un grupo en el que ejecutar tareas y valores de configuración opcionales, como la prioridad y la programación del trabajo. En el ejemplo se crea un trabajo con una llamada a CreateJobAsync. Este método definido usa el método BatchClient.JobOperations.CreateJob para crear un trabajo en el grupo.

El método CommitAsync envía el trabajo al servicio Batch. Inicialmente, el trabajo no tiene tareas.

CloudJob job = batchClient.JobOperations.CreateJob();
job.Id = JobId;
job.PoolInformation = new PoolInformation { PoolId = PoolId };

await job.CommitAsync();

Creación de tareas

En el ejemplo se crean tareas en el trabajo con una llamada al método AddTasksAsync, que crea una lista de objetos CloudTask. Los objetos CloudTask ejecutan ffmpeg para procesar un objeto ResourceFile de entrada con una propiedad CommandLine. Al crear el grupo, ffmpeg se instaló previamente en todos los nodos. En este caso, la línea de comandos ejecuta ffmpeg para convertir los archivos MP4 de entrada (vídeo) en archivos MP3 (audio).

En el ejemplo se crea un objeto OutputFile para el archivo MP3 después de ejecutar la línea de comandos. Los archivos de salida de la tarea (en este caso, uno) se cargan en un contenedor en la cuenta de Storage vinculada mediante la propiedad OutputFiles de la tarea. Anteriormente en el ejemplo de código, una dirección URL de firma de acceso compartido (outputContainerSasUrl) se ha obtenido para proporcionar acceso de escritura al contenedor de salida. Tenga en cuenta las condiciones establecidas en el objeto outputFile. Solo se carga un archivo de salida de una tarea en el contenedor después de que la tarea se haya completado correctamente (OutputFileUploadCondition.TaskSuccess). Vea el código de ejemplo en GitHub para obtener más detalles sobre la implementación.

A continuación, en el ejemplo se agregan tareas al trabajo con el método AddTask, que las pone en cola para que se ejecuten en los nodos de proceso.

Reemplace la ruta de acceso del archivo ejecutable por el nombre de la versión que descargó. En este código de ejemplo se usa el ejemplo ffmpeg-4.3.1-2020-09-21-full_build.

 // Create a collection to hold the tasks added to the job.
List<CloudTask> tasks = new List<CloudTask>();

for (int i = 0; i < inputFiles.Count; i++)
{
    string taskId = String.Format("Task{0}", i);

    // Define task command line to convert each input file.
    string appPath = String.Format("%AZ_BATCH_APP_PACKAGE_{0}#{1}%", appPackageId, appPackageVersion);
    string inputMediaFile = inputFiles[i].FilePath;
    string outputMediaFile = String.Format("{0}{1}",
        System.IO.Path.GetFileNameWithoutExtension(inputMediaFile),
        ".mp3");
    string taskCommandLine = String.Format("cmd /c {0}\\ffmpeg-4.3.1-2020-09-21-full_build\\bin\\ffmpeg.exe -i {1} {2}", appPath, inputMediaFile, outputMediaFile);

    // Create a cloud task (with the task ID and command line)
    CloudTask task = new CloudTask(taskId, taskCommandLine);
    task.ResourceFiles = new List<ResourceFile> { inputFiles[i] };

    // Task output file
    List<OutputFile> outputFileList = new List<OutputFile>();
    OutputFileBlobContainerDestination outputContainer = new OutputFileBlobContainerDestination(outputContainerSasUrl);
    OutputFile outputFile = new OutputFile(outputMediaFile,
       new OutputFileDestination(outputContainer),
       new OutputFileUploadOptions(OutputFileUploadCondition.TaskSuccess));
    outputFileList.Add(outputFile);
    task.OutputFiles = outputFileList;
    tasks.Add(task);
}

// Add tasks as a collection
await batchClient.JobOperations.AddTaskAsync(jobId, tasks);
return tasks

Supervisión de las tareas

Cuando Batch agrega tareas a un trabajo, las pone en cola automáticamente y las programa para que se ejecuten en nodos de proceso del grupo asociado. Según la configuración que especifique, Batch controla la administración de las colas, la programación, los reintentos y otras labores de administración de tareas.

Existen varios enfoques para supervisar la ejecución de tareas. En este ejemplo se define un método MonitorTasks para informar solo de los estados de finalización y de tarea correcta o incorrecta. El código MonitorTasks especifica un valor de ODATADetailLevel para seleccionar eficazmente solo la información mínima sobre las tareas. A continuación, crea TaskStateMonitor, que proporciona utilidades auxiliares para supervisar los estados de la tarea. En MonitorTasks, el ejemplo espera que todas las tareas alcancen el estado TaskState.Completed en un tiempo determinado. A continuación, finaliza el trabajo e informa de las tareas que se completaran, pero que puedan haber producido errores, como un código de salida distinto de cero.

TaskStateMonitor taskStateMonitor = batchClient.Utilities.CreateTaskStateMonitor();
try
{
    await taskStateMonitor.WhenAll(addedTasks, TaskState.Completed, timeout);
}
catch (TimeoutException)
{
    batchClient.JobOperations.TerminateJob(jobId);
    Console.WriteLine(incompleteMessage);
    return false;
}
batchClient.JobOperations.TerminateJob(jobId);
 Console.WriteLine(completeMessage);
...

Limpieza de recursos

Después de ejecutar las tareas, la aplicación elimina automáticamente el contenedor de almacenamiento de entrada que creó y ofrece la opción de eliminar el grupo y el trabajo de Batch. Las clases JobOperations y Pool Operations tienen sus métodos de eliminación correspondientes, a los que se llama si el usuario confirma la eliminación. Aunque no se cobran los trabajos y las tareas, sí se cobran los nodos de proceso. Por consiguiente, se recomienda asignar solo los grupos necesarios. Al eliminar el grupo, las salidas de tarea de los nodos también se eliminan. Sin embargo, los archivos de salida permanecen en la cuenta de almacenamiento.

Cuando ya no los necesite, elimine el grupo de recursos, la cuenta de Batch y la de Storage. Para hacerlo desde Azure Portal, seleccione el grupo de recursos de la cuenta de Batch y haga clic en Eliminar grupo de recursos.

Pasos siguientes

En este tutorial, ha aprendido a:

  • Agregar un paquete de aplicación a la cuenta de Batch
  • Autenticarse en las cuentas de Batch y Storage
  • Cargar archivos de entrada en Storage
  • Crear un grupo de nodos de proceso para ejecutar una aplicación
  • Crear un trabajo y tareas para procesar archivos de entrada
  • Supervisar la ejecución de las tareas
  • Recuperación de archivos de salida

Para más ejemplos de uso de la API de .NET para programar y procesar cargas de trabajo de Batch, consulte los ejemplos de GitHub.