Tutorial: Executar uma carga de trabalho paralela com o Lote do Azure usando a API do .NET

Use o Lote do Azure para executar trabalhos em lote de HPC (computação de alto desempenho) e paralelos em larga escala com eficiência no Azure. Este tutorial percorre um exemplo de C# para executar uma carga de trabalho paralela usando o Lote. Você conhecerá um fluxo de trabalho de aplicativo comum no Lote e como interagir programaticamente com recursos do Armazenamento e do Lote.

  • Adicionar um pacote de aplicativos à sua conta do Batch.
  • Autenticar com contas do Lote e de Armazenamento.
  • Carregar arquivos de entrada para o Armazenamento.
  • Criar um pool de nós de computação para executar um aplicativo.
  • Criar um trabalho e tarefas para processar os arquivos de entrada.
  • Monitorar a execução da tarefa.
  • Recuperar arquivos de saída.

Neste tutorial, você converterá arquivos de mídia MP4 para o formato MP3, em paralelo, usando a ferramenta de código aberto ffmpeg.

Caso você não tenha uma assinatura do Azure, crie uma conta gratuita do Azure antes de começar.

Pré-requisitos

Entrar no Azure

Entre no Portal do Azure.

Adicionar um pacote de aplicativos

Use o portal do Azure para adicionar o ffmpeg à sua conta do Lote como um pacote de aplicativos. Os pacotes de aplicativos ajudam a gerenciar aplicativos de tarefas e sua implantação nos nós de computação em seu pool.

  1. No portal do Azure, clique em Mais serviços>contas do Lote e selecione o nome da sua conta do Lote.

  2. Clique em Aplicativos>Adicionar.

    Captura de tela da seção Aplicativos da conta do Lote.

  3. Insira ffmpeg no campo ID do aplicativo e uma versão do pacote da 4.3.1 no campo Versão. Selecione o arquivo zip do ffmpeg que você baixou e, em seguida, selecione Enviar. O pacote de aplicativos ffmpeg é adicionado à sua conta do Lote.

    Captura de tela dos campos de ID e versão na seção Adicionar aplicativo.

Obter credenciais da conta

Para este exemplo, você precisa fornecer credenciais para suas contas do Lote e do Armazenamento. Uma maneira simples de obter as credenciais necessárias e no Portal do Azure. (Você também pode obter essas credenciais usando as APIs do Azure ou as ferramentas de linha de comando.)

  1. Selecione Todos os serviços>Contas do Lote e, em seguida, selecione o nome de sua conta do Lote.

  2. Para ver as credenciais do Lote, selecione Chaves. Copie os valores da conta do Lote, URL, e Chave de acesso primária em um editor de texto.

  3. Para ver o nome e as chaves da conta de armazenamento, selecione Conta de armazenamento. Copie os valores de Nome da conta de Armazenamento e Chave1 em um editor de texto.

Baixar e executar o aplicativo de amostra

Baixar o aplicativo de exemplo

Baixe ou clone o aplicativo de exemplo do GitHub. Para clonar o repositório do aplicativo de exemplo com um cliente Git, use o seguinte comando:

git clone https://github.com/Azure-Samples/batch-dotnet-ffmpeg-tutorial.git

Navegue até o diretório que contém o arquivo de solução do Visual Studio BatchDotNetFfmpegTutorial.sln.

Além disso, verifique se a referência do pacote de aplicativos do ffmpeg na solução corresponde ao identificador e à versão do pacote do ffmpeg que você carregou na sua conta do Lote. Por exemplo, ffmpeg e 4.3.1.

const string appPackageId = "ffmpeg";
const string appPackageVersion = "4.3.1";

Criar e executar o projeto de exemplo

Compile e execute o aplicativo no Visual Studio ou na linha de comando com os comandos dotnet build e dotnet run. Depois de executar o aplicativo, examine o código para saber o que cada parte do aplicativo faz. Por exemplo, no Visual Studio:

  1. Clique com o botão direito do mouse na solução no Gerenciador de Soluções e selecione Compilar Solução.

  2. Confirme a restauração de qualquer pacote NuGet, se solicitado. Se você precisar baixar pacotes ausentes, verifique se o Gerenciador de Pacotes do NuGet está instalado.

  3. Execute a solução. Quando você executa o aplicativo de exemplo, a saída do console fica mais ou menos assim. Durante a execução, você tem uma pausa em Monitoring all tasks for 'Completed' state, timeout in 00:30:00... enquanto os nós de computação do pool são iniciados.

Sample start: 11/19/2018 3:20:21 PM

