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 Sie einen Auftrag und Aufgaben zur Verarbeitung von Eingabedateien.
  • Überwachen der Aufgabenausführung
  • Ausgabedateien abrufen.

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

Wenn Sie nicht über ein Azure-Konto verfügen, erstellen Sie ein kostenloses 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. Anwendungspakete helfen Ihnen dabei, Taskanwendungen und deren Bereitstellung auf den Rechenknoten in Ihrem Pool zu 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 des Bereichs „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“.

Kontoanmeldeinformationen abrufen

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. Um den Namen und die Schlüssel des Speicherkontos anzuzeigen, wählen Sie 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 sich die Visual Studio-Projektmappendatei BatchDotNetFfmpegTutorial.sln befindet.

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. Zum Beispiel in Visual Studio:

  1. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf die Projektmappe, 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

Wechseln Sie im Azure-Portal zu Ihrem Batch-Konto, um den Pool, die Rechenknoten, 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.

Ausgabedateien abrufen

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. Ziehen Sie beim Lesen des restlichen Teils dieses Artikels die Datei Program.cs in der Projektmappe heran, da nicht jede Codezeile im Beispiel erläutert wird.

Blob- und Batch-Clients authentifizieren

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 den BatchAccountResource über den Resource Manager ArmClient, 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 CreateContainerIfNotExist-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);

Ausführliche Informationen zum Hochladen von Dateien als Blobs in einem Speicherkonto mit .NET finden Sie unter 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 für Ihre Pools jeweils einen oder beide Typen verwenden. Dedizierte Knoten sind für Ihren Pool reserviert. Spot-Knoten werden zu einem reduzierten Preis aus überschüssigen VM-Kapazitäten 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

Achten Sie darauf, Ihre Knotenquoten zu überprüfen. Eine Anleitung zum Erstellen einer Kontingentanforderung finden Sie unter Batch-Dienst – Kontingente und Limits.

Die ffmpeg-Anwendung wird auf den Rechenknoten bereitgestellt, indem der Poolkonfiguration ein BatchApplicationPackageReference 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(appPackageResourceID))
                    {
                        Version = appPackageVersion,
                    }
            },

        });
    BatchAccountPoolResource pool = armOperation.Value;

Auftrag erstellen

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. Das Beispiel erstellt einen Job durch einen Aufruf von CreateJobAsync. In dieser definierten Methode wird die BatchClient.CreateJobAsync-Methode zum Erstellen eines Auftrags in Ihrem Pool verwendet.

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

Aufgaben erstellen

Im Beispiel werden Aufgaben im Auftrag durch einen Aufruf der AddTasksAsync-Methode erstellt, mit der eine Liste mit BatchTask-Objekten erstellt wird. Jede BatchTask führt ffmpeg aus, um ein ResourceFile-Eingabeobjekt mithilfe der Eigenschaft CommandLine 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 fügt das Beispiel dem Auftrag mit der CreateTaskAsync -Methode Aufgaben hinzu, die zur Ausführung auf den Computeknoten in die Warteschlange gestellt werden.

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<BatchTaskCreateOptions> tasks = new List<BatchTaskCreateOptions>();

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].StorageContainerUri.ToString();
    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

    BatchTaskCreateOptions batchTaskCreateOptions = new BatchTaskCreateOptions(taskId, taskCommandLine);
    batchTaskCreateOptions.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(new Uri("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));
    batchTaskCreateOptions.OutputFiles.Add(outputFile);

    tasks.Add(batchTaskCreateOptions);
}

// 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 Rechenknoten werden jedoch Kosten berechnet. Daher empfehlen wir Ihnen, Pools nur bei Bedarf zuzuordnen. Wenn Sie den Pool löschen, wird die gesamte Aufgabenausgabe 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 Sie einen Auftrag und Aufgaben zur Verarbeitung von Eingabedateien.
  • Überwachen der Aufgabenausführung
  • Ausgabedateien abrufen.

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