Tutorial: Ausführen einer parallelen Workload mit Azure Batch über die .NET-API

Mithilfe von Azure Batch können Sie umfangreiche auf Parallelverarbeitung ausgelegte HPC-Batchaufträge (High Performance Computing) effizient in Azure ausführen. In diesem Tutorial wird Schritt für Schritt ein C#-Beispiel für die Ausführung einer parallelen Workload per Batch beschrieben. Sie erfahren, wie Sie einen gängigen Batch-Anwendungsworkflow durchführen und programmgesteuert mit Batch- und Storage-Ressourcen interagieren.

  • Hinzufügen eines Anwendungspakets zu Ihrem Batch-Konto.
  • Authentifizieren mit Batch- und Storage-Konten.
  • Hochladen von Eingabedateien in Storage.
  • Erstellen eines Pools mit Computeknoten für die Ausführung einer Anwendung.
  • Erstellen eines Auftrags und von Aufgaben zum Verarbeiten von Eingabedateien.
  • Überwachen der Aufgabenausführung
  • Abrufen von Ausgabedateien.

In diesem Tutorial konvertieren Sie MP4-Mediendateien parallel in das MP3-Format, indem Sie das Open-Source-Tool ffmpeg verwenden.

Wenn Sie kein Azure-Abonnement haben, erstellen Sie ein kostenloses Azure-Konto, bevor Sie beginnen.

Voraussetzungen

  • Visual Studio 2017 oder höher bzw. .NET Core SDK für Linux, macOS oder Windows.

  • Ein Batch-Konto und ein verknüpftes Azure Storage-Konto. Informationen zur Erstellung dieser Konten finden Sie in den Batch-Schnellstartanleitungen zum Azure-Portal oder zur Azure CLI.

  • Laden Sie die geeignete Version von ffmpeg für Ihren Einsatzfall auf Ihren lokalen Computer herunter. In diesem Tutorial und der zugehörigen Beispiel-App wird die komplette Windows 64-Bit-Buildversion von ffmpeg 4.3.1 verwendet. Für dieses Tutorial benötigen Sie nur die ZIP-Datei. Es ist nicht erforderlich, die Datei zu entzippen oder sie lokal zu installieren.

Anmelden bei Azure

Melden Sie sich beim Azure-Portal an.

Hinzufügen eines Anwendungspakets

Verwenden Sie das Azure-Portal, um ffmpeg Ihrem Batch-Konto als Anwendungspaket hinzuzufügen. Mit Anwendungspaketen können Sie Aufgabenanwendungen und die dazugehörige Bereitstellung auf den Computeknoten in Ihrem Pool verwalten.

  1. Klicken Sie im Azure-Portal auf Weitere Dienste>Batch-Konten, und wählen Sie dann den Namen Ihres Batch-Kontos aus.

  2. Klicken Sie auf Anwendungen>Hinzufügen.

    Screenshot: Bereich „Anwendungen“ des Batch-Kontos.

  3. Geben Sie ffmpeg in das Feld Anwendungs-ID und die Paketversion 4.3.1 in das Feld Version ein. Wählen Sie die Datei „ffmpeg.zip“ aus, die Sie heruntergeladen haben, und klicken Sie auf Senden. Das ffmpeg-Anwendungspaket wird Ihrem Batch-Konto hinzugefügt.

    Screenshot: Die Felder „ID“ und „Version“ im Abschnitt „Anwendung hinzufügen“.

Abrufen der Kontoanmeldeinformationen

In diesem Beispiel müssen Sie Anmeldeinformationen für Ihr Batch- und Ihr Storage-Konto angeben. Die erforderlichen Anmeldeinformationen können Sie ganz einfach über das Azure-Portal abrufen. (Sie können aber auch die Azure-APIs oder Befehlszeilentools verwenden, um die Anmeldeinformationen abzurufen.)

  1. Wählen Sie Alle Dienste>Batch-Konten und anschließend den Namen Ihres Batch-Kontos aus.

  2. Wählen Sie zum Anzeigen der Batch-Anmeldeinformationen Schlüssel aus. Kopieren Sie die Werte für Batch-Konto, URL und Primärer Zugriffsschlüssel in einen Texteditor.

  3. Wählen Sie zum Anzeigen des Namens und der Schlüssel für das Storage-Konto auf Speicherkonto aus. Kopieren Sie die Werte für Speicherkontoname und Schlüssel1 in einen Texteditor.