Container [input] created.
Container [output] created.
Uploading file LowPriVMs-1.mp4 to container [input]...
Uploading file LowPriVMs-2.mp4 to container [input]...
Uploading file LowPriVMs-3.mp4 to container [input]...
Uploading file LowPriVMs-4.mp4 to container [input]...
Uploading file LowPriVMs-5.mp4 to container [input]...
Creating pool [WinFFmpegPool]...
Creating job [WinFFmpegJob]...
Adding 5 tasks to job [WinFFmpegJob]...
Monitoring all tasks for 'Completed' state, timeout in 00:30:00...
Success! All tasks completed successfully within the specified timeout period.
Deleting container [input]...

Sample end: 11/19/2018 3:29:36 PM
Elapsed time: 00:09:14.3418742

Vá para sua conta do Lote no portal do Azure para monitorar o pool, os nós de computação, os trabalhos e as tarefas. Por exemplo, para ver um mapa de calor dos nós de computação em seu pool, clique em Pools>WinFFmpegPool.

Quando as tarefas são executadas, o mapa de calor fica mais ou menos assim:

Captura de tela do mapa de calor do pool no portal do Azure.

O tempo de execução típico é de aproximadamente 10 minutos ao executar o aplicativo em sua configuração padrão. A criação de pool leva mais tempo.

Recuperar arquivos de saída

Você pode usar o Portal do Azure para baixar os arquivos MP3 de saída gerados pelas tarefas de ffmpeg.

  1. Clique em Todos os serviços>Contas de armazenamento e, depois, clique no nome da sua conta de armazenamento.
  2. Clique em Blobs>saída.
  3. Clique com o botão direito em um dos arquivos MP3 de saída e, em seguida, clique em Baixar. Siga as instruções em seu navegador para abrir ou salvar o arquivo.

Baixar o arquivo de saída

Embora não seja mostrado neste exemplo, você também pode baixar os arquivos de forma programática a partir dos nós de computação ou do contêiner de armazenamento.

Examine o código

As seções a seguir separa o aplicativo de exemplo nas etapas executadas para processar uma carga de trabalho no serviço Lote. Consulte o arquivo Program.cs na solução enquanto você lê o restante deste artigo, pois nem todas as linhas de código na amostra são discutidas.

Autenticar clientes de Blob e do Lote

Para interagir com a conta de armazenamento vinculada, o aplicativo usa a Biblioteca Azure.Storage.Blobs para .NET. Usando a classe BlobServiceClient que faz referência ao Uri da conta e autenticando o Token, como DefaultAzureCredential.

// TODO: Replace <storage-account-name> with your actual storage account name
Uri accountUri = new Uri("https://<storage-account-name>.blob.core.windows.net/");
BlobServiceClient blobClient = new BlobServiceClient(accountUri, new DefaultAzureCredential());

O aplicativo cria uma referência ao BatchAccountResource pelo ArmClient do Gerenciador de Recursos para criar o pool no serviço de Lote. O cliente Arm no exemplo usa a autenticação DefaultAzureCredential.

ArmClient _armClient = new ArmClient(new DefaultAzureCredential());
var batchAccountIdentifier = ResourceIdentifier.Parse(BatchAccountResourceID);
BatchAccountResource batchAccount = await _armClient.GetBatchAccountResource(batchAccountIdentifier).GetAsync();

O aplicativo cria um objeto BatchClient para criar e trabalhos e tarefas no serviço de Lote. O cliente do Lote no exemplo usa a autenticação DefaultAzureCredential.

// TODO: Replace <batch-account-name> with your actual storage account name
Uri batchUri = new Uri("https://<batch-account-name>t.eastus.batch.azure.com");
BatchClient _batchClient = new BatchClient(batchUri, new DefaultAzureCredential());

Carregar arquivos de entrada

A aplicativo passa o objeto blobServerClient para o método CreateContainerIfNotExistc a fim de criar um contêiner de armazenamento para os arquivos de entrada (formato MP4) e um contêiner para a saída da tarefa.

CreateContainerIfNotExist(blobClient, inputContainerName);
CreateContainerIfNotExist(blobClient, outputContainerName);

Em seguida, os arquivos serão enviados para o contêiner de entrada da pasta InputFiles local. Os arquivos no armazenamento são definidos como objetos ResourceFile do Lote que ele pode baixar mais tarde para os nós de computação.

