Упражнение. Создание пула вычислительных узлов для выполнения заданий

Завершено

Чтобы запустить пакетное задание, необходимо добавить пул в учетную запись Batch. Пул содержит вычислительные узлы, которые являются движками для выполнения задания Batch. Вы указываете количество, размер и операционную систему узлов во время создания. В этом упражнении вы измените консольное приложение, созданное в предыдущем упражнении, чтобы добавить пул в вашу учетную запись Batch.

Ваша компания хочет контролировать затраты на приложение и попросила вас использовать фиксированное количество узлов.

Важный

Для выполнения этого упражнения вам потребуется собственная подписка Azure, и возможно, вы понесете расходы. Если у вас еще нет подписки Azure, создайте бесплатную учетную запись перед началом.

Добавление параметров для нового пула

  1. В Cloud Shell измените файл Program.cs в редакторе:

    code Program.cs
    
  2. Добавьте следующие свойства в класс Program в Program.cs:

    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: имя, которое наш код использует для ссылки на пул в других вызовах клиентской пакетной службы.
    • LowPriorityNodeCount: вы собираетесь создать пул из трёх виртуальных машин с низким приоритетом.
    • PoolVMSize: виртуальные машины будут STANDARD_A1_v2, что дает узлам 1 ЦП, 2 ГБ ОЗУ и 10 ГБ хранилища 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.
    • Принимает два параметра, batchClient и PoolId.
      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. Вызов CreateBatchPoolAsync из нашего метода Main. Теперь метод 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 Server 2012 с одним ЦП и 2 ГБ ОЗУ. Передача образов виртуальных машин Windows из Azure Virtual Machine Marketplace, создание сетевой и инфраструктуры виртуальной машины и последующий запуск каждого узла через Azure Batch требуют времени. Это наиболее времязатратная часть большинства пакетных решений. Типичный рабочий процесс пакетной службы не очищает пул и его узлы.