チュートリアル:.NET API を使用して Azure Batch で並列ワークロードを実行する

Azure Batch を使用すると、大規模な並列コンピューティングやハイパフォーマンス コンピューティング (HPC) のバッチ ジョブを Azure で効率的に実行することができます。 このチュートリアルでは、Batch を使用して並列ワークロードを実行する C# の例を紹介します。 一般的な Batch アプリケーション ワークフローのほか、Batch および Storage のリソースをプログラムで操作する方法を学習します。

  • アプリケーション パッケージを Batch アカウントに追加します。
  • Batch アカウントおよびストレージ アカウントで認証します。
  • Storage に入力ファイルをアップロードします。
  • アプリケーションを実行するコンピューティング ノードのプールを作成します。
  • 入力ファイルを処理するジョブとタスクを作成します。
  • タスクの実行を監視する。
  • 出力ファイルを取得します。

このチュートリアルでは、ffmpeg オープンソース ツールを使用して複数の MP4 メディア ファイルを並行して MP3 形式に変換します。

Azure サブスクリプションをお持ちでない場合は、開始する前に Azure 無料アカウントを作成してください。

前提条件

  • Visual Studio 2017 以降、あるいは Linux、macOS、または Windows 用の .NET Core SDK

  • Batch アカウントおよびリンクされた Azure ストレージ アカウント。 これらのアカウントを作成するには、Azure Portal または Azure CLI 用の Batch クイック スタート ガイドを参照してください。

  • ユース ケースに応じた適切なバージョンの ffmpeg をローカル コンピューターにダウンロードします。 このチュートリアルと関連サンプル アプリでは、Windows 64 ビット フルビルド バージョンの ffmpeg 4.3.1 を使用します。 このチュートリアルで必要なのは、この ZIP ファイルのみです。 ファイルを解凍したり、ローカルにインストールしたりする必要はありません。

Azure へのサインイン

Azure ポータルにサインインします。

アプリケーション パッケージの追加

Azure Portal を使用して、ffmpeg をアプリケーション パッケージとして Batch アカウントに追加します。 アプリケーション パッケージは、タスク アプリケーションと、プール内のコンピューティング ノードへのそのアプリケーションのデプロイを管理する際に役立ちます。

  1. Azure portal で、[その他のサービス]>[Batch アカウント] の順にクリックし、Batch アカウントの名前を選択します。

  2. [アプリケーション]>[追加] の順にクリックします。

    Screenshot of the Applications section of the batch account.

  3. [アプリケーション ID] フィールドに「ffmpeg」と入力し、[バージョン] フィールドにパッケージ バージョン 4.3.1 を入力します。 ダウンロードした ffmpeg の ZIP ファイルを選択し、[送信] をクリックします。 ffmpeg アプリケーション パッケージが Batch アカウントに追加されます。

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

アカウントの資格情報を取得する

この例では、Batch アカウントと Storage アカウントの資格情報を指定する必要があります。 Azure Portal を使用すると、必要な資格情報を簡単に取得できます (Azure API やコマンドライン ツールを使用してこれらの資格情報を取得することもできます)。

  1. [すべてのサービス]>[Batch アカウント] の順に選択し、Batch アカウントの名前を選択します。

  2. Batch 資格情報を表示するには、 [キー] を選択します。 [Batch アカウント][URL][プライマリ アクセス キー] の値をテキスト エディターにコピーします。

  3. Storage アカウント名とキーを表示するには、 [ストレージ アカウント] を選択します。 [ストレージ アカウント名][Key1] の値をテキスト エディターにコピーします。

サンプル アプリケーションのダウンロードと実行

サンプル アプリ をダウンロードする

GitHub からサンプル アプリをダウンロードまたは複製します。 Git クライアントを使用してサンプル アプリ リポジトリを複製するには、次のコマンドを使用します。

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

Visual Studio ソリューション ファイル BatchDotNetFfmpegTutorial.sln が含まれているディレクトリに移動します。

Visual Studio でソリューション ファイルを開き、Program.cs 内の資格情報文字列を、お使いのアカウントに関して取得した値で更新します。 次に例を示します。

// 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==";

注意

この例では、単純化するために Batch アカウントと Storage アカウントの資格情報をクリア テキストで表示しています。 実際の資格情報は利用を制限し、コード中では環境変数か構成ファイルを使用して参照することをお勧めします。 その例については、Azure Batch のコード サンプル リポジトリを参照してください。

また、ソリューション内の ffmpeg アプリケーション パッケージの参照が、Batch アカウントにアップロードした ffmpeg パッケージの ID およびバージョンと一致することを確認します。 たとえば、ffmpeg4.3.1 です。

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

サンプル プロジェクトのビルドと実行