Herunterladen und Ausführen der Beispiel-App

Herunterladen der Beispiel-App

Laden Sie die Beispiel-App von GitHub herunter, oder klonen Sie sie. Verwenden Sie den folgenden Befehl, um das Beispiel-App-Repository mit einem Git-Client zu klonen:

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

Navigieren Sie zu dem Verzeichnis, in dem die Visual Studio-Projektmappendatei BatchDotNetFfmpegTutorial.sln enthalten ist.

Stellen Sie außerdem sicher, dass der Verweis auf das ffmpeg-Anwendungspaket in der Lösung mit dem Bezeichner und der Version des ffmpeg-Pakets übereinstimmt, das Sie in Ihr Batch-Konto hochgeladen haben. Beispiel: ffmpeg und 4.3.1.

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

Erstellen und Ausführen des Beispielprojekts

Verwenden Sie für das Erstellen und Ausführen der Anwendung Visual Studio oder die Befehlszeile mit den Befehlen dotnet build und dotnet run. Sehen Sie sich nach dem Ausführen der Anwendung den Code an, um zu erfahren, welche Aufgabe die einzelnen Teile der Anwendung jeweils haben. In Visual Studio:

  1. Klicken Sie mit der rechten Maustaste in den Projektmappen-Explorer, und wählen Sie Projektmappe erstellen aus.

  2. Bestätigen Sie die Wiederherstellung von NuGet-Paketen, wenn Sie hierzu aufgefordert werden. Stellen Sie beim Herunterladen von fehlenden Paketen sicher, dass der NuGet-Paket-Manager installiert ist.

  3. Führen Sie die Lösung aus. Beim Ausführen der Beispielanwendung sieht die Konsolenausgabe in etwa wie folgt aus: Bei der Ausführung kommt es bei Monitoring all tasks for 'Completed' state, timeout in 00:30:00... zu einer Pause, während die Computeknoten des Pools gestartet werden.

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

Navigieren Sie im Azure-Portal zu Ihrem Batch-Konto, um den Pool, die Computeknoten, den Auftrag und die Aufgaben zu überwachen. Klicken Sie beispielsweise auf Pools>WinFFmpegPool, um ein Wärmebild mit den Computeknoten Ihres Pools anzuzeigen.

Bei der Ausführung von Aufgaben sieht das Wärmebild in etwa wie folgt aus:

Screenshot: Pool-Wärmebild im Azure-Portal.

Die normale Ausführungsdauer beträgt ca. zehn Minuten, wenn die Anwendung in der Standardkonfiguration ausgeführt wird. Die meiste Zeit wird für die Poolerstellung benötigt.

Abrufen von Ausgabedateien

Sie können das Azure-Portal verwenden, um die ausgegebenen MP3-Dateien herunterzuladen, die durch die ffmpeg-Aufgaben generiert werden.

  1. Klicken Sie auf Alle Dienste>Speicherkonten und anschließend auf den Namen Ihres Speicherkontos.
  2. Klicken Sie auf Blobs>Ausgabe.
  3. Klicken Sie mit der rechten Maustaste auf eine der ausgegebenen MP3-Dateien, und klicken Sie anschließend auf Herunterladen. Folgen Sie den Anweisungen in Ihrem Browser, um die Datei zu öffnen oder zu speichern.

Herunterladen der Ausgabedatei

Die Dateien können auch programmgesteuert aus den Computeknoten oder aus dem Speichercontainer heruntergeladen werden. Dies wird in diesem Beispiel allerdings nicht gezeigt.

Überprüfen des Codes

In den folgenden Abschnitten ist die Beispielanwendung in die Schritte unterteilt, die ausgeführt werden, um eine Workload im Batch-Dienst zu verarbeiten. Verwenden Sie beim Lesen des restlichen Teils dieses Artikels die Datei Program.cs der Projektmappe, da nicht jede Codezeile im Beispiel beschrieben wird.

Authentifizieren des Blobs und der Batch-Clients

Zum Interagieren mit dem verknüpften Speicherkonto verwendet die App die Bibliothek Azure.Storage.Blobs für .NET. Verwenden der BlobServiceClient-Klasse, die einen Verweis auf den Konto-URI und ein authentifizierendes Token wie DefaultAzureCredential verwendet.

// TODO: Replace <storage-account-name> with your actual storage account name
Uri accountUri = new Uri("https://<storage-account-name>.blob.core.windows.net/");
BlobServiceClient blobClient = new BlobServiceClient(accountUri, new DefaultAzureCredential());

