Esercitazione: Eseguire un carico di lavoro parallelo con Azure Batch usando l'API .NET

Usare Azure Batch per eseguire in modo efficiente processi batch paralleli e HPC (High Performance Computing) su larga scala in Azure. Questa esercitazione illustra un esempio C# di esecuzione di un carico di lavoro parallelo usando Batch. Vengono fornite informazioni su un flusso di lavoro dell'applicazione Batch comune e su come interagire a livello di codice con le risorse di Archiviazione e Batch.

  • Aggiungere un pacchetto dell'applicazione all'account Batch.
  • Eseguire l'autenticazione con gli account Batch e Archiviazione.
  • Caricare i file di input in Archiviazione.
  • Creare un pool di nodi di calcolo per eseguire un'applicazione.
  • Creare un processo e attività per elaborare i file di input.
  • Monitorare l'esecuzione delle attività.
  • Recuperare i file di output.

In questa esercitazione si converteno file multimediali MP4 in formato MP3, in parallelo, usando lo strumento open source ffmpeg .

Se non si ha una sottoscrizione di Azure, creare un account Azure gratuito prima di iniziare.

Prerequisiti

Accedere ad Azure

Accedi al portale di Azure.

Aggiungere un pacchetto dell'applicazione

Usare il portale di Azure per aggiungere ffmpeg all'account Batch come pacchetto dell'applicazione. I pacchetti dell'applicazione aiutano a gestire le applicazioni di attività e la loro distribuzione nei nodi di calcolo del pool.

  1. Nella portale di Azure fare clic su Altri servizi>Account Batch e selezionare il nome dell'account Batch.

  2. Fare clic su Applicazioni>Aggiungi.

    Screenshot of the Applications section of the batch account.

  3. Immettere ffmpeg nel campo ID applicazione e una versione del pacchetto 4.3.1 nel campo Versione . Selezionare il file ZIP ffmpeg scaricato e quindi selezionare Invia. Il pacchetto dell'applicazione ffmpeg viene aggiunto all'account Batch.

    Screenshot of the ID and version fields in the Add application section.

Ottenere le credenziali dell'account

Per questo esempio, è necessario fornire le credenziali per gli account di archiviazione e Batch. Un modo semplice per ottenere le credenziali necessarie consiste nell'usare il portale di Azure. È anche possibile ottenere le credenziali usando le API di Azure o gli strumenti da riga di comando.

  1. Selezionare Tutti i servizi>Account Batch e quindi selezionare il nome dell'account Batch.

  2. Per visualizzare le credenziali di Batch, selezionare Chiavi. Copiare i valori di Account Batch, URL e Chiave di accesso primaria in un editor di testo.

  3. Per visualizzare il nome e le chiavi dell'account di archiviazione, selezionare Account di archiviazione. Copiare i valori dei campi Nome account di archiviazione e Key1 in un editor di testo.

Scaricare ed eseguire l'app di esempio

Scaricare l'app di esempio

Scaricare o clonare l'app di esempio da GitHub. Per clonare il repository dell'app di esempio con un client Git, usare il comando seguente:

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

Passare alla directory contenente il file della soluzione di Visual Studio BatchDotNetFfmpegTutorial.sln.

Aprire il file della soluzione in Visual Studio e aggiornare le stringhe delle credenziali in Program.cs con i valori ottenuti per gli account. Ad esempio:

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

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

Nota

Per semplificare l'esempio, le credenziali dell'account di archiviazione e Batch vengono visualizzate in testo non crittografato. In pratica, è consigliabile limitare l'accesso alle credenziali e fare riferimento a esse nel codice usando variabili di ambiente o un file di configurazione. Per alcuni esempi, vedere l'archivio degli esempi di codice di Azure Batch.

Assicurarsi inoltre che il riferimento al pacchetto dell'applicazione ffmpeg nella soluzione corrisponda all'identificatore e alla versione del pacchetto ffmpeg caricato nell'account Batch. Ad esempio, ffmpeg e 4.3.1.

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

Compilare ed eseguire il progetto di esempio

Compilare ed eseguire l'applicazione in Visual Studio oppure eseguire i comandi dotnet build e dotnet run dalla riga di comando. Dopo l'esecuzione dell'applicazione, esaminare il codice per comprendere le operazioni eseguite da ogni parte dell'applicazione. Ad esempio, in Visual Studio:

  1. Fare clic con il pulsante destro del mouse sulla soluzione in Esplora soluzioni e selezionare Compila soluzione.

  2. Confermare il ripristino di eventuali pacchetti NuGet, se richiesto. Se è necessario scaricare i pacchetti mancanti, verificare che lo strumento Gestione pacchetti NuGet sia installato.

  3. Eseguire la soluzione. Quando si esegue l'applicazione di esempio, l'output della console è simile al seguente. Durante l'esecuzione si verifica una pausa in corrispondenza di Monitoring all tasks for 'Completed' state, timeout in 00:30:00... mentre vengono avviati i nodi di calcolo del pool.

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

