演習 - アプリの Azure Batch ジョブの進捗状況を監視し、記録するコードを追加する

完了

Batch クライアント API を利用することでアプリでは、プール、ノード、ジョブ、タスクの現在の状態を監視できます。

動画を変換する会社のコンソール アプリを完成させるには、アプリでファイル変換の状況を監視し、レポートする必要があります。 また、動画の変換後、ジョブやプールを削除する機能をアプリに追加することで、Batch により発生するコストを減らします。 ファイル ストレージ コストを減らすために、アップロード後の動画ファイルを削除することもできます。

アプリでは、既存のプールをチェックし、存在しない場合は作成します。 ジョブとタスクを開始し、監視します。 タスクが正常に完了すると、作成したジョブとプールを削除するオプションがアプリから提示されます。 アップロードされた動画は、BLOB ストレージのコストを節約するために、アプリによって自動的に削除されます。

監視を追加する

  1. Cloud Shell のエディターで Program.cs ファイルを編集します。

    code Program.cs
    
  2. 次のメソッド MonitorTasksAsync()Program.cs に追加し、ジョブ タスクを監視します。

    private static async Task<bool> MonitorTasksAsync(BatchClient batchClient, string jobId, TimeSpan timeout)
    {
        bool allTasksSuccessful = true;
        const string completeMessage = "All tasks reached state Completed.";
        const string incompleteMessage = "One or more tasks failed to reach the Completed state within the timeout period.";
        const string successMessage = "Success! All tasks completed successfully. Output files uploaded to output container.";
        const string failureMessage = "One or more tasks failed.";
    
        Console.WriteLine("Monitoring all tasks for 'Completed' state, timeout in {0}...", timeout.ToString());
    
        // We use a TaskStateMonitor to monitor the state of our tasks. In this case, we will wait for all tasks to
        // reach the Completed state.
        IEnumerable<CloudTask> addedTasks = batchClient.JobOperations.ListTasks(JobId);
    
        TaskStateMonitor taskStateMonitor = batchClient.Utilities.CreateTaskStateMonitor();
        try
        {
            await taskStateMonitor.WhenAll(addedTasks, TaskState.Completed, timeout);
        }
        catch (TimeoutException)
        {
            await batchClient.JobOperations.TerminateJobAsync(jobId);
            Console.WriteLine(incompleteMessage);
            return false;
        }
        await batchClient.JobOperations.TerminateJobAsync(jobId);
        Console.WriteLine(completeMessage);
    
        // All tasks have reached the "Completed" state, however, this does not guarantee all tasks completed successfully.
        // Here we further check for any tasks with an execution result of "Failure".
    
        // Obtain the collection of tasks currently managed by the job. 
        // Use a detail level to specify that only the "id" property of each task should be populated. 
        // See https://learn.microsoft.com/azure/batch/batch-efficient-list-queries
        ODATADetailLevel detail = new ODATADetailLevel(selectClause: "executionInfo");
    
        // Filter for tasks with 'Failure' result.
        detail.FilterClause = "executionInfo/result eq 'Failure'";
    
        List<CloudTask> failedTasks = await batchClient.JobOperations.ListTasks(jobId, detail).ToListAsync();
    
        if (failedTasks.Any())
        {
            allTasksSuccessful = false;
            Console.WriteLine(failureMessage);
        }
        else
        {
            Console.WriteLine(successMessage);
        }
        return allTasksSuccessful;
    }
    

    TaskStateMonitor オブジェクトが呼び出され、すべてのタスクの状態が (TaskState.Completed) になると戻されます。 timeout 値より待機時間が長くなると、アプリはタイムアウトになります。

    このメソッドでは batchClient.JobOperations.ListTasks を利用することで、Batch アカウントでタスクの現在の状態を取得します。 ODATADetailLevel パラメーターを渡すことで、必要な情報のみを返すようにこれらの呼び出しをフィルター処理できます。 すべてのタスクが完了したら、コードでそれらすべてが正常に完了したことをチェックする必要があるため、ListTasks 呼び出しで "executionInfo/result eq 'Failure'" のフィルターを使用して、失敗したすべてのタスクを返します。

  3. Main() の using ブロック内に新しい MonitorTasksAsync() メソッドの呼び出しを追加し、AddTaskAsync 呼び出しから返されたタスクの一覧を保存します。

    using (BatchClient batchClient = BatchClient.Open(sharedKeyCredentials))
    {
        // Create the Batch pool, which contains the compute nodes that execute the tasks.
        await CreatePoolIfNotExistAsync(batchClient, PoolId);
    
        // Create the job that runs the tasks.
        await CreateJobAsync(batchClient, JobId, PoolId);
    
        // Create a collection of tasks and add them to the Batch job. 
        // Provide a shared access signature for the tasks so that they can upload their output
        // to the Storage container.
        List<CloudTask> runningTasks = await AddTasksAsync(batchClient, JobId, inputFiles, outputContainerSasUrl);
    
        // Monitor task success or failure, specifying a maximum amount of time to wait for
        // the tasks to complete.
        await MonitorTasksAsync(batchClient, JobId, TimeSpan.FromMinutes(30));
    }
    