Visual Studio またはコマンド ライン (dotnet build コマンドと dotnet run コマンド) でアプリケーションをビルドして実行します。 アプリケーションの実行後に、コードを確認して、アプリケーションの各部分での処理内容を学習します。 たとえば Visual Studio で次の操作を実行します。

  1. ソリューション エクスプローラーを右クリックし、[ソリューションのビルド] を選択します。

  2. メッセージに従って NuGet パッケージの復元を確認します。 不足しているパッケージをダウンロードする必要がある場合は、NuGet Package Manager がインストールされていることを確認します。

  3. ソリューションを実行する サンプル アプリケーションを実行すると、コンソールの出力は次のようになります。 実行中、プールのコンピューティング ノードを開始する際に、Monitoring all tasks for 'Completed' state, timeout in 00:30:00... で一時停止が発生します。

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

プール、コンピューティング ノード、ジョブ、タスクを監視するには、Azure Portal で Batch アカウントに移動します。 たとえば、プール内のコンピューティング ノードのヒート マップを確認するには、 [プール]>WinFFmpegPool の順にクリックします。

タスクが実行されていると、ヒート マップは次のようになります。

Screenshot of the pool heat map in the Azure portal.

既定の構成でアプリケーションを実行する場合、通常の実行時間は約 10 分間です。 プールの作成に最も時間がかかります。

出力ファイルを取得する

Azure Portal を使用して、ffmpeg タスクによって生成された出力 MP3 ファイルをダウンロードすることができます。

  1. [すべてのサービス]>[ストレージ アカウント] の順にクリックし、ストレージ アカウントの名前をクリックします。
  2. [BLOB]>[出力] の順にクリックします。
  3. 出力 MP3 ファイルの 1 つを右クリックして、[ダウンロード] をクリックします。 ブラウザーのメッセージに従って、ファイルを開くか保存します。

Download output file

このサンプルには示されていませんが、コンピューティング ノードまたはストレージ コンテナーからプログラムでファイルをダウンロードすることもできます。

コードの確認

以降のセクションでは、サンプル アプリケーションを、Batch サービスでワークロードを処理するために実行する複数の手順に分けます。 サンプルのすべてのコード行について説明しているわけではないので、この記事の残りの部分を読む際は、ソリューション内のファイル Program.cs を参照してください。

BLOB クライアントと Batch クライアントの認証

リンクされているストレージ アカウントを操作するために、アプリでは .NET 用 Azure Storage クライアント ライブラリを使用します。 CloudStorageAccount を使用してアカウントへの参照を作成し、共有キー認証を使用して認証します。 その後、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();

このアプリは BatchClient オブジェクトを作成して、Batch サービスでプール、ジョブ、タスクを作成および管理します。 このサンプルの Batch クライアントでは共有キー認証を使用します。 Batch は、個々のユーザーまたは自動アプリケーションを認証するために、Microsoft Entra ID による認証もサポートしています。

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

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

入力ファイルのアップロード

アプリは、blobClient オブジェクトを CreateContainerIfNotExistAsync メソッドに渡して、入力ファイル (MP4 形式) 用のストレージ コンテナーとタスク出力用のコンテナーを作成します。

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

その後、ファイルは、ローカルの InputFiles フォルダーから入力用コンテナーにアップロードされます。 ストレージ内のファイルは、Batch の ResourceFile オブジェクトとして定義されており、Batch が後でコンピューティング ノードにダウンロードできます。

Program.cs 内の 2 つのメソッドは、ファイルのアップロードに関連しています。

  • UploadFilesToContainerAsync: ResourceFile オブジェクトのコレクションを返し、内部的に UploadResourceFileToContainerAsync を呼び出して、inputFilePaths パラメーターで渡される各ファイルをアップロードします。
  • UploadResourceFileToContainerAsync:各ファイルを BLOB として入力用コンテナーにアップロードします。 ファイルのアップロード後、BLOB の Shared Access Signature (SAS) を取得し、それを表す ResourceFile オブジェクトを返します。
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);

.NET を使用してファイルを BLOB としてストレージ アカウントにアップロードする方法の詳細については、「.NET を使用して BLOB をアップロード、ダウンロード、および一覧表示する」を参照してください。

コンピューティング ノードのプールの作成

次に、CreatePoolIfNotExistAsync が呼び出されて、コンピューティング ノードのプールが Batch アカウントに作成されます。 この定義済みのメソッドは、BatchClient.PoolOperations.CreatePool メソッドを使用してノードの数、VM のサイズ、プールの構成を設定します。 ここで、VirtualMachineConfiguration オブジェクトでは、ImageReference に、Azure Marketplace で公開されている Windows Server イメージを指定します。 Batch は、Azure Marketplace のさまざまな VM イメージだけでなく、カスタム VM イメージもサポートしています。

ノードの数と VM のサイズは、定義済みの定数を使用して設定されます。 Batch では専用ノードとスポット ノードがサポートされているため、ご利用のプールではそのいずれかまたは両方を使用できます。 専用ノードは、プール用に予約されています。 スポット ノードは、Azure の VM の余剰容量から割引価格で提供されます。 スポット ノードは、Azure に十分な容量がない場合に使用できなくなります。 このサンプルは、既定で、サイズ Standard_A1_v2 のスポット ノードが 5 つだけ含まれているプールを作成します。