Dois métodos em Program.cs estão envolvidos no upload dos arquivos:

  • UploadFilesToContainerAsync: Retorna uma coleção de objetos ResourceFile e chama internamente UploadResourceFileToContainerAsync para carregar cada arquivo passado no parâmetro inputFilePaths.
  • UploadResourceFileToContainerAsync: Carrega cada arquivo como um blob no contêiner de entrada. Depois de carregar o arquivo, ele obtém uma assinatura de acesso compartilhado (SAS) para o blob e retorna um objeto ResourceFile para representá-lo.
string inputPath = Path.Combine(Environment.CurrentDirectory, "InputFiles");

List<string> inputFilePaths = new List<string>(Directory.GetFileSystemEntries(inputPath, "*.mp4",
    SearchOption.TopDirectoryOnly));

List<ResourceFile> inputFiles = await UploadFilesToContainerAsync(
  blobClient,
  inputContainerName,
  inputFilePaths);

Para obter detalhes de como carregar arquivos como blobs em uma conta de armazenamento com o .NET, confira Carregar, baixar e listar blobs usando o .NET.

Criar um pool de nós de computação

Em seguida, o exemplo cria um pool de nós de computação na conta do Lote com uma chamada para CreatePoolIfNotExistAsync. Esse método definido usa o método BatchAccountResource.GetBatchAccountPools(). CreateOrUpdateAsync para definir o número de nós, o tamanho da VM e uma configuração do pool. Aqui, um objeto BatchVmConfiguration especifica um BatchImageReference em uma imagem do Windows Server publicada no Azure Marketplace. O Lote dá suporte a uma ampla gama de imagens de VM no Azure Marketplace, bem como imagens de VM personalizadas.

O número de nós e o tamanho da VM são definidos usando constantes definidas. O Lote dá suporte a nós dedicados e nós spot, e você pode usar um ou ambos em seus pools. Nós dedicados são reservados para o pool. Nós spot são oferecidos a um preço menor do excedente de capacidade da VM no Azure. Nós spot ficam indisponíveis quando o Azure não tem capacidade suficiente. O exemplo, por padrão, cria um pool que contém apenas cinco Nós spot em tamanho Standard_A1_v2.

Observação

Confira as cotas do seu nó. Consulte Cotas e limites de serviço do Lote para obter instruções sobre como criar uma solicitação de cota.

O aplicativo ffmpeg for implantado para os nós de computação adicionando um ApplicationPackageReference à configuração do pool.

var credential = new DefaultAzureCredential();
ArmClient _armClient = new ArmClient(credential);

var batchAccountIdentifier = ResourceIdentifier.Parse(BatchAccountResourceID);
BatchAccountResource batchAccount = await _armClient.GetBatchAccountResource(batchAccountIdentifier).GetAsync();