クリーンアップ

  1. using ブロック内で、MonitorTasks メソッドの呼び出しの下にこのクリーンアップ コードを追加します。

    // Delete input container in storage
    Console.WriteLine("Deleting container [{0}]...", inputContainerName);
    CloudBlobContainer container = blobClient.GetContainerReference(inputContainerName);
    await container.DeleteIfExistsAsync();
    
    // Clean up the job (if the user so chooses)
    Console.WriteLine();
    Console.Write("Delete job? [yes] no: ");
    string response = Console.ReadLine().ToLower();
    if (response != "n" && response != "no")
    {
        await batchClient.JobOperations.DeleteJobAsync(JobId);
    }
    
    // Clean up the pool (if the user so chooses - do not delete the pool if new batches of videos are ready to process)
    Console.Write("Delete pool? [yes] no: ");
    response = Console.ReadLine().ToLower();
    if (response != "n" && response != "no")
    {
        Console.WriteLine("Deleting pool ...");
        await batchClient.PoolOperations.DeletePoolAsync(PoolId);
        Console.WriteLine("Pool deleted.");
    }
    

    上記のコードでは、アップロードされたすべての動画を含む入力コンテナーが削除されます。

    その後、ユーザーはターミナルから、ジョブとプールの削除を選択するように求められます。 batchClient により、アプリでこれらのコンポーネントを削除できます。

コンソール アプリをテストする

  1. コード エディターで、右クリックして [保存] を選択し、[終了] を選択します。

  2. アプリをビルドし、実行します。

    dotnet run
    
  3. 次のような出力が表示されるはずです。

    Creating container [input].
    Creating container [output].
    Uploading file ~\cutifypets\InputFiles\3.mp4 to container [input]...
    Uploading file ~\cutifypets\InputFiles\2.mp4 to container [input]...
    Uploading file ~\cutifypets\InputFiles\4.mp4 to container [input]...
    Uploading file ~\cutifypets\InputFiles\1.mp4 to container [input]...
    Uploading file ~\cutifypets\InputFiles\5.mp4 to container [input]...
    Uploading file ~\cutifypets\InputFiles\6.mp4 to container [input]...
    Creating pool [WinFFmpegPool]...
    Creating job [WinFFmpegJob]...
    Adding 6 tasks to job [WinFFmpegJob]...
    Monitoring all tasks for 'Completed' state, timeout in 00:30:00...
    All tasks reached state Completed.
    Success! All tasks completed successfully. Output files uploaded to output container.
    Deleting container [input]...
    
    Delete job? [yes] no: y
    Delete pool? [yes] no: y
    
    Sample complete, hit ENTER to exit...
    
  4. ただし、前の実行のジョブがまだ存在している場合、アプリは失敗します。前のアプリにはジョブをクリーンアップするコードが含まれていなかったためです。 Azure portal または Cloud Shell で次のようにジョブを削除できます。

    az batch job delete --job-id WinFFmpegJob \
    --account-name $BATCH_NAME \
    --account-key $BATCH_KEY \
    --account-endpoint $BATCH_URL
    
    Are you sure you want to perform this operation? (y/n):
    

    プロンプトで「y」と入力します。

  5. ノード (Windows 2012 を実行する 3 つの仮想マシン) がアイドル状態になり、ジョブの実行を待つため、今回はコンソール アプリがずっと速く実行されます。