Passare all'account Batch nel portale di Azure per monitorare il pool, i nodi di calcolo, il processo e le attività. Per visualizzare, ad esempio, una mappa termica dei nodi di calcolo nel pool, fare clic su Pool>WinFFmpegPool.

Quando le attività sono in esecuzione, la mappa termica è simile all'esempio seguente:

Screenshot of the pool heat map in the Azure portal.

Quando si esegue l'applicazione con la configurazione predefinita, il tempo di esecuzione tipico è di circa 10 minuti. La maggior parte del tempo è necessaria per la creazione del pool.

Recuperare i file di output

È possibile usare il portale di Azure per scaricare i file MP3 di output generati dalle attività ffmpeg.

  1. Fare clic su Tutti i servizi>Account di archiviazione e quindi fare clic sul nome dell'account di archiviazione.
  2. Fare clic su BLOB>output.
  3. Fare clic con il pulsante destro del mouse su uno dei file MP3 di output e quindi scegliere Scarica. Seguire i prompt nel browser per aprire o salvare il file.

Download output file

Anche se non viene mostrato in questo esempio, è anche possibile scaricare i file a livello di codice dai nodi di calcolo o dal contenitore di archiviazione.

Esaminare il codice

Nelle sezioni seguenti si esamineranno in dettaglio i singoli passaggi eseguiti dall'applicazione di esempio per l'elaborazione di un carico di lavoro nel servizio Batch. Fare riferimento al file Program.cs nella soluzione mentre si legge il resto di questo articolo, poiché non viene discussa ogni riga di codice nell'esempio.

Autenticare i client BLOB e Batch

Per interagire con l'account di archiviazione collegato, l'app usa la libreria client di Archiviazione di Azure per .NET. Crea un riferimento all'account con CloudStorageAccount ed esegue l'autenticazione usando l'autenticazione con chiave condivisa. Crea quindi un oggetto 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();

L'app crea un oggetto BatchClient per creare e gestire pool, processi e attività nel servizio Batch. Il client Batch nell'esempio usa l'autenticazione con chiave condivisa. Batch supporta anche l'autenticazione tramite Microsoft Entra ID per autenticare singoli utenti o un'applicazione automatica.

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

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

Caricare i file di input

L'app passa l'oggetto blobClient al metodo CreateContainerIfNotExistAsync per creare un contenitore di archiviazione per i file di input (formato MP4) e un contenitore per l'output dell'attività.

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

I file vengono quindi caricati nel contenitore di input dalla cartella InputFiles locale. I file nel contenitore di archiviazione sono definiti come oggetti ResourceFile di Batch che successivamente Batch può scaricare nei nodi di calcolo.

Due metodi in Program.cs sono coinvolti nel caricamento dei file:

  • UploadFilesToContainerAsync: restituisce una raccolta di ResourceFile oggetti e chiamate UploadResourceFileToContainerAsync internamente per caricare ogni file passato nel inputFilePaths parametro .
  • UploadResourceFileToContainerAsync: carica ogni file come BLOB nel contenitore di input. Dopo aver caricato il file, ottiene una firma di accesso condiviso (SAS) per il BLOB e restituisce un ResourceFile oggetto per rappresentarlo.
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);

Per informazioni dettagliate sul caricamento di file come BLOB in un account di archiviazione con .NET, vedere Caricare, scaricare ed elencare BLOB con .NET.

Creare un pool di nodi di calcolo

L'esempio crea quindi un pool di nodi di calcolo nell'account Batch con una chiamata a CreatePoolIfNotExistAsync. Questo metodo definito usa il metodo BatchClient.PoolOperations.CreatePool per impostare il numero di nodi, le dimensioni delle VM e una configurazione del pool. In questo caso, un oggetto VirtualMachineConfiguration specifica un oggetto ImageReference che fa riferimento a un'immagine di Windows Server pubblicata in Azure Marketplace. Batch supporta una vasta gamma di immagini di VM in Azure Marketplace, oltre che immagini di VM personalizzate.

Il numero di nodi e le dimensioni delle VM vengono impostati usando costanti definite. Batch supporta nodi dedicati e nodi spot ed è possibile usare uno o entrambi i pool. I nodi dedicati sono riservati per il pool. I nodi spot vengono offerti a un prezzo ridotto rispetto alla capacità di macchine virtuali in eccedenza in Azure. I nodi spot diventano non disponibili se Azure non dispone di capacità sufficiente. Per impostazione predefinita, l'esempio crea un pool contenente solo 5 nodi spot di dimensioni Standard_A1_v2.

Nota

Assicurarsi di controllare le quote del nodo. Per istruzioni su come creare una richiesta di quota, vedere Quote e limiti del servizio Batch.