Die App erstellt einen Verweis auf BatchAccountResource über den ArmClient des Ressourcen-Managers, um den Pool im Batchdienst zu erstellen. Der ARM-Client im Beispiel verwendet die Authentifizierung mit DefaultAzureCredential.

ArmClient _armClient = new ArmClient(new DefaultAzureCredential());
var batchAccountIdentifier = ResourceIdentifier.Parse(BatchAccountResourceID);
BatchAccountResource batchAccount = await _armClient.GetBatchAccountResource(batchAccountIdentifier).GetAsync();

Die App erstellt ein BatchClient-Objekt zum Erstellen von Aufträgen und Aufgaben im Batch-Dienst. Der Batch-Client im Beispiel verwendet die Authentifizierung mit DefaultAzureCredential.

// TODO: Replace <batch-account-name> with your actual storage account name
Uri batchUri = new Uri("https://<batch-account-name>t.eastus.batch.azure.com");
BatchClient _batchClient = new BatchClient(batchUri, new DefaultAzureCredential());

Hochladen von Eingabedateien

Die App übergibt das blobServerClient-Objekt an die CreateContainerIfNotExistc-Methode, um einen Speichercontainer für die Eingabedateien (MP4-Format) und einen Container für die Aufgabenausgabe zu erstellen.

CreateContainerIfNotExist(blobClient, inputContainerName);
CreateContainerIfNotExist(blobClient, outputContainerName);

Anschließend werden Dateien aus dem lokalen Ordner InputFiles in den Eingabecontainer hochgeladen. Die Dateien im Speicher werden als Batch-ResourceFile-Objekte definiert, die von Batch später auf Computeknoten heruntergeladen werden können.

Am Upload der Dateien sind in Program.cs zwei Methoden beteiligt:

  • UploadFilesToContainerAsync: Gibt eine Sammlung mit ResourceFile-Objekten zurück und ruft intern UploadResourceFileToContainerAsync auf, um die einzelnen Dateien hochzuladen, die im Parameter inputFilePaths übergeben werden.
  • UploadResourceFileToContainerAsync: Lädt jede Datei als Blob in den Eingabecontainer hoch. Nach dem Hochladen der Datei wird eine Shared Access Signature (SAS) für das Blob abgerufen, und es wird ein ResourceFile-Objekt zurückgegeben, um das Blob darzustellen.
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);

Weitere Informationen zum Hochladen von Dateien als Blobs in ein Speicherkonto mit .NET finden Sie unter Schnellstart: Hochladen, Herunterladen und Auflisten von Blobs mit .NET.

Erstellen eines Pools mit Computeknoten

Als Nächstes erstellt das Beispiel im Batch-Konto durch Aufrufen von CreatePoolIfNotExistAsync einen Pool mit Computeknoten. In dieser definierten Methode wird die BatchAccountResource.GetBatchAccountPools().CreateOrUpdateAsync-Methode verwendet, um die Anzahl von Knoten, die VM-Größe und eine Poolkonfiguration festzulegen. Hier gibt ein BatchVmConfiguration-Objekt einen BatchImageReference-Verweis auf ein Windows Server-Image an, das im Azure Marketplace veröffentlicht wurde. Batch unterstützt viele verschiedene VM-Images im Azure Marketplace und auch benutzerdefinierte VM-Images.

Die Anzahl von Knoten und die VM-Größe werden mit definierten Konstanten festgelegt. Batch unterstützt dedizierte Knoten und Spot-Knoten, und Sie können entweder einen oder beide in Ihren Pools verwenden. Dedizierte Knoten sind für Ihren Pool reserviert. Spot-Knoten werden zu einem reduzierten Preis aus überschüssiger VM-Kapazität in Azure angeboten. Spot-Knoten sind nicht mehr verfügbar, wenn Azure nicht über genügend Kapazität verfügt. Das Beispiel erstellt standardmäßig einen Pool mit nur 5 Spot-Knoten der Größe Standard_A1_v2.

Hinweis

Überprüfen Sie Ihre Knotenkontingente. Eine Anleitung zum Erstellen einer Kontingentanforderung finden Sie unter Batch-Dienst – Kontingente und Limits.

Die Anwendung ffmpeg wird auf den Computeknoten bereitgestellt, indem der Poolkonfiguration ein ApplicationPackageReference-Element hinzugefügt wird.

