各ノードで複数のタスクを同時に実行することで、プール内の少数のコンピューティング ノードでリソース使用量を最大化できます。
一部のシナリオは、1 つのタスク専用のすべてのノードのリソースで最適に動作しますが、複数のタスクがそれらのリソースを共有する場合、特定のワークロードではジョブ時間が短くなり、コストが削減される場合があります。 次のシナリオについて考えてみましょう。
- データを共有できるタスクのデータ転送を最小限に抑えます。 共有データを少数のノードにコピーし、各ノードでタスクを並列に実行することで、データ転送料金を大幅に削減できます。 この方法は、各ノードにコピーするデータを地理的リージョン間で転送する必要がある場合に特に適用されます。
- 大量のメモリを必要とするタスクのメモリ使用量を最大化しますが、実行中は短い期間と可変時間に限られます。 このようなスパイクを効率的に処理するために、使用できるコンピューティング ノードの数は少なくなりますが、より大きいコンピューティング ノードの方がメモリが多くなります。 これらのノードには、各ノードで複数のタスクが並列に実行されていますが、各タスクは、ノードの豊富なメモリを異なる時間に利用できます。
- プール内でノード間通信が必要な場合に、ノード数の制限を軽減します。 現時点では、ノード間通信用に構成されたプールは、50 個のコンピューティング ノードに制限されています。 このようなプール内の各ノードがタスクを並列で実行できる場合は、より多くのタスクを同時に実行できます。
- コンピューティング環境を Azure に初めて移動する場合など、オンプレミスのコンピューティング クラスターをレプリケートします。 現在のオンプレミス ソリューションがコンピューティング ノードごとに複数のタスクを実行している場合は、ノード タスクの最大数を増やして、その構成をより厳密に反映することができます。
サンプル シナリオ
たとえば、Standard_D1ノードが十分に機能するような、CPUとメモリの要件を持つタスクアプリケーションを想定してください。 ただし、必要な時間内にジョブを完了するには、これらのノードのうち 1,000 個が必要です。
1 つの CPU コアを持つStandard_D1 ノードを使用する代わりに、それぞれ 16 個のコアを持つ Standard_D14 ノードを使用して、並列タスクの実行を有効にすることができます。 1,000 ノードではなく 16 倍のノードを使用する可能性があり、必要なのは 63 個のみです。 各ノードに大きなアプリケーション ファイルまたは参照データが必要な場合、データは 63 個のノードにのみコピーされるため、ジョブの実行時間と効率が向上します。
並列タスク実行を有効にする
コンピューティング ノードは、プール レベルで並列タスク実行用に構成します。 Batch .NET ライブラリを使用して、プールの作成時に CloudPool.TaskSlotsPerNode プロパティを設定します。 Batch REST API を使用している場合は、プールの作成時に要求本文に taskSlotsPerNode 要素を設定します。
注
taskSlotsPerNode要素と TaskSlotsPerNode プロパティは、プールの作成時にのみ設定できます。 プールが既に作成された後は変更できません。
Azure Batch では、ノードあたりのタスク スロットをノード コア数の最大 (4 倍) に設定できます。 たとえば、プールがサイズ "Large" (4 コア) のノードで構成されている場合、 taskSlotsPerNode は 16 に設定できます。 ただし、ノードのコア数に関係なく、ノードあたり 256 個を超えるタスク スロットを持つことはありません。 各ノード サイズのコア数の詳細については、 Cloud Services (クラシック) のサイズに関するページを参照してください。 サービスの制限の詳細については、「 Batch サービスのクォータと制限」を参照してください。
ヒント
プールのtaskSlotsPerNodeを作成するときは、必ず値を考慮してください。 たとえば、 $RunningTasks を評価する数式は、ノードあたりのタスクの増加の影響を大幅に受ける可能性があります。 詳細については、「 Batch プール内のコンピューティング ノードをスケーリングするための自動式を作成する」を参照してください。
タスクの配布を指定する
同時実行タスクを有効にする場合は、プール内のノード間でタスクを分散する方法を指定することが重要です。
CloudPool.TaskSchedulingPolicy プロパティを使用すると、タスクをプール内のすべてのノードに均等に割り当てる必要があることを指定できます ("拡散")。 または、タスクをプール内の別のノードに割り当てる前に、できるだけ多くのタスクを各ノードに割り当てる必要があることを指定できます ("パッキング")。
たとえば、CloudPool.TaskSlotsPerNode 値が 16 で構成されているStandard_D14 ノードのプール (前の例) について考えてみます。 CloudPool.TaskSchedulingPolicy が ComputeNodeFillType of Pack で構成されている場合、各ノードのすべての 16 コアの使用率が最大化され、自動スケール プールで使用されていないノード (タスクが割り当てされていないノード) がプールから削除されます。 自動スケーリングにより、リソースの使用量が最小限に抑え、コストを節約できます。
タスクごとに可変スロットを定義する
タスクは CloudTask.RequiredSlots プロパティを使用して定義でき、コンピューティング ノードで実行するために必要なスロットの数を指定できます。 既定値は 1 です。 タスクの重みがコンピューティング ノードのリソース使用量に関連付けられている場合は、可変タスク スロットを設定できます。 可変タスク スロットを使用すると、CPU やメモリなどのシステム リソースを大量に消費することなく、各コンピューティング ノードで適切な数の同時実行タスクを実行できます。
たとえば、プロパティ taskSlotsPerNode = 8を持つプールの場合、 requiredSlots = 8を使用してマルチコアの必要な CPU 集中型タスクを送信できますが、他のタスクは requiredSlots = 1に設定できます。 この混合ワークロードがスケジュールされると、CPU 集中型タスクはコンピューティング ノードでのみ実行され、他のタスクは他のノードで同時に (最大 8 個のタスクを同時に) 実行できます。 混合ワークロードは、コンピューティング ノード間でワークロードのバランスを取り、リソースの使用効率を向上するのに役立ちます。
タスクのrequiredSlotsがプールのtaskSlotsPerNodeより大きくならないように指定してください。そうしないとタスクは実行されません。 Batch サービスでは、現在、タスクを送信するときにこの競合が検証されません。 送信時にジョブが特定のプールにバインドされていない場合や、無効化した後再有効化することで異なるプールに変更される可能性があるため、競合は検証されません。
ヒント
可変タスク スロットを使用する場合、必要なスロットが多い大規模なタスクは、一部のノードでまだアイドル 状態のスロットがある場合でも、どのコンピューティング ノードでも十分なスロットが使用できないため、スケジュールに一時的に失敗する可能性があります。 これらのタスクのジョブ優先度を上げて、ノードで使用可能なスロットを競う機会を増やすことができます。
Batch サービスは、実行するタスクのスケジュールに失敗し、必要なスロットが使用可能になるまでスケジュールを再試行し続けると、 TaskScheduleFailEvent を生成します。 そのイベントをリッスンして、潜在的なタスク スケジュールの問題を検出し、それに応じて軽減することができます。
Batch .NET の例
次の Batch .NET API コード スニペットは、ノードごとに複数のタスク スロットを含むプールを作成する方法と、必要なスロットを持つタスクを送信する方法を示しています。
ノードごとに複数のタスク スロットを含むプールを作成する
このコード スニペットは、ノードごとに 4 つのタスク スロットが許可されている 4 つのノードを含むプールを作成する要求を示しています。 プール内の別のノードにタスクを割り当てる前に、各ノードにタスクを入力するタスク スケジュール ポリシーを指定します。
Batch .NET API を使用してプールを追加する方法の詳細については、「 BatchClient.PoolOperations.CreatePool」を参照してください。
CloudPool pool =
batchClient.PoolOperations.CreatePool(
poolId: "mypool",
targetDedicatedComputeNodes: 4
virtualMachineSize: "standard_d1_v2",
VirtualMachineConfiguration: new VirtualMachineConfiguration(
imageReference: new ImageReference(
publisher: "MicrosoftWindowsServer",
offer: "WindowsServer",
sku: "2019-datacenter-core",
version: "latest"),
nodeAgentSkuId: "batch.node.windows amd64");
pool.TaskSlotsPerNode = 4;
pool.TaskSchedulingPolicy = new TaskSchedulingPolicy(ComputeNodeFillType.Pack);
pool.Commit();
必要なスロットを含むタスクを作成する
このコード スニペットは、既定以外の requiredSlotsを持つタスクを作成します。 このタスクは、コンピューティング ノードで使用可能な空きスロットが十分にある場合に実行されます。
CloudTask task = new CloudTask(taskId, taskCommandLine)
{
RequiredSlots = 2
};
実行中のタスクとスロットの数を含むコンピューティング ノードを一覧表示する
このコード スニペットは、プール内のすべてのコンピューティング ノードを一覧表示し、ノードごとにタスクとタスク スロットを実行するための数を出力します。
ODATADetailLevel nodeDetail = new ODATADetailLevel(selectClause: "id,runningTasksCount,runningTaskSlotsCount");
IPagedEnumerable<ComputeNode> nodes = batchClient.PoolOperations.ListComputeNodes(poolId, nodeDetail);
await nodes.ForEachAsync(node =>
{
Console.WriteLine(node.Id + " :");
Console.WriteLine($"RunningTasks = {node.RunningTasksCount}, RunningTaskSlots = {node.RunningTaskSlotsCount}");
}).ConfigureAwait(continueOnCapturedContext: false);
ジョブのタスク数を一覧表示する
このコード スニペットは、タスクの状態ごとのタスク数とタスク スロット数の両方を含む、ジョブのタスク数を取得します。
TaskCountsResult result = await batchClient.JobOperations.GetJobTaskCountsAsync(jobId);
Console.WriteLine("\t\tActive\tRunning\tCompleted");
Console.WriteLine($"TaskCounts:\t{result.TaskCounts.Active}\t{result.TaskCounts.Running}\t{result.TaskCounts.Completed}");
Console.WriteLine($"TaskSlotCounts:\t{result.TaskSlotCounts.Active}\t{result.TaskSlotCounts.Running}\t{result.TaskSlotCounts.Completed}");
Batch REST の例
次の Batch REST API コード スニペットは、ノードごとに複数のタスク スロットを含むプールを作成する方法と、必要なスロットを持つタスクを送信する方法を示しています。
ノードごとに複数のタスク スロットを含むプールを作成する
このスニペットは、ノードごとに最大 4 つのタスクを含む 2 つの大きなノードを含むプールを作成する要求を示しています。
REST API を使用したプールの追加の詳細については、「 アカウントへのプールの追加」を参照してください。
{
"odata.metadata":"https://myaccount.myregion.batch.azure.com/$metadata#pools/@Element",
"id":"mypool",
"vmSize":"large",
"virtualMachineConfiguration": {
"imageReference": {
"publisher": "canonical",
"offer": "ubuntuserver",
"sku": "20.04-lts"
},
"nodeAgentSKUId": "batch.node.ubuntu 20.04"
},
"targetDedicatedComputeNodes":2,
"taskSlotsPerNode":4,
"enableInterNodeCommunication":true,
}
必要なスロットを含むタスクを作成する
このスニペットは、既定以外の requiredSlotsを持つタスクを追加する要求を示しています。 このタスクは、コンピューティング ノードで使用可能な空きスロットが十分にある場合にのみ実行されます。
{
"id": "taskId",
"commandLine": "bash -c 'echo hello'",
"userIdentity": {
"autoUser": {
"scope": "task",
"elevationLevel": "nonadmin"
}
},
"requiredSLots": 2
}
GitHub のコード サンプル
GitHub の ParallelTasks プロジェクトは、 CloudPool.TaskSlotsPerNode プロパティの使用方法を示しています。
この C# コンソール アプリケーションでは 、Batch .NET ライブラリを使用して、1 つ以上のコンピューティング ノードを含むプールを作成します。 これらのノードで構成可能な数のタスクを実行して、可変負荷をシミュレートします。 アプリケーションからの出力には、各タスクを実行したノードが表示されます。 アプリケーションには、ジョブのパラメーターと期間の概要も示されます。
次の例は、ParallelTasks サンプル アプリケーションの 2 つの異なる実行からの出力の概要部分を示しています。 ここで示すジョブの実行時間には、プールの作成時間は含まれません。各ジョブは、送信時にコンピューティング ノードが アイドル 状態であった以前に作成されたプールに送信されたためです。
サンプル アプリケーションの最初の実行では、プール内の 1 つのノードと、ノードごとに 1 つのタスクの既定の設定では、ジョブの実行時間が 30 分以上であることが示されています。
Nodes: 1
Node size: large
Task slots per node: 1
Max slots per task: 1
Tasks: 32
Duration: 00:30:01.4638023
サンプルの 2 回目の実行では、ジョブの実行時間が大幅に短縮されています。 この削減は、プールがノードごとに 4 つのタスクで構成されており、ほぼ 4 分の 1 の時間で並列タスク実行がジョブを完了できるようにするためです。
Nodes: 1
Node size: large
Task slots per node: 4
Max slots per task: 1
Tasks: 32
Duration: 00:08:48.2423500