練習 - 建立計算節點的集區以執行我們的作業

已完成

若要執行 Batch 作業,我們需要將集區新增至我們的 Batch 帳戶。 集區包含計算節點,也就是執行 Batch 作業的引擎。 您要在建立時指定節點的數目、大小和作業系統。 在此練習中,您將修改在上一個練習中建立的主控台應用程式,以將集區新增至您的 Batch 帳戶。

公司想要控制應用程式的成本,並要求您使用固定的節點數目。

重要

您必須有自己的 Azure 訂用帳戶才能執行本練習,且可能會產生費用。 如果您還沒有 Azure 訂用帳戶,請在開始前建立免費帳戶

為新的集區新增設定

  1. 在 Cloud Shell 中,於編輯器內編輯 Program.cs 檔案:

    code Program.cs
    
  2. 將下列屬性新增至 Program.cs 中的 Program 類別:

    private const string PoolId = "WinFFmpegPool";
    private const int DedicatedNodeCount = 0;
    private const int LowPriorityNodeCount = 3;
    private const string PoolVMSize = "STANDARD_D2_v2";
    private const string appPackageId = "ffmpeg";
    private const string appPackageVersion = "3.4";
    

    上述設定會在程式碼中用來建立集區。 查看每個變數,我們可以說明如下:

    • PoolId:我們的程式碼會用來參考其他 Batch 用戶端呼叫中集區的名稱。
    • LowPriorityNodeCount:您將建立一個具有三個低優先順序虛擬機器 (VM) 的集區。
    • PoolVMSize:VM 將為 STANDARD_A1_v2,這會提供節點 1 顆 CPU、2 GB RAM 和 10 GB SSD 儲存體。
    • appPackageId:在所建立節點上使用之應用程式封裝的名稱。
    • appPackageVersion:在所建立節點上使用之應用程式的版本。

更新 Main() 方法以支援非同步呼叫

我們將對雲端服務進行數個非同步呼叫,因此,首先要將 Main 變成非同步。 使用 C# .NET 7.1 版及更新版本,在主控台應用程式中支援非同步的 Main 方法。

  1. 先新增 System.Threading.Tasks 程式庫,變更主控台應用程式以允許非同步方法呼叫。

    using System.Threading.Tasks;
    using System.Collections.Generic; // Also add generics to allow the app to use Lists
    
  2. 接下來,更新 Main 方法簽章,如下所示:

    static async Task Main(string[] args)
    

建立集區

  1. 將下列新方法新增至 Program 類別,以建立 Batch 集區。 方法:

    • 建立映像參考物件,以儲存要新增至集區之節點的設定。
    • 使用映像參考來建立 VirtualMachineConfiguration 物件。
    • 使用先前宣告的屬性和 VirtualMachineConfiguration,建立未繫結的集區。
    • 將應用程式封裝參考新增至集區。
    • 在 Azure 上建立集區。
    • 接受兩個參數 batchClientPoolId
      private static async Task CreateBatchPoolAsync(BatchClient batchClient, string poolId)
        {
            CloudPool pool = null;
            Console.WriteLine("Creating pool [{0}]...", poolId);
    
            // Create an image reference object to store the settings for the nodes to be added to the pool
            ImageReference imageReference = new ImageReference(
                    publisher: "MicrosoftWindowsServer",
                    offer: "WindowsServer",
                    sku: "2012-R2-Datacenter-smalldisk",
                    version: "latest");
    
            // Use the image reference to create a VirtualMachineConfiguration object
            VirtualMachineConfiguration virtualMachineConfiguration =
            new VirtualMachineConfiguration(
                imageReference: imageReference,
                nodeAgentSkuId: "batch.node.windows amd64");
    
            try
            {
                // Create an unbound pool. No pool is actually created in the Batch service until we call
                // CloudPool.CommitAsync(). This CloudPool instance is therefore considered "unbound," and we can
                // modify its properties.
                pool = batchClient.PoolOperations.CreatePool(
                    poolId: poolId,
                    targetDedicatedComputeNodes: DedicatedNodeCount,
                    targetLowPriorityComputeNodes: LowPriorityNodeCount,
                    virtualMachineSize: PoolVMSize,
                    virtualMachineConfiguration: virtualMachineConfiguration);  
    
                // Specify the application and version to install on the compute nodes
                pool.ApplicationPackageReferences = new List<ApplicationPackageReference>
                {
                    new ApplicationPackageReference
                    {
                    ApplicationId = appPackageId,
                    Version = appPackageVersion
                    }
                };
    
                // Create the pool
                await pool.CommitAsync();
            }
            catch (BatchException be)
            {
                // Accept the specific error code PoolExists as that is expected if the pool already exists
                if (be.RequestInformation?.BatchError?.Code == BatchErrorCodeStrings.PoolExists)
                {
                    Console.WriteLine("The pool [{0}] already existed when we tried to create it", poolId);
                }
                else
                {
                    throw; // Any other exception is unexpected
                }
            }
        }  
    
  2. 從我們的 Main 方法呼叫 CreateBatchPoolAsync。 Main 方法現在應該如下所示:

    static async Task Main(string[] args)
    {
        // Read the environment variables to allow the app to connect to the Azure Batch account
        batchAccountUrl = Environment.GetEnvironmentVariable(envVarBatchURI);
        batchAccountName = Environment.GetEnvironmentVariable(envVarBatchName);
        batchAccountKey = Environment.GetEnvironmentVariable(envVarKey);
    
        // Show the user the batch the app is attaching to
        Console.WriteLine("URL: {0}, Name: {1}, Key: {2}", batchAccountUrl, batchAccountName, batchAccountKey);
    
        // The batch client requires a BatchSharedKeyCredentials object to open a connection
        var sharedKeyCredentials = new BatchSharedKeyCredentials(batchAccountUrl, batchAccountName, batchAccountKey);
        var batchClient = BatchClient.Open(sharedKeyCredentials);
    
        // Create the Batch pool, which contains the compute nodes that execute tasks.
        await CreateBatchPoolAsync(batchClient, PoolId);
    }
    

測試應用程式

  1. 在程式碼編輯器中,以滑鼠右鍵按一下並選取 [儲存],然後選取 [結束]

  2. 在 Cloud Shell 中,使用下列命令來編譯並執行應用程式:

    dotnet run
    
  3. 應用程式需要幾分鐘的時間才能執行,而您應該會取得下列輸出:

    URL: <your batch account url, Name: <your batch name>, Key: <your batch key>
    Creating pool [WinFFmpegPool]...
    

請記住,每個節點都是一部執行 Windows 2012 Server 的 VM,且只配置一個 CPU 和 2 GB RAM。 Batch 需要時間,從 Azure Virtual Machine Marketplace 傳送那些 Windows VM 映像、建立 VM 基礎結構和網路功能,最後啟動每個節點。 這是大部分 Batch 解決方案最耗時的部分。 典型的 Batch 工作流程不會清除集區及其節點。