Ejercicio: Creación de un grupo de nodos de proceso para ejecutar los trabajos

Completado

Para ejecutar un trabajo por lotes, es necesario agregar un grupo a la cuenta de Batch. Un grupo contiene nodos de proceso, que son los motores que ejecutan el trabajo de Batch. El número, el tamaño y el sistema operativo de los nodos se especifican en tiempo de creación. En este ejercicio, modificará la aplicación de consola que ha creado en el anterior para agregar un grupo a la cuenta de Batch.

Su empresa quiere controlar los costes de la aplicación y le pidió que use un número fijo de nodos.

Importante

Para realizar este ejercicio, se necesita una suscripción de Azure propia y puede que se apliquen cargos. Si aún no tiene una suscripción de Azure, cree una cuenta gratuita antes de comenzar.

Adición de configuración para el nuevo grupo

  1. En Cloud Shell, modifique el archivo Program.cs en el editor:

    code Program.cs
    
  2. Agregue las propiedades siguientes a la clase Program de Program.cs:

    private const string PoolId = "WinFFmpegPool";
    private const int DedicatedNodeCount = 0;
    private const int LowPriorityNodeCount = 3;
    private const string PoolVMSize = "STANDARD_D2_v2";
    private const string appPackageId = "ffmpeg";
    private const string appPackageVersion = "3.4";
    

    La configuración anterior se usa en el código para crear el grupo. Al examinar cada variable se pueden explicar como se muestra a continuación:

    • PoolId: Nombre que usará el código para hacer referencia al grupo en otras llamadas de cliente de Batch.
    • LowPriorityNodeCount: Creará un grupo con tres máquinas virtuales (VM) de prioridad baja.
    • PoolVMSize: Las máquinas virtuales serán STANDARD_A1_v2, lo que proporciona a los nodos 1 CPU, 2 GB de RAM y 10 GB de almacenamiento SSD.
    • appPackageId: Nombre del paquete de aplicación que se usará en los nodos que se creen.
    • appPackageVersion: Versión de la aplicación que se usará en los nodos que se creen.

Actualización del método Main() para admitir llamadas asincrónicas

Se realizarán varias llamadas asincrónicas a servicios en la nube, por lo que el primer paso consiste en convertir Main en asincrónico. Con la versión 7.1 de .NET para C# y posteriores, se admiten métodos Main asincrónicos en las aplicaciones de consola.

  1. Para cambiar la aplicación de consola para permitir llamadas de método asincrónicas, agregue primero la biblioteca System.Threading.Tasks.

    using System.Threading.Tasks;
    using System.Collections.Generic; // Also add generics to allow the app to use Lists
    
  2. A continuación, actualice la firma del método Main como sigue:

    static async Task Main(string[] args)
    

Creación de un grupo

  1. Agregue el siguiente método nuevo a la clase Program para crear un grupo de Batch. El método realiza las acciones siguientes:

    • Crea un objeto de referencia de imagen para almacenar la configuración de los nodos que se vayan a agregar al grupo.
    • Usa la referencia de imagen para crear un objeto VirtualMachineConfiguration.
    • Crea un grupo sin enlazar con las propiedades declaradas anteriormente y VirtualMachineConfiguration.
    • Agrega una referencia de paquete de aplicación al grupo.
    • Crea el grupo en Azure.
    • Requiere dos parámetros, batchClient y PoolId.
      private static async Task CreateBatchPoolAsync(BatchClient batchClient, string poolId)
        {
            CloudPool pool = null;
            Console.WriteLine("Creating pool [{0}]...", poolId);
    
            // Create an image reference object to store the settings for the nodes to be added to the pool
            ImageReference imageReference = new ImageReference(
                    publisher: "MicrosoftWindowsServer",
                    offer: "WindowsServer",
                    sku: "2012-R2-Datacenter-smalldisk",
                    version: "latest");
    
            // Use the image reference to create a VirtualMachineConfiguration object
            VirtualMachineConfiguration virtualMachineConfiguration =
            new VirtualMachineConfiguration(
                imageReference: imageReference,
                nodeAgentSkuId: "batch.node.windows amd64");
    
            try
            {
                // Create an unbound pool. No pool is actually created in the Batch service until we call
                // CloudPool.CommitAsync(). This CloudPool instance is therefore considered "unbound," and we can
                // modify its properties.
                pool = batchClient.PoolOperations.CreatePool(
                    poolId: poolId,
                    targetDedicatedComputeNodes: DedicatedNodeCount,
                    targetLowPriorityComputeNodes: LowPriorityNodeCount,
                    virtualMachineSize: PoolVMSize,
                    virtualMachineConfiguration: virtualMachineConfiguration);  
    
                // Specify the application and version to install on the compute nodes
                pool.ApplicationPackageReferences = new List<ApplicationPackageReference>
                {
                    new ApplicationPackageReference
                    {
                    ApplicationId = appPackageId,
                    Version = appPackageVersion
                    }
                };
    
                // Create the pool
                await pool.CommitAsync();
            }
            catch (BatchException be)
            {
                // Accept the specific error code PoolExists as that is expected if the pool already exists
                if (be.RequestInformation?.BatchError?.Code == BatchErrorCodeStrings.PoolExists)
                {
                    Console.WriteLine("The pool [{0}] already existed when we tried to create it", poolId);
                }
                else
                {
                    throw; // Any other exception is unexpected
                }
            }
        }  
    
  2. Llame a CreateBatchPoolAsync desde el método Main. Ahora el método Main debe ser el siguiente:

    static async Task Main(string[] args)
    {
        // Read the environment variables to allow the app to connect to the Azure Batch account
        batchAccountUrl = Environment.GetEnvironmentVariable(envVarBatchURI);
        batchAccountName = Environment.GetEnvironmentVariable(envVarBatchName);
        batchAccountKey = Environment.GetEnvironmentVariable(envVarKey);
    
        // Show the user the batch the app is attaching to
        Console.WriteLine("URL: {0}, Name: {1}, Key: {2}", batchAccountUrl, batchAccountName, batchAccountKey);
    
        // The batch client requires a BatchSharedKeyCredentials object to open a connection
        var sharedKeyCredentials = new BatchSharedKeyCredentials(batchAccountUrl, batchAccountName, batchAccountKey);
        var batchClient = BatchClient.Open(sharedKeyCredentials);
    
        // Create the Batch pool, which contains the compute nodes that execute tasks.
        await CreateBatchPoolAsync(batchClient, PoolId);
    }
    

Prueba de la aplicación

  1. En el editor de código, haga clic con el botón derecho y seleccione Guardar y, a continuación, haga clic con el botón derecho y seleccione Salir.

  2. En Cloud Shell, compile y ejecute la aplicación con el comando siguiente:

    dotnet run
    
  3. La aplicación tardará unos minutos en ejecutarse y debería obtener la siguiente salida:

    URL: <your batch account url, Name: <your batch name>, Key: <your batch key>
    Creating pool [WinFFmpegPool]...
    

Recuerde que cada nodo es una máquina virtual que ejecuta un servidor Windows 2012, con solo una CPU y 2 GB de RAM. Batch tarda tiempo en transferir esas imágenes de máquina virtual Windows desde el Marketplace de máquinas virtuales de Azure, crear la infraestructura de máquinas virtuales y redes, y por último, iniciar cada nodo. Esta es la parte más lenta de la mayoría de las soluciones de Batch. Un flujo de trabajo típico de Batch no limpia el grupo y sus nodos.