BatchAccountPoolCollection collection = batchAccount.GetBatchAccountPools();
if (collection.Exists(poolId) == false)
{
    var poolName = poolId;
    var imageReference = new BatchImageReference()
    {
        Publisher = "MicrosoftWindowsServer",
        Offer = "WindowsServer",
        Sku = "2019-datacenter-smalldisk",
        Version = "latest"
    };
    string nodeAgentSku = "batch.node.windows amd64";


    ArmOperation<BatchAccountPoolResource> armOperation = await batchAccount.GetBatchAccountPools().CreateOrUpdateAsync(
        WaitUntil.Completed, poolName, new BatchAccountPoolData()
        {
            VmSize = "Standard_DS1_v2",
            DeploymentConfiguration = new BatchDeploymentConfiguration()
            {
                VmConfiguration = new BatchVmConfiguration(imageReference, nodeAgentSku)
            },
            ScaleSettings = new BatchAccountPoolScaleSettings()
            {
                FixedScale = new BatchAccountFixedScaleSettings()
                {
                    TargetDedicatedNodes = DedicatedNodeCount,
                    TargetLowPriorityNodes = LowPriorityNodeCount
                }
            },
            Identity = new ManagedServiceIdentity(ManagedServiceIdentityType.UserAssigned)
            {
                UserAssignedIdentities =
                {
                        [new ResourceIdentifier(ManagedIdentityId)] = new Azure.ResourceManager.Models.UserAssignedIdentity(),
                },
            },
            ApplicationPackages =
            {
                    new Azure.ResourceManager.Batch.Models.BatchApplicationPackageReference(new ResourceIdentifier(appPacakgeResourceID))
                    {
                        Version = appPackageVersion,
                    }
            },

        });
    BatchAccountPoolResource pool = armOperation.Value;

Criar um trabalho

Um trabalho do Lote especifica um pool onde executar tarefas, e configurações opcionais, como uma prioridade e uma agenda para o trabalho. O exemplo cria um trabalho com uma chamada para CreateJobAsync. Esse método definido usa o método BatchClient.CreateJobAsync para criar um trabalho no seu pool.

 BatchJobCreateContent batchJobCreateContent = new BatchJobCreateContent(jobId, new BatchPoolInfo { PoolId = poolId });
 await batchClient.CreateJobAsync(batchJobCreateContent);

Criar tarefas

O exemplo cria tarefas no trabalho com uma chamada para o método AddTasksAsync, que cria uma lista de objetos BatchTask. Cada BatchTask executa ffmpeg para processar um objeto ResourceFile de entrada usando uma propriedade CommandLine. O ffmpeg anteriormente foi instalado em cada nó quando o pool foi criado. Aqui, a linha de comando executa ffmpeg para converter cada arquivo MP4 (vídeo) de entrada em um arquivo MP3 (áudio).

O exemplo cria um objeto OutputFile para o arquivo MP3 depois de executar a linha de comando. Os arquivos de saída de cada tarefa (um, neste caso) são carregados em um contêiner na conta de armazenamento vinculada, usando a propriedade OutputFiles da tarefa. Observe as condições definidas no objeto outputFile. Um arquivo de saída de uma tarefa é carregado no contêiner somente depois que a tarefa é concluída com êxito (OutputFileUploadCondition.TaskSuccess). Confira o exemplo de código completo no GitHub para obter mais detalhes de implementação.

Em seguida, o exemplo adiciona tarefas ao trabalho com o método CreateTaskAsync , que as enfileira para serem executadas nos nós de computação.

Substitua o caminho do arquivo do executável pelo nome da versão baixada. Este código de exemplo usa o ffmpeg-4.3.1-2020-11-08-full_build de exemplo.

// Create a collection to hold the tasks added to the job:
List<BatchTaskCreateContent> tasks = new List<BatchTaskCreateContent>();

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

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

    // Create a batch task (with the task ID and command line) and add it to the task list

    BatchTaskCreateContent batchTaskCreateContent = new BatchTaskCreateContent(taskId, taskCommandLine);
    batchTaskCreateContent.ResourceFiles.Add(inputFiles[i]);

    // Task output file will be uploaded to the output container in Storage.
    // TODO: Replace <storage-account-name> with your actual storage account name
    OutputFileBlobContainerDestination outputContainer = new OutputFileBlobContainerDestination("https://<storage-account-name>.blob.core.windows.net/output/" + outputMediaFile)
    {
        IdentityReference = inputFiles[i].IdentityReference,
    };

    OutputFile outputFile = new OutputFile(outputMediaFile,
                                           new OutputFileDestination() { Container = outputContainer },
                                           new OutputFileUploadConfig(OutputFileUploadCondition.TaskSuccess));
    batchTaskCreateContent.OutputFiles.Add(outputFile);

    tasks.Add(batchTaskCreateContent);
}

// Call BatchClient.CreateTaskCollectionAsync() to add the tasks as a collection rather than making a
// separate call for each. Bulk task submission helps to ensure efficient underlying API
// calls to the Batch service. 

await batchClient.CreateTaskCollectionAsync(jobId, new BatchTaskGroup(tasks));

Limpar os recursos

Depois que ele executa as tarefas, o aplicativo exclui automaticamente o contêiner de armazenamento de entrada criado e oferece a opção de excluir o pool do Lote e o trabalho. O BatchClient tem um método para excluir um trabalho DeleteJobAsync e excluir um pool DeletePoolAsync, que são chamados se você confirmar a exclusão. Embora você não seja cobrado pelos trabalhos e pelas tarefas, será cobrado pelos nós de computação. Portanto, recomendamos que você aloque os pools conforme a necessidade. Quando você excluir o pool, todas as saídas de tarefa nos nós são excluídas. No entanto, os arquivos de saída permanecerão na conta de armazenamento.

Quando não forem mais necessário, exclua o grupo de recursos, a conta do Lote e a conta de armazenamento. Para fazer isso no Portal do Azure, selecione o grupo de recursos para a conta do Lote e clique em Excluir grupo de recursos.

Próximas etapas

Neste tutorial, você aprendeu a:

  • Adicionar um pacote de aplicativos à sua conta do Batch.
  • Autenticar com contas do Lote e de Armazenamento.
  • Carregar arquivos de entrada para o Armazenamento.
  • Criar um pool de nós de computação para executar um aplicativo.
  • Criar um trabalho e tarefas para processar os arquivos de entrada.
  • Monitorar a execução da tarefa.
  • Recuperar arquivos de saída.

Para obter mais exemplos de uso da API do.NET para agendar e processar cargas de trabalho em lote, consulte as amostras do C# do Lote no GitHub.