Skip to content

Commit aa178eb

Browse files
authored
Update tutorial-parallel-dotnet.md
1 parent 9cf557b commit aa178eb

File tree

1 file changed

+100
-85
lines changed

1 file changed

+100
-85
lines changed

articles/batch/tutorial-parallel-dotnet.md

Lines changed: 100 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,15 @@ Uri accountUri = new Uri("https://<storage-account-name>.blob.core.windows.net/"
126126
BlobServiceClient blobClient = new BlobServiceClient(accountUri, new DefaultAzureCredential());
127127
```
128128

129-
The app creates a [BatchClient](/dotnet/api/microsoft.azure.batch.batchclient) object to create and manage pools, jobs, and tasks in the Batch service. The Batch client in the sample uses shared key authentication. Batch also supports authentication through [Microsoft Entra ID](batch-aad-auth.md) to authenticate individual users or an unattended application.
129+
The app creates a reference to the [BatchAccountResource](/dotnet/api/azure.resourcemanager.batch.batchaccountresource) via the Resource managemer's [ArmClient](dotnet/api/azure.resourcemanager.armclient to create the pool in the Batch service. The Arm client in the sample uses [DefaultAzureCredential](dotnet/api/azure.identity.defaultazurecredential) authentication.
130+
131+
```csharp
132+
ArmClient _armClient = new ArmClient(new DefaultAzureCredential());
133+
var batchAccountIdentifier = ResourceIdentifier.Parse(BatchAccountResourceID);
134+
BatchAccountResource batchAccount = await _armClient.GetBatchAccountResource(batchAccountIdentifier).GetAsync();
135+
```
136+
137+
The app creates a [BatchClient](/dotnet/api/azure.compute.batch.batchclient) object to create and jobs and tasks in the Batch service. The Batch client in the sample uses [DefaultAzureCredential](dotnet/api/azure.identity.defaultazurecredential) authentication.
130138

131139
```csharp
132140
// TODO: Replace <batch-account-name> with your actual storage account name
@@ -136,14 +144,14 @@ BatchClient _batchClient = new BatchClient(batchUri, new DefaultAzureCredential(
136144

137145
### Upload input files
138146

139-
The app passes the `blobClient` object to the `CreateContainerIfNotExistAsync` method to create a storage container for the input files (MP4 format) and a container for the task output.
147+
The app passes the `blobServerClient` object to the `CreateContainerIfNotExistc` method to create a storage container for the input files (MP4 format) and a container for the task output.
140148

141149
```csharp
142-
CreateContainerIfNotExistAsync(blobClient, inputContainerName);
143-
CreateContainerIfNotExistAsync(blobClient, outputContainerName);
150+
CreateContainerIfNotExist(blobClient, inputContainerName);
151+
CreateContainerIfNotExist(blobClient, outputContainerName);
144152
```
145153

146-
Then, files are uploaded to the input container from the local *InputFiles* folder. The files in storage are defined as Batch [ResourceFile](/dotnet/api/microsoft.azure.batch.resourcefile) objects that Batch can later download to compute nodes.
154+
Then, files are uploaded to the input container from the local *InputFiles* folder. The files in storage are defined as Batch [ResourceFile](/dotnet/api/azure.compute.batch.resourcefile) objects that Batch can later download to compute nodes.
147155

148156
Two methods in *Program.cs* are involved in uploading the files:
149157

@@ -166,7 +174,7 @@ For details about uploading files as blobs to a storage account with .NET, see [
166174

167175
### Create a pool of compute nodes
168176

169-
Next, the sample creates a pool of compute nodes in the Batch account with a call to `CreatePoolIfNotExistAsync`. This defined method uses the [BatchClient.PoolOperations.CreatePool](/dotnet/api/microsoft.azure.batch.pooloperations.createpool) method to set the number of nodes, VM size, and a pool configuration. Here, a [VirtualMachineConfiguration](/dotnet/api/microsoft.azure.batch.virtualmachineconfiguration) object specifies an [ImageReference](/dotnet/api/microsoft.azure.batch.imagereference) to a Windows Server image published in the Azure Marketplace. Batch supports a wide range of VM images in the Azure Marketplace, as well as custom VM images.
177+
Next, the sample creates a pool of compute nodes in the Batch account with a call to `CreatePoolIfNotExistAsync`. This defined method uses the [BatchAccountResource.GetBatchAccountPools().CreateOrUpdateAsync](/dotnet/api/azure.resourcemanager.batch.batchaccountpoolcollection.createorupdateasync) method to set the number of nodes, VM size, and a pool configuration. Here, a [BatchVmConfiguration](/dotnet/api/azure.resourcemanager.batch.models.batchvmconfiguration) object specifies an [BatchImageReference ](/dotnet/api/azure.resourcemanager.batch.models.batchimagereference) to a Windows Server image published in the Azure Marketplace. Batch supports a wide range of VM images in the Azure Marketplace, as well as custom VM images.
170178

171179
The number of nodes and VM size are set using defined constants. Batch supports dedicated nodes and [Spot nodes](batch-spot-vms.md), and you can use either or both in your pools. Dedicated nodes are reserved for your pool. Spot nodes are offered at a reduced price from surplus VM capacity in Azure. Spot nodes become unavailable if Azure does not have enough capacity. The sample by default creates a pool containing only 5 Spot nodes in size *Standard_A1_v2*.
172180

@@ -175,123 +183,130 @@ The number of nodes and VM size are set using defined constants. Batch supports
175183
176184
The ffmpeg application is deployed to the compute nodes by adding an [ApplicationPackageReference](/dotnet/api/microsoft.azure.batch.applicationpackagereference) to the pool configuration.
177185

178-
The [CommitAsync](/dotnet/api/microsoft.azure.batch.cloudpool.commitasync) method submits the pool to the Batch service.
179-
180186
```csharp
181-
ImageReference imageReference = new ImageReference(
182-
publisher: "MicrosoftWindowsServer",
183-
offer: "WindowsServer",
184-
sku: "2016-Datacenter-smalldisk",
185-
version: "latest");
186-
187-
VirtualMachineConfiguration virtualMachineConfiguration =
188-
new VirtualMachineConfiguration(
189-
imageReference: imageReference,
190-
nodeAgentSkuId: "batch.node.windows amd64");
191-
192-
pool = batchClient.PoolOperations.CreatePool(
193-
poolId: poolId,
194-
targetDedicatedComputeNodes: DedicatedNodeCount,
195-
targetLowPriorityComputeNodes: LowPriorityNodeCount,
196-
virtualMachineSize: PoolVMSize,
197-
virtualMachineConfiguration: virtualMachineConfiguration);
198-
199-
pool.ApplicationPackageReferences = new List<ApplicationPackageReference>
200-
{
201-
new ApplicationPackageReference {
202-
ApplicationId = appPackageId,
203-
Version = appPackageVersion}};
187+
var credential = new DefaultAzureCredential();
188+
ArmClient _armClient = new ArmClient(credential);
204189

205-
await pool.CommitAsync();
190+
var batchAccountIdentifier = ResourceIdentifier.Parse(BatchAccountResourceID);
191+
BatchAccountResource batchAccount = await _armClient.GetBatchAccountResource(batchAccountIdentifier).GetAsync();
192+
193+
BatchAccountPoolCollection collection = batchAccount.GetBatchAccountPools();
194+
if (collection.Exists(poolId) == false)
195+
{
196+
var poolName = poolId;
197+
var imageReference = new BatchImageReference()
198+
{
199+
Publisher = "MicrosoftWindowsServer",
200+
Offer = "WindowsServer",
201+
Sku = "2019-datacenter-smalldisk",
202+
Version = "latest"
203+
};
204+
string nodeAgentSku = "batch.node.windows amd64";
205+
206+
207+
ArmOperation<BatchAccountPoolResource> armOperation = await batchAccount.GetBatchAccountPools().CreateOrUpdateAsync(
208+
WaitUntil.Completed, poolName, new BatchAccountPoolData()
209+
{
210+
VmSize = "Standard_DS1_v2",
211+
DeploymentConfiguration = new BatchDeploymentConfiguration()
212+
{
213+
VmConfiguration = new BatchVmConfiguration(imageReference, nodeAgentSku)
214+
},
215+
ScaleSettings = new BatchAccountPoolScaleSettings()
216+
{
217+
FixedScale = new BatchAccountFixedScaleSettings()
218+
{
219+
TargetDedicatedNodes = DedicatedNodeCount,
220+
TargetLowPriorityNodes = LowPriorityNodeCount
221+
}
222+
},
223+
Identity = new ManagedServiceIdentity(ManagedServiceIdentityType.UserAssigned)
224+
{
225+
UserAssignedIdentities =
226+
{
227+
[new ResourceIdentifier(ManagedIdentityId)] = new Azure.ResourceManager.Models.UserAssignedIdentity(),
228+
},
229+
},
230+
ApplicationPackages =
231+
{
232+
new Azure.ResourceManager.Batch.Models.BatchApplicationPackageReference(new ResourceIdentifier(appPacakgeResourceID))
233+
{
234+
Version = appPackageVersion,
235+
}
236+
},
237+
238+
});
239+
BatchAccountPoolResource pool = armOperation.Value;
206240
```
207241

208242
### Create a job
209243

210-
A Batch job specifies a pool to run tasks on and optional settings such as a priority and schedule for the work. The sample creates a job with a call to `CreateJobAsync`. This defined method uses the [BatchClient.JobOperations.CreateJob](/dotnet/api/microsoft.azure.batch.joboperations.createjob) method to create a job on your pool.
211-
212-
The [CommitAsync](/dotnet/api/microsoft.azure.batch.cloudjob.commitasync) method submits the job to the Batch service. Initially the job has no tasks.
244+
A Batch job specifies a pool to run tasks on and optional settings such as a priority and schedule for the work. The sample creates a job with a call to `CreateJobAsync`. This defined method uses the [BatchClient.CreateJobAsync](/dotnet/api/azure.compute.batch.batchclient.createjobasync) method to create a job on your pool.
213245

214246
```csharp
215-
CloudJob job = batchClient.JobOperations.CreateJob();
216-
job.Id = JobId;
217-
job.PoolInformation = new PoolInformation { PoolId = PoolId };
218-
219-
await job.CommitAsync();
247+
BatchJobCreateContent batchJobCreateContent = new BatchJobCreateContent(jobId, new BatchPoolInfo { PoolId = poolId });
248+
await batchClient.CreateJobAsync(batchJobCreateContent);
220249
```
221250

222251
### Create tasks
223252

224-
The sample creates tasks in the job with a call to the `AddTasksAsync` method, which creates a list of [CloudTask](/dotnet/api/microsoft.azure.batch.cloudtask) objects. Each `CloudTask` runs ffmpeg to process an input `ResourceFile` object using a [CommandLine](/dotnet/api/microsoft.azure.batch.cloudtask.commandline) property. ffmpeg was previously installed on each node when the pool was created. Here, the command line runs ffmpeg to convert each input MP4 (video) file to an MP3 (audio) file.
253+
The sample creates tasks in the job with a call to the `AddTasksAsync` method, which creates a list of [BatchTask ](/dotnet/api/azure.compute.batch.batchtask) objects. Each `BatchTask` runs ffmpeg to process an input `ResourceFile` object using a [CommandLine](/dotnet/api/azure.compute.batch.batchtask.commandline) property. ffmpeg was previously installed on each node when the pool was created. Here, the command line runs ffmpeg to convert each input MP4 (video) file to an MP3 (audio) file.
225254

226-
The sample creates an [OutputFile](/dotnet/api/microsoft.azure.batch.outputfile) object for the MP3 file after running the command line. Each task's output files (one, in this case) are uploaded to a container in the linked storage account, using the task's [OutputFiles](/dotnet/api/microsoft.azure.batch.cloudtask.outputfiles) property. Previously in the code sample, a shared access signature URL (`outputContainerSasUrl`) was obtained to provide write access to the output container. Note the conditions set on the `outputFile` object. An output file from a task is only uploaded to the container after the task has successfully completed (`OutputFileUploadCondition.TaskSuccess`). See the full [code sample](https://github.com/Azure-Samples/batch-dotnet-ffmpeg-tutorial) on GitHub for further implementation details.
255+
The sample creates an [OutputFile](/dotnet/api/azure.compute.batch.outputfile) object for the MP3 file after running the command line. Each task's output files (one, in this case) are uploaded to a container in the linked storage account, using the task's [OutputFiles](/dotnet/api/azure.compute.batch.batchtask.outputfiles) property. Note the conditions set on the `outputFile` object. An output file from a task is only uploaded to the container after the task has successfully completed (`OutputFileUploadCondition.TaskSuccess`). See the full [code sample](https://github.com/Azure-Samples/batch-dotnet-ffmpeg-tutorial) on GitHub for further implementation details.
227256
228-
Then, the sample adds tasks to the job with the [AddTaskAsync](/dotnet/api/microsoft.azure.batch.joboperations.addtaskasync) method, which queues them to run on the compute nodes.
257+
Then, the sample adds tasks to the job with the [CreateTaskAsync ](/dotnet/api/azure.compute.batch.batchclient.createtaskasync) method, which queues them to run on the compute nodes.
229258

230259
Replace the executable's file path with the name of the version that you downloaded. This sample code uses the example `ffmpeg-4.3.1-2020-11-08-full_build`.
231260

232261
```csharp
233-
// Create a collection to hold the tasks added to the job.
234-
List<CloudTask> tasks = new List<CloudTask>();
262+
// Create a collection to hold the tasks added to the job:
263+
List<BatchTaskCreateContent> tasks = new List<BatchTaskCreateContent>();
235264

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

240-
// Define task command line to convert each input file.
270+
// Define task command line to convert the video format from MP4 to MP3 using ffmpeg.
271+
// Note that ffmpeg syntax specifies the format as the file extension of the input file
272+
// and the output file respectively. In this case inputs are MP4.
241273
string appPath = String.Format("%AZ_BATCH_APP_PACKAGE_{0}#{1}%", appPackageId, appPackageVersion);
242-
string inputMediaFile = inputFiles[i].FilePath;
274+
string inputMediaFile = inputFiles[i].StorageContainerUrl;
243275
string outputMediaFile = String.Format("{0}{1}",
244276
System.IO.Path.GetFileNameWithoutExtension(inputMediaFile),
245277
".mp3");
246-
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);
278+
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);
247279

248-
// Create a cloud task (with the task ID and command line)
249-
CloudTask task = new CloudTask(taskId, taskCommandLine);
250-
task.ResourceFiles = new List<ResourceFile> { inputFiles[i] };
280+
// Create a batch task (with the task ID and command line) and add it to the task list
251281
252-
// Task output file
253-
List<OutputFile> outputFileList = new List<OutputFile>();
254-
OutputFileBlobContainerDestination outputContainer = new OutputFileBlobContainerDestination(outputContainerSasUrl);
255-
OutputFile outputFile = new OutputFile(outputMediaFile,
256-
new OutputFileDestination(outputContainer),
257-
new OutputFileUploadOptions(OutputFileUploadCondition.TaskSuccess));
258-
outputFileList.Add(outputFile);
259-
task.OutputFiles = outputFileList;
260-
tasks.Add(task);
261-
}
262-
263-
// Add tasks as a collection
264-
await batchClient.JobOperations.AddTaskAsync(jobId, tasks);
265-
return tasks
266-
```
282+
BatchTaskCreateContent batchTaskCreateContent = new BatchTaskCreateContent(taskId, taskCommandLine);
283+
batchTaskCreateContent.ResourceFiles.Add(inputFiles[i]);
267284

268-
### Monitor tasks
269-
270-
When Batch adds tasks to a job, the service automatically queues and schedules them for execution on compute nodes in the associated pool. Based on the settings you specify, Batch handles all task queuing, scheduling, retrying, and other task administration duties.
285+
// Task output file will be uploaded to the output container in Storage.
286+
// TODO: Replace <storage-account-name> with your actual storage account name
287+
OutputFileBlobContainerDestination outputContainer = new OutputFileBlobContainerDestination("https://<storage-account-name>.blob.core.windows.net/output/" + outputMediaFile)
288+
{
289+
IdentityReference = inputFiles[i].IdentityReference,
290+
};
271291

272-
There are many approaches to monitoring task execution. This sample defines a `MonitorTasks` method to report only on completion and task failure or success states. The `MonitorTasks` code specifies an [ODATADetailLevel](/dotnet/api/microsoft.azure.batch.odatadetaillevel) to efficiently select only minimal information about the tasks. Then, it creates a [TaskStateMonitor](/dotnet/api/microsoft.azure.batch.taskstatemonitor), which provides helper utilities for monitoring task states. In `MonitorTasks`, the sample waits for all tasks to reach `TaskState.Completed` within a time limit. Then it terminates the job and reports on any tasks that completed but may have encountered a failure such as a non-zero exit code.
292+
OutputFile outputFile = new OutputFile(outputMediaFile,
293+
new OutputFileDestination() { Container = outputContainer },
294+
new OutputFileUploadConfig(OutputFileUploadCondition.TaskSuccess));
295+
batchTaskCreateContent.OutputFiles.Add(outputFile);
273296

274-
```csharp
275-
TaskStateMonitor taskStateMonitor = batchClient.Utilities.CreateTaskStateMonitor();
276-
try
277-
{
278-
await taskStateMonitor.WhenAll(addedTasks, TaskState.Completed, timeout);
297+
tasks.Add(batchTaskCreateContent);
279298
}
280-
catch (TimeoutException)
281-
{
282-
batchClient.JobOperations.TerminateJob(jobId);
283-
Console.WriteLine(incompleteMessage);
284-
return false;
285-
}
286-
batchClient.JobOperations.TerminateJob(jobId);
287-
Console.WriteLine(completeMessage);
288-
...
289299

300+
// Call BatchClient.CreateTaskCollectionAsync() to add the tasks as a collection rather than making a
301+
// separate call for each. Bulk task submission helps to ensure efficient underlying API
302+
// calls to the Batch service.
303+
304+
await batchClient.CreateTaskCollectionAsync(jobId, new BatchTaskGroup(tasks));
290305
```
291306

292307
## Clean up resources
293308

294-
After it runs the tasks, the app automatically deletes the input storage container it created, and gives you the option to delete the Batch pool and job. The BatchClient's [JobOperations](/dotnet/api/microsoft.azure.batch.batchclient.joboperations) and [PoolOperations](/dotnet/api/microsoft.azure.batch.batchclient.pooloperations) classes both have corresponding delete methods, which are called if you confirm deletion. Although you're not charged for jobs and tasks themselves, you are charged for compute nodes. Thus, we recommend that you allocate pools only as needed. When you delete the pool, all task output on the nodes is deleted. However, the output files remain in the storage account.
309+
After it runs the tasks, the app automatically deletes the input storage container it created, and gives you the option to delete the Batch pool and job. The BatchClient has a method to delete a job [DeleteJobAsync](/dotnet/api/azure.compute.batch.batchclient.deletejobasync) and delete a pool [DeletePoolAsync](/dotnet/api/azure.compute.batch.batchclient.deletepoolasync), which are called if you confirm deletion. Although you're not charged for jobs and tasks themselves, you are charged for compute nodes. Thus, we recommend that you allocate pools only as needed. When you delete the pool, all task output on the nodes is deleted. However, the output files remain in the storage account.
295310

296311
When no longer needed, delete the resource group, Batch account, and storage account. To do so in the Azure portal, select the resource group for the Batch account and click **Delete resource group**.
297312

0 commit comments

Comments
 (0)