L'applicazione ffmpeg viene distribuita nei nodi di calcolo aggiungendo un oggetto ApplicationPackageReference alla configurazione del pool.

Il metodo CommitAsync invia il pool al servizio 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();  

Creare un processo

Un processo Batch specifica un pool in cui eseguire le attività e impostazioni facoltative, ad esempio una priorità e una pianificazione per il lavoro. L'esempio crea un processo con una chiamata a CreateJobAsync. Questo metodo definito usa il metodo BatchClient.JobOperations.CreateJob per creare un processo nel pool.

Il metodo CommitAsync invia il processo al servizio Batch. Inizialmente il processo è privo di attività.

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

await job.CommitAsync();

Creare attività

L'esempio crea le attività del processo con una chiamata al metodo AddTasksAsync, che crea un elenco di oggetti CloudTask. Ogni oggetto CloudTask esegue ffmpeg per elaborare un oggetto ResourceFile di input usando una proprietà CommandLine. Lo strumento ffmpeg è stato installato in precedenza in ogni nodo al momento della creazione del pool. In questo caso, la riga di comando esegue ffmpeg per convertire ogni file (video) MP4 di input in un file (audio) MP3.

L'esempio crea un oggetto OutputFile per il file MP3 dopo l'esecuzione della riga di comando. I file di output di ogni attività, in questo caso uno, vengono caricati in un contenitore nell'account di archiviazione collegato, usando la proprietà OutputFiles dell'attività. In precedenza nell'esempio di codice è stato ottenuto un URL di firma di accesso condiviso (outputContainerSasUrl) per fornire accesso in scrittura al contenitore di output. Notare le condizioni impostate sull'oggetto outputFile. Un file di output da un'attività viene caricato nel contenitore solo dopo il completamento corretto dell'attività (OutputFileUploadCondition.TaskSuccess). Vedere l'esempio di codice completo su GitHub per altri dettagli sull'implementazione.

L'esempio aggiunge quindi le attività al processo con il metodo AddTaskAsync, che le accoda per l'esecuzione nei nodi di calcolo.

Sostituire il percorso del file eseguibile con il nome della versione scaricata. Questo codice di esempio usa l'esempio ffmpeg-4.3.1-2020-11-08-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

Monitorare le attività

Quando Batch aggiunge attività a un processo, il servizio le accoda automaticamente e ne pianifica l'esecuzione nei nodi di calcolo nel pool associato. In base alle impostazioni specificate, Batch gestisce tutte le operazioni di accodamento, pianificazione, ripetizione di tentativi e tutte le altre operazioni di amministrazione relative alle attività.

Sono disponibili molti approcci al monitoraggio dell'esecuzione delle attività. Questo esempio definisce un metodo MonitorTasks che segnala solo gli stati di completamento ed esito positivo o negativo dell'attività. Il codice MonitorTasks specifica un oggetto ODATADetailLevel per selezionare in modo efficiente solo informazioni minime sulle attività. Crea quindi un oggetto TaskStateMonitor, che fornisce utilità helper per il monitoraggio degli stati delle attività. In MonitorTasks l'esempio attende che tutte le attività raggiungano uno stato TaskState.Completed entro un limite di tempo. Termina quindi il processo e segnala le attività che sono state completate ma per cui potrebbe essersi verificato un errore, ad esempio un codice di uscita diverso da zero.

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

Pulire le risorse

Al termine dell'esecuzione delle attività, l'app elimina automaticamente il contenitore di archiviazione di input creato e consente di scegliere se eliminare il processo e il pool di Batch. Le classi JobOperations e PoolOperations di BatchClient hanno entrambe metodi di eliminazione corrispondenti, che vengono chiamati se l'utente conferma l'eliminazione. Anche se non vengono addebitati costi per i processi e per le attività, vengono invece addebiti costi per i nodi di calcolo. È quindi consigliabile allocare solo i pool necessari. Quando si elimina il pool, tutto l'output delle attività nei nodi viene eliminato. I file di output rimangono tuttavia nell'account di archiviazione.

Quando non sono più necessari, eliminare il gruppo di risorse, l'account Batch e l'account di archiviazione. A tale scopo, nel portale di Azure selezionare il gruppo di risorse per l'account Batch e fare clic su Elimina gruppo di risorse.

Passaggi successivi

Questa esercitazione ha descritto come:

  • Aggiungere un pacchetto dell'applicazione all'account Batch.
  • Eseguire l'autenticazione con gli account Batch e Archiviazione.
  • Caricare i file di input in Archiviazione.
  • Creare un pool di nodi di calcolo per eseguire un'applicazione.
  • Creare un processo e attività per elaborare i file di input.
  • Monitorare l'esecuzione delle attività.
  • Recuperare i file di output.

Per altri esempi sull'uso dell'API .NET per pianificare ed elaborare carichi di lavoro batch, vedere gli esempi di Batch C# in GitHub.