チュートリアル:.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 アカウントに追加します。 アプリケーション パッケージは、タスク アプリケーションと、プール内のコンピューティング ノードへのそのアプリケーションのデプロイを管理する際に役立ちます。
Azure portal で、[その他のサービス]>[Batch アカウント] の順にクリックし、Batch アカウントの名前を選択します。
[アプリケーション]>[追加] の順にクリックします。
[アプリケーション ID] フィールドに「ffmpeg」と入力し、[バージョン] フィールドにパッケージ バージョン 4.3.1 を入力します。 ダウンロードした ffmpeg の ZIP ファイルを選択し、[送信] をクリックします。 ffmpeg アプリケーション パッケージが Batch アカウントに追加されます。
アカウントの資格情報を取得する
この例では、Batch アカウントと Storage アカウントの資格情報を指定する必要があります。 Azure Portal を使用すると、必要な資格情報を簡単に取得できます (Azure API やコマンドライン ツールを使用してこれらの資格情報を取得することもできます)。
[すべてのサービス]>[Batch アカウント] の順に選択し、Batch アカウントの名前を選択します。
Batch 資格情報を表示するには、 [キー] を選択します。 [Batch アカウント] 、 [URL] 、 [プライマリ アクセス キー] の値をテキスト エディターにコピーします。
Storage アカウント名とキーを表示するには、 [ストレージ アカウント] を選択します。 [ストレージ アカウント名] と [Key1] の値をテキスト エディターにコピーします。
サンプル アプリケーションのダウンロードと実行
サンプル アプリ をダウンロードする
GitHub からサンプル アプリをダウンロードまたは複製します。 Git クライアントを使用してサンプル アプリ リポジトリを複製するには、次のコマンドを使用します。
git clone https://github.com/Azure-Samples/batch-dotnet-ffmpeg-tutorial.git
Visual Studio ソリューション ファイル BatchDotNetFfmpegTutorial.sln が含まれているディレクトリに移動します。
また、ソリューション内の ffmpeg アプリケーション パッケージの参照が、Batch アカウントにアップロードした ffmpeg パッケージの ID およびバージョンと一致することを確認します。 たとえば、ffmpeg
と4.3.1
です。
const string appPackageId = "ffmpeg";
const string appPackageVersion = "4.3.1";
サンプル プロジェクトのビルドと実行
Visual Studio またはコマンド ライン (dotnet build
コマンドと dotnet run
コマンド) でアプリケーションをビルドして実行します。 アプリケーションの実行後に、コードを確認して、アプリケーションの各部分での処理内容を学習します。 たとえば Visual Studio で次の操作を実行します。
ソリューション エクスプローラーを右クリックし、[ソリューションのビルド] を選択します。
メッセージに従って NuGet パッケージの復元を確認します。 不足しているパッケージをダウンロードする必要がある場合は、NuGet Package Manager がインストールされていることを確認します。
ソリューションを実行する サンプル アプリケーションを実行すると、コンソールの出力は次のようになります。 実行中、プールのコンピューティング ノードを開始する際に、
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 の順にクリックします。
タスクが実行されていると、ヒート マップは次のようになります。
既定の構成でアプリケーションを実行する場合、通常の実行時間は約 10 分間です。 プールの作成に最も時間がかかります。
出力ファイルを取得する
Azure Portal を使用して、ffmpeg タスクによって生成された出力 MP3 ファイルをダウンロードすることができます。
- [すべてのサービス]>[ストレージ アカウント] の順にクリックし、ストレージ アカウントの名前をクリックします。
- [BLOB]>[出力] の順にクリックします。
- 出力 MP3 ファイルの 1 つを右クリックして、[ダウンロード] をクリックします。 ブラウザーのメッセージに従って、ファイルを開くか保存します。
このサンプルには示されていませんが、コンピューティング ノードまたはストレージ コンテナーからプログラムでファイルをダウンロードすることもできます。
コードの確認
以降のセクションでは、サンプル アプリケーションを、Batch サービスでワークロードを処理するために実行する複数の手順に分けます。 サンプルのすべてのコード行について説明しているわけではないので、この記事の残りの部分を読む際は、ソリューション内のファイル Program.cs を参照してください。
BLOB クライアントと Batch クライアントの認証
リンクされているストレージ アカウントとやり取りするために、アプリは .NET 用 Azure.Storage.Blobs ライブラリを使用します。 アカウント URI への参照を受け取る BlobServiceClient を使用し、DefaultAzureCredential などのトークンを認証します。
// 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());
このアプリは、リソース マネージャーの ArmClient を介して BatchAccountResource への参照を作成し、Batch サービス内にプールを作成します。 サンプルの Arm クライアントは、DefaultAzureCredential 認証を使用しています。
ArmClient _armClient = new ArmClient(new DefaultAzureCredential());
var batchAccountIdentifier = ResourceIdentifier.Parse(BatchAccountResourceID);
BatchAccountResource batchAccount = await _armClient.GetBatchAccountResource(batchAccountIdentifier).GetAsync();
このアプリは BatchClient オブジェクトを作成して、Batch サービス内のジョブとタスクを作成します。 サンプルの Batch クライアントは、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());
入力ファイルのアップロード
アプリは、blobServerClient
オブジェクトを CreateContainerIfNotExistc
メソッドに渡して、入力ファイル (MP4 形式) 用のストレージ コンテナーとタスク出力用のコンテナーを作成します。
CreateContainerIfNotExist(blobClient, inputContainerName);
CreateContainerIfNotExist(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 アカウントに作成されます。 この定義済みメソッドは、BatchAccountResource.GetBatchAccountPools().CreateOrUpdateAsync メソッドを使用して、ノードの数、VM サイズ、プール構成を設定します。 ここで、BatchVmConfiguration オブジェクトは、Azure Marketplace で公開されている Windows Server イメージに対する BatchImageReference を指定します。 Batch は、Azure Marketplace のさまざまな VM イメージだけでなく、カスタム VM イメージもサポートしています。
ノードの数と VM のサイズは、定義済みの定数を使用して設定されます。 Batch では専用ノードとスポット ノードがサポートされているため、ご利用のプールではそのいずれかまたは両方を使用できます。 専用ノードは、プール用に予約されています。 スポット ノードは、Azure の VM の余剰容量から割引価格で提供されます。 スポット ノードは、Azure に十分な容量がない場合に使用できなくなります。 このサンプルは、既定で、サイズ Standard_A1_v2 のスポット ノードが 5 つだけ含まれているプールを作成します。
Note
ノードのクォータを必ず確認してください。 クォータ要求の作成手順については、Batch サービスのクォータと制限に関するページを参照してください。
ffmpeg アプリケーションは、プールの構成に ApplicationPackageReference を追加することで、コンピューティング ノードにデプロイされます。
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;
ジョブの作成
Batch ジョブでは、タスクの実行対象となるプールと、作業の優先順位やスケジュールなどのオプションの設定を指定します。 このサンプルでは、CreateJobAsync
の呼び出しを使用してジョブを作成します。 この定義済みのメソッドは、BatchClient.CreateJobAsync メソッドを使用してプールにジョブを作成します。
BatchJobCreateContent batchJobCreateContent = new BatchJobCreateContent(jobId, new BatchPoolInfo { PoolId = poolId });
await batchClient.CreateJobAsync(batchJobCreateContent);
タスクの作成
このサンプルは、AddTasksAsync
メソッドの呼び出しを使用してジョブ内のタスクを作成し、これによって BatchTask オブジェクトのリストが作成されます。 各 BatchTask
は、CommandLine プロパティを使用することで、ffmpeg を実行して入力の ResourceFile
オブジェクトを処理します。 ffmpeg は、以前にプールが作成されたときに各ノードにインストールされています。 ここでは、コマンド ラインで ffmpeg を実行して、各入力 MP4 (ビデオ) ファイルを MP3 (オーディオ) ファイルに変換します。
このサンプルでは、コマンド ラインの実行後に MP3 ファイルの OutputFile オブジェクトを作成します。 各タスクの出力ファイル (この場合は 1 つ) は、タスクの OutputFiles プロパティを使用して、リンクされているストレージ アカウントのコンテナーにアップロードされます。 outputFile
オブジェクトに設定されている条件に注意してください。 タスクが正常に完了すると、タスクからの出力ファイルがコンテナーに単純にアップロードされます (OutputFileUploadCondition.TaskSuccess
)。 実装の詳細については、GitHub のコード サンプルを参照してください。
その後、このサンプルは CreateTaskAsync メソッドを使用してジョブにタスクを追加し、これによってタスクは、コンピューティング ノードで実行するためにキュー登録されます。
実行可能ファイルのファイル パスを、ダウンロードしたバージョンの名前に置き換えます。 このサンプル コードでは、ffmpeg-4.3.1-2020-11-08-full_build
の例を使用します。
// 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));
リソースをクリーンアップする
タスクの実行後、自動的に、作成された入力用ストレージ コンテナーが削除され、Batch プールとジョブを削除するためのオプションが表示されます。 BatchClient はジョブを削除するためのメソッド DeleteJobAsync と、プールを削除するためのメソッド DeletePoolAsync を持ち、これらはユーザーが削除を確認すると呼び出されます。 ジョブとタスク自体は課金対象ではありませんが、コンピューティング ノードは課金対象です。 そのため、必要な場合にのみプールを割り当てることをお勧めします。 プールを削除すると、ノード上のタスク出力はすべて削除されます。 ただし、出力ファイルはストレージ アカウントに残ります。
リソース グループ、Batch アカウント、ストレージ アカウントは、不要になったら削除します。 Azure Portal でこれを行うには、Batch アカウントのリソース グループを選択し、 [リソース グループの削除] をクリックしてください。
次のステップ
このチュートリアルでは、以下の内容を学習しました。
- アプリケーション パッケージを Batch アカウントに追加します。
- Batch アカウントおよびストレージ アカウントで認証します。
- Storage に入力ファイルをアップロードします。
- アプリケーションを実行するコンピューティング ノードのプールを作成します。
- 入力ファイルを処理するジョブとタスクを作成します。
- タスクの実行を監視する。
- 出力ファイルを取得します。
.NET API を使用して Batch ワークロードのスケジュール設定と処理を行う他の例については、GitHub の Batch C# サンプルを参照してください。