var credential = new DefaultAzureCredential();
ArmClient _armClient = new ArmClient(credential);

var batchAccountIdentifier = ResourceIdentifier.Parse(BatchAccountResourceID);
BatchAccountResource batchAccount = await _armClient.GetBatchAccountResource(batchAccountIdentifier).GetAsync();

BatchAccountPoolCollection collection = batchAccount.GetBatchAccountPools();
if (collection.Exists(poolId) == false)
{
    var poolName = poolId;
    var imageReference = new BatchImageReference()
    {
        Publisher = "MicrosoftWindowsServer",
        Offer = "WindowsServer",
        Sku = "2019-datacenter-smalldisk",
        Version = "latest"
    };
    string nodeAgentSku = "batch.node.windows amd64";


    ArmOperation<BatchAccountPoolResource> armOperation = await batchAccount.GetBatchAccountPools().CreateOrUpdateAsync(
        WaitUntil.Completed, poolName, new BatchAccountPoolData()
        {
            VmSize = "Standard_DS1_v2",
            DeploymentConfiguration = new BatchDeploymentConfiguration()
            {
                VmConfiguration = new BatchVmConfiguration(imageReference, nodeAgentSku)
            },
            ScaleSettings = new BatchAccountPoolScaleSettings()
            {
                FixedScale = new BatchAccountFixedScaleSettings()
                {
                    TargetDedicatedNodes = DedicatedNodeCount,
                    TargetLowPriorityNodes = LowPriorityNodeCount
                }
            },
            Identity = new ManagedServiceIdentity(ManagedServiceIdentityType.UserAssigned)
            {
                UserAssignedIdentities =
                {
                        [new ResourceIdentifier(ManagedIdentityId)] = new Azure.ResourceManager.Models.UserAssignedIdentity(),
                },
            },
            ApplicationPackages =
            {
                    new Azure.ResourceManager.Batch.Models.BatchApplicationPackageReference(new ResourceIdentifier(appPacakgeResourceID))
                    {
                        Version = appPackageVersion,
                    }
            },

        });
    BatchAccountPoolResource pool = armOperation.Value;

Erstellen eines Auftrags

Für einen Batch-Auftrag werden ein Pool zum Ausführen von Aufgaben und optionale Einstellungen wie eine Priorität und ein Zeitplan für die Arbeitsschritte angegeben. Im Beispiel wird ein Auftrag mit einem Aufruf von CreateJobAsync erstellt. In dieser definierten Methode wird die BatchClient.CreateJobAsync-Methode zum Erstellen eines Auftrags in Ihrem Pool verwendet.

 BatchJobCreateContent batchJobCreateContent = new BatchJobCreateContent(jobId, new BatchPoolInfo { PoolId = poolId });
 await batchClient.CreateJobAsync(batchJobCreateContent);

Aufgaben erstellen

Im Beispiel werden Aufgaben im Auftrag durch einen Aufruf der AddTasksAsync-Methode erstellt, mit der eine Liste mit BatchTask-Objekten erstellt wird. Für jede BatchTask wird ffmpeg ausgeführt, um ein ResourceFile-Eingabeobjekt per CommandLine-Eigenschaft zu verarbeiten. ffmpeg wurde zuvor bei der Erstellung des Pools auf jedem Knoten installiert. Hier wird in der Befehlszeile ffmpeg ausgeführt, um jede MP4-Eingabedatei (Video) in eine MP3-Datei (Audio) zu konvertieren.

Im Beispiel wird nach der Ausführung über die Befehlszeile ein OutputFile-Objekt für die MP3-Datei erstellt. Die Ausgabedateien (in diesem Fall eine Datei) jeder Aufgabe werden in einen Container im verknüpften Speicherkonto hochgeladen, indem die OutputFiles-Eigenschaft der Aufgabe verwendet wird. Beachten Sie die Bedingungen, die für das outputFile-Objekt festgelegt sind. Eine Ausgabedatei von einer Aufgabe wird nur in den Container hochgeladen, nachdem die Aufgabe erfolgreich abgeschlossen wurde (OutputFileUploadCondition.TaskSuccess). Weitere Implementierungsdetails finden Sie im vollständigen Codebeispiel auf GitHub.

Anschließend werden dem Auftrag im Beispiel mit der CreateTaskAsync-Methode Aufgaben hinzugefügt und für die Ausführung auf den Computeknoten in die Warteschlange eingereiht.

Ersetzen Sie den Dateipfad der ausführbaren Datei durch den Namen der von Ihnen heruntergeladenen Version. In diesem Beispielcode wird das Beispiel ffmpeg-4.3.1-2020-11-08-full_buildverwendet.

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

for (int i = 0; i < inputFiles.Count; i++)
{
    // Assign a task ID for each iteration
    string taskId = String.Format("Task{0}", i);

    // Define task command line to convert the video format from MP4 to MP3 using ffmpeg.
    // Note that ffmpeg syntax specifies the format as the file extension of the input file
    // and the output file respectively. In this case inputs are MP4.
    string appPath = String.Format("%AZ_BATCH_APP_PACKAGE_{0}#{1}%", appPackageId, appPackageVersion);
    string inputMediaFile = inputFiles[i].StorageContainerUrl;
    string outputMediaFile = String.Format("{0}{1}",
        System.IO.Path.GetFileNameWithoutExtension(inputMediaFile),
        ".mp3");
    string taskCommandLine = String.Format("cmd /c {0}\\ffmpeg-4.3.1-2020-11-08-full_build\\bin\\ffmpeg.exe -i {1} {2}", appPath, inputMediaFile, outputMediaFile);

    // Create a batch task (with the task ID and command line) and add it to the task list

    BatchTaskCreateContent batchTaskCreateContent = new BatchTaskCreateContent(taskId, taskCommandLine);
    batchTaskCreateContent.ResourceFiles.Add(inputFiles[i]);

    // Task output file will be uploaded to the output container in Storage.
    // TODO: Replace <storage-account-name> with your actual storage account name
    OutputFileBlobContainerDestination outputContainer = new OutputFileBlobContainerDestination("https://<storage-account-name>.blob.core.windows.net/output/" + outputMediaFile)
    {
        IdentityReference = inputFiles[i].IdentityReference,
    };

    OutputFile outputFile = new OutputFile(outputMediaFile,
                                           new OutputFileDestination() { Container = outputContainer },
                                           new OutputFileUploadConfig(OutputFileUploadCondition.TaskSuccess));
    batchTaskCreateContent.OutputFiles.Add(outputFile);

    tasks.Add(batchTaskCreateContent);
}

// Call BatchClient.CreateTaskCollectionAsync() to add the tasks as a collection rather than making a
// separate call for each. Bulk task submission helps to ensure efficient underlying API
// calls to the Batch service. 

await batchClient.CreateTaskCollectionAsync(jobId, new BatchTaskGroup(tasks));

Bereinigen von Ressourcen

Nach dem Ausführen der Aufgaben löscht die App den erstellten Eingabespeichercontainer automatisch und ermöglicht Ihnen das Löschen des Batch-Pools und -Auftrags. Der BatchClient verfügt über die DeleteJobAsync-Methode zum Löschen eines Auftrags und die DeletePoolAsync-Methode zum Löschen eines Pools, die aufgerufen werden, wenn Sie den Löschvorgang bestätigen. Für die Aufträge und Aufgaben fallen zwar keine Kosten an, für Computeknoten dagegen schon. Daher empfehlen wir Ihnen, Pools nur bei Bedarf zuzuordnen. Beim Löschen des Pools werden alle Aufgabenausgaben auf den Knoten gelöscht. Die Ausgabedateien verbleiben aber im Speicherkonto.

Löschen Sie die Ressourcengruppe, das Batch-Konto und das Speicherkonto, wenn diese Elemente nicht mehr benötigt werden. Wählen Sie hierzu im Azure-Portal die Ressourcengruppe für das Batch-Konto aus, und klicken Sie auf Ressourcengruppe löschen.

Nächste Schritte

In diesem Tutorial haben Sie Folgendes gelernt:

  • Hinzufügen eines Anwendungspakets zu Ihrem Batch-Konto.
  • Authentifizieren mit Batch- und Storage-Konten.
  • Hochladen von Eingabedateien in Storage.
  • Erstellen eines Pools mit Computeknoten für die Ausführung einer Anwendung.
  • Erstellen eines Auftrags und von Aufgaben zum Verarbeiten von Eingabedateien.
  • Überwachen der Aufgabenausführung
  • Abrufen von Ausgabedateien.

Weitere Beispiele zur Verwendung der .NET-API zum Planen und Verarbeiten von Batch-Workloads finden Sie in den Batch C#-Beispielen auf GitHub.