Note

ノードのクォータを必ず確認してください。 クォータ要求の作成手順については、Batch サービスのクォータと制限に関するページを参照してください。

ffmpeg アプリケーションは、プールの構成に ApplicationPackageReference を追加することで、コンピューティング ノードにデプロイされます。

CommitAsync メソッドは、プールを 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();  

ジョブの作成

Batch ジョブでは、タスクの実行対象となるプールと、作業の優先順位やスケジュールなどのオプションの設定を指定します。 このサンプルでは、CreateJobAsync の呼び出しを使用してジョブを作成します。 この定義済みのメソッドは、BatchClient.JobOperations.CreateJob メソッドを使用してプールにジョブを作成します。

CommitAsync メソッドは、ジョブを Batch サービスに送信します。 最初、ジョブにはタスクがありません。

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

await job.CommitAsync();

タスクの作成

このサンプルでは、AddTasksAsync メソッドの呼び出しを使用してジョブにタスクを作成します。これにより、CloudTask オブジェクトの一覧が作成されます。 各 CloudTask は、CommandLine プロパティを使用することで、ffmpeg を実行して入力の ResourceFile オブジェクトを処理します。 ffmpeg は、以前にプールが作成されたときに各ノードにインストールされています。 ここでは、コマンド ラインで ffmpeg を実行して、各入力 MP4 (ビデオ) ファイルを MP3 (オーディオ) ファイルに変換します。

このサンプルでは、コマンド ラインの実行後に MP3 ファイルの OutputFile オブジェクトを作成します。 各タスクの出力ファイル (この場合は 1 つ) は、タスクの OutputFiles プロパティを使用して、リンクされているストレージ アカウントのコンテナーにアップロードされます。 以前、コード サンプルでは、出力コンテナーへの書き込みアクセス権を得るために Shared Access Signature URL (outputContainerSasUrl) が取得されました。 outputFile オブジェクトに設定されている条件に注意してください。 タスクが正常に完了すると、タスクからの出力ファイルがコンテナーに単純にアップロードされます (OutputFileUploadCondition.TaskSuccess)。 実装の詳細については、GitHub のコード サンプルを参照してください。

その後、このサンプルは、AddTaskAsync メソッドを使用してジョブにタスクを追加します。これにより、タスクは、コンピューティング ノードで実行するためにキューに登録されます。

実行可能ファイルのファイル パスを、ダウンロードしたバージョンの名前に置き換えます。 このサンプル コードでは、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

タスクの監視

Batch によってタスクがジョブに追加されると、関連付けられたプール内のコンピューティング ノードで実行するために自動的にキューに登録され、スケジュールが設定されます。 指定した設定に基づいて、Batch は、タスクのキューへの登録、スケジュール設定、再試行など、タスク管理作業すべてを処理します。

タスクの実行を監視する方法は多数ありますが、 このサンプルでは、完了と、タスクの失敗または成功の状態のみをレポートするよう MonitorTasks メソッドを定義します。 MonitorTasks コードでは、ODATADetailLevel を指定して、タスクについて最小限の情報のみを効率的に選択します。 その後、TaskStateMonitor を作成します。これは、タスクの状態を監視するためのヘルパー ユーティリティを提供します。 MonitorTasks で、サンプルはすべてのタスクが制限時間内に TaskState.Completed に達するまで待ちます。 その後、ジョブを終了し、完了してもゼロ以外の終了コードなどのエラーが発生している可能性があるタスクに関するレポートを作成します。

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

リソースをクリーンアップする

タスクの実行後、自動的に、作成された入力用ストレージ コンテナーが削除され、Batch プールとジョブを削除するためのオプションが表示されます。 BatchClient の JobOperations クラスと PoolOperations クラスの両方に対応する削除メソッドがあります。このメソッドは、削除を確定すると呼び出されます。 ジョブとタスク自体は課金対象ではありませんが、コンピューティング ノードは課金対象です。 そのため、必要な場合にのみプールを割り当てることをお勧めします。 プールを削除すると、ノード上のタスク出力はすべて削除されます。 ただし、出力ファイルはストレージ アカウントに残ります。

リソース グループ、Batch アカウント、ストレージ アカウントは、不要になったら削除します。 Azure Portal でこれを行うには、Batch アカウントのリソース グループを選択し、 [リソース グループの削除] をクリックしてください。

次のステップ

このチュートリアルでは、以下の内容を学習しました。

  • アプリケーション パッケージを Batch アカウントに追加します。
  • Batch アカウントおよびストレージ アカウントで認証します。
  • Storage に入力ファイルをアップロードします。
  • アプリケーションを実行するコンピューティング ノードのプールを作成します。
  • 入力ファイルを処理するジョブとタスクを作成します。
  • タスクの実行を監視する。
  • 出力ファイルを取得します。

.NET API を使用して Batch ワークロードのスケジュール設定と処理を行う他の例については、GitHub の Batch C# サンプルを参照してください。