Esercizio: Creare un pool di nodi di calcolo per eseguire i processi

Completato

Per eseguire un processo batch, è necessario aggiungere un pool all'account Batch. Un pool contiene nodi di calcolo, vale a dire motori che eseguono il processo batch. Al momento della creazione, è necessario specificare il numero, le dimensioni e il sistema operativo dei nodi. In questo esercizio si modificherà l'app console compilata nell'esercizio precedente per aggiungere un pool all'account Batch.

L'azienda vuole tenere sotto controllo i costi dell'app e ha pertanto richiesto di usare un numero fisso di nodi.

Importante

Per eseguire questo esercizio è necessario disporre di una propria sottoscrizione di Azure e questo potrebbe comportare dei costi. Se non hai ancora una sottoscrizione di Azure, crea un account gratuito prima di iniziare.

Aggiungere le impostazioni per il nuovo pool

  1. In Cloud Shell modificare il file Program.cs nell'editor:

    code Program.cs
    
  2. Aggiungere le proprietà seguenti alla classe Program in 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";
    

    Le impostazioni precedenti vengono usate nel codice per creare il pool. È possibile esaminare ogni variabile nel modo seguente:

    • PoolId: Il nome che il codice usa per fare riferimento al pool in altre chiamate client al batch.
    • LowPriorityNodeCount: Il pool che si creerà avrà tre macchine virtuali con priorità bassa (VM).
    • PoolVMSize: Le macchine virtuali saranno di tipo STANDARD_A1_v2. I nodi avranno quindi 1 CPU, 2 GB di RAM e 10 GB di archiviazione SSD.
    • appPackageId: Il nome del pacchetto dell'applicazione da usare nei nodi creati.
    • appPackageVersion: La versione dell'applicazione da usare nei nodi creati.

Aggiornare il metodo Main() per supportare le chiamate asincrone

Le chiamate asincrone a servizi cloud saranno frequenti, quindi è prima di tutto necessario rendere Main asincrono. In C# .NET versione 7.1 e versioni successive i metodi Main asincroni sono supportati nelle applicazioni console.

  1. Modificare l'app console per consentire le chiamate al metodo asincrono aggiungendo prima di tutto la libreria System.Threading.Tasks.

    using System.Threading.Tasks;
    using System.Collections.Generic; // Also add generics to allow the app to use Lists
    
  2. A questo punto, aggiornare la firma del metodo Main nel modo seguente:

    static async Task Main(string[] args)
    

Creare un pool

  1. Aggiungere il nuovo metodo seguente alla classe Program per creare un pool di Batch. Il metodo si comporta come di seguito:

    • Creare un oggetto di riferimento all'immagine per archiviare le impostazioni dei nodi da aggiungere al pool.
    • Usa il riferimento all'immagine per creare un oggetto VirtualMachineConfiguration.
    • Crea un pool non associato usando le proprietà dichiarate in precedenza e VirtualMachineConfiguration.
    • Aggiunge al pool un riferimento al pacchetto dell'applicazione.
    • Crea il pool in Azure.
    • Accetta due parametri, batchClient e 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. Chiamare CreateBatchPoolAsync dal metodo Main. Il metodo Main deve avere la struttura seguente:

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

Testare l'app

  1. Nell'editor di codice fare clic con il pulsante destro del mouse e scegliere Salva, quindi fare clic con il pulsante destro del mouse e scegliere Esci.

  2. Compilare ed eseguire l'app con il comando seguente in Cloud Shell:

    dotnet run
    
  3. L'esecuzione dell'app richiederà alcuni minuti e si dovrebbe ottenere l'output seguente:

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

Tenere presente che ogni nodo è un macchina virtuale che esegue Windows Server 2012, con solo 1 CPU e 2 GB di RAM. Batch richiede tempo per trasferire le immagini delle macchine virtuali di Windows dal Marketplace delle macchine virtuali di Azure, creare l'infrastruttura e la rete delle macchini virtuali e infine avviare ogni nodo. Si tratta della parte più lunga nella maggior parte delle soluzioni di Batch. In genere un flusso di lavoro di Batch non esegue la pulizia del pool e dei relativi nodi.