Manter os dados de tarefa para o Armazenamento do Azure com a API de serviço de lote

Uma tarefa em execução em lote do Azure pode produzir dados de saída quando ele é executado. Com frequência, os dados de saída precisam ser armazenados para a recuperação por outras tarefas no trabalho, o aplicativo cliente que executou o trabalho, ou ambos. As tarefas gravam dados de saída no sistema de arquivos de um nó de computação do Lote, mas todos os dados no nó serão perdidos quando ele for reimaginado ou quando o nó deixar o pool. As tarefas também podem ter um período de retenção de arquivo, após o qual os arquivos criados pela tarefa são excluídos. Por esses motivos, é importante manter a saída da tarefa que você vai precisar posteriormente para um repositório de dados, como o Armazenamento do Azure.

Para opções de conta de armazenamento do Lote, confira Contas do Lote e contas do Armazenamento do Azure.

A API de serviço de Lote dá suporte a dados de saída persistentes para o Armazenamento do Azure em tarefas e tarefas do gerenciador de trabalho que são executadas em pools com Configuração de Máquina Virtual. Quando você adiciona uma tarefa, pode especificar um contêiner no Armazenamento do Azure como o destino para a saída da tarefa. O serviço de lote grava os dados de saída nesse contêiner quando a tarefa é concluída.

Ao usar a API do serviço do Lote para manter a saída da tarefa, não é necessário modificar o aplicativo em que a tarefa está sendo executada. Em vez disso, com algumas modificações no seu aplicativo cliente, você pode manter a saída da tarefa de dentro do mesmo código que cria a tarefa.

Importante

A persistência de dados de tarefa para o Armazenamento do Microsoft Azure usando a API do serviço de Lote não funciona com pools criados antes de 1º de fevereiro de 2018.

Quando uso a API do serviço de Lote para manter a saída da tarefa?

O Lote do Azure fornece mais de uma maneira de manter a saída da tarefa. Usar a API de serviço de lote é uma abordagem conveniente que é mais adequada para esses cenários:

  • Você deseja escrever o código para manter a saída da tarefa de dentro de seu aplicativo cliente, sem modificar o aplicativo que sua tarefa está executando.
  • Você deseja manter a saída das tarefas em Lote e as tarefas do gerenciador de trabalho em pools criados com a configuração de máquina virtual.
  • Você deseja manter a saída a um contêiner de Armazenamento do Azure com um nome arbitrário.
  • Você deseja manter a saída a um contêiner de Armazenamento do Azure nomeado de acordo com o padrão de Convenções de Arquivo em Lotes.

Se seu cenário for diferente daqueles listados acima, poderá ser necessário considerar uma abordagem diferente. Por exemplo, a API de serviço de lote atualmente não suporta saída de transmissão para o armazenamento do Azure durante a execução da tarefa. Para a saída de fluxo, considere usar a biblioteca de Convenções de Arquivo em Lotes, disponível para .NET. Para outros idiomas, você precisará implantar sua própria solução. Para obter mais informações sobre outras opções, confira Manter trabalho e saída de tarefa no Armazenamento do Azure.

Criar um contêiner no Armazenamento do Azure

Para manter a saída da tarefa no Armazenamento do Azure, você precisará criar um contêiner que serve como o destino para seus arquivos de saída. Crie o contêiner antes de executar sua tarefa, de preferência antes de enviar seu trabalho, usando a biblioteca de cliente de Armazenamento do Azure apropriada ou o SDK. Para obter mais informações sobre APIs de Armazenamento do Azure, consulte a documentação do Armazenamento do Azure.

Por exemplo, se você estiver escrevendo seu aplicativo em C#, use a biblioteca de cliente de Armazenamento do Azure para .NET. O exemplo a seguir mostra como criar um contêiner:

CloudBlobContainer container = storageAccount.CreateCloudBlobClient().GetContainerReference(containerName);
await container.CreateIfNotExists();

Especificar arquivos de saída para a saída da tarefa

Para especificar os arquivos de saída para uma tarefa, crie uma coleção de objetos OutputFile e a atribua para a propriedade de CloudTask.OutputFiles ao criar a tarefa. Você pode usar uma SAS (Assinatura de Acesso Compartilhado) ou uma identidade gerenciada para autenticar o acesso ao contêiner.

Uso de uma assinatura de acesso compartilhado

Depois de criar o contêiner, obtenha uma assinatura de acesso compartilhado (SAS) com acesso de gravação ao contêiner. Uma SAS fornece acesso delegado para o contêiner. A SAS concede acesso com um conjunto especificado de permissões e em um intervalo de tempo especificado. O serviço de Lote precisa de uma SAS com permissões de gravação para gravar a saída da tarefa no contêiner. Para saber mais sobre SAS, confira Uso de SAS (assinaturas de acesso compartilhado) no Armazenamento do Azure.

Quando você receber uma SAS usando as APIs de Armazenamento do Azure, a API retorna uma cadeia de caracteres de token SAS. Essa cadeia de caracteres de token inclui todos os parâmetros de SAS, incluindo as permissões e o intervalo no qual a SAS é válida. Para usar a SAS para acessar um contêiner no Armazenamento do Azure, você precisa anexar a cadeia de caracteres de token SAS para o URI do recurso. O URI de recurso, junto com o token SAS anexado, fornece acesso autenticado ao Armazenamento do Azure.

O exemplo a seguir mostra como obter uma cadeia de token de SAS somente gravação para o contêiner e anexa a SAS para o URI do contêiner:

string containerSasToken = container.GetSharedAccessSignature(new SharedAccessBlobPolicy()
{
    SharedAccessExpiryTime = DateTimeOffset.UtcNow.AddDays(1),
    Permissions = SharedAccessBlobPermissions.Write
});

string containerSasUrl = container.Uri.AbsoluteUri + containerSasToken;

O exemplo de código C# a seguir cria uma tarefa que grava números aleatórios para um arquivo chamado output.txt. O exemplo cria um arquivo de saída para output.txt a ser gravado para o contêiner. O exemplo também cria arquivos de saída para os arquivos de log que correspondem ao padrão de arquivo std*.txt ( , por exemplo, , stdout.txt e stderr.txt). A URL do contêiner requer o SAS que foi criado anteriormente para o contêiner. O serviço de Lote usa as SAS para autenticar o acesso ao contêiner.

new CloudTask(taskId, "cmd /v:ON /c \"echo off && set && (FOR /L %i IN (1,1,100000) DO (ECHO !RANDOM!)) > output.txt\"")
{
    OutputFiles = new List<OutputFile>
    {
        new OutputFile(
            filePattern: @"..\std*.txt",
            destination: new OutputFileDestination(
         new OutputFileBlobContainerDestination(
                    containerUrl: containerSasUrl,
                    path: taskId)),
            uploadOptions: new OutputFileUploadOptions(
            uploadCondition: OutputFileUploadCondition.TaskCompletion)),
        new OutputFile(
            filePattern: @"output.txt",
            destination: 
         new OutputFileDestination(new OutputFileBlobContainerDestination(
                    containerUrl: containerSasUrl,
                    path: taskId + @"\output.txt")),
            uploadOptions: new OutputFileUploadOptions(
            uploadCondition: OutputFileUploadCondition.TaskCompletion)),
}

Observação

Se estiver usando este exemplo com o Linux, altere as barras invertidas (\) para barras comuns (/).

Usar a identidade gerenciada

Em vez de gerar e passar uma SAS com acesso de gravação ao contêiner para o Lote, uma identidade gerenciada pode ser usada para autenticar com o Armazenamento do Microsoft Azure. A identidade deve ser atribuída ao Pool do Lote e também ter a atribuição de função Storage Blob Data Contributor para o contêiner a ser gravado. O serviço do Lote pode então ser instruído a usar a identidade gerenciada em vez de uma SAS para autenticar o acesso ao contêiner.

CloudBlobContainer container = storageAccount.CreateCloudBlobClient().GetContainerReference(containerName);
await container.CreateIfNotExists();

new CloudTask(taskId, "cmd /v:ON /c \"echo off && set && (FOR /L %i IN (1,1,100000) DO (ECHO !RANDOM!)) > output.txt\"")
{
    OutputFiles = new List<OutputFile>
    {
        new OutputFile(
            filePattern: @"..\std*.txt",
            destination: new OutputFileDestination(
         new OutputFileBlobContainerDestination(
                    containerUrl: container.Uri,
                    path: taskId,
                    identityReference: new ComputeNodeIdentityReference() { ResourceId = "/subscriptions/SUB/resourceGroups/RG/providers/Microsoft.ManagedIdentity/userAssignedIdentities/identity-name"} })),
            uploadOptions: new OutputFileUploadOptions(
            uploadCondition: OutputFileUploadCondition.TaskCompletion))
    }
}

Especifique um padrão de arquivo para correspondência

Quando você especifica um arquivo de saída, você pode usar a propriedade OutputFile.FilePattern para especificar um padrão de arquivo para correspondência. O padrão de arquivo pode corresponder a zero arquivos, um único arquivo ou um conjunto de arquivos que são criados pela tarefa.

A propriedade FilePattern oferece suporte a caracteres curinga do sistema de arquivos padrão como * (para não-recursivo corresponde) e ** (para corresponde recursivo). Por exemplo, o exemplo de código acima especifica o padrão de arquivo para corresponder std*.txt não recursiva:

filePattern: @"..\std*.txt"

Para carregar um único arquivo, especifique um padrão de arquivo sem curingas. Por exemplo, o exemplo de código acima especifica o padrão de arquivo para corresponder output.txt:

filePattern: @"output.txt"

Especificar uma condição de upload

A propriedade Output​File​Upload​Options.UploadCondition permite o upload condicional de arquivos de saída. Um cenário comum é carregar um conjunto de arquivos se a tarefa for bem-sucedida, e um conjunto diferente de arquivos se ele falhar. Por exemplo, você talvez queira carregar arquivos de log detalhados somente quando a tarefa falhar e for encerrada com um código de saída diferente de zero. Da mesma forma, você talvez queira carregar arquivos de resultado somente se a tarefa for bem-sucedida, pois esses arquivos podem estar ausentes ou incompletos se a tarefa falhar.

O exemplo de código acima define a propriedade UploadCondition para TaskCompletion. Essa configuração especifica que o arquivo será carregado após a conclusão de tarefas, independentemente do valor do código de saída.

uploadCondition: OutputFileUploadCondition.TaskCompletion

Para outras configurações, confira a enumeração Output​File​Upload​Condition.

Desfazer a ambiguidade de arquivos com o mesmo nome

As tarefas em um trabalho podem produzir os arquivos que têm o mesmo nome. Por exemplo, stdout.txt e stderr.txt são criados para cada tarefa que executa em um trabalho. Como cada tarefa é executada em seu próprio contexto, esses arquivos não estão em conflito no sistema de arquivos do nó. No entanto, quando você carrega arquivos de várias tarefas para um contêiner compartilhado, você precisará desfazer a ambiguidade de arquivos com o mesmo nome.

A propriedade Output​File​Blob​Container​Destination.​Path especifica o blob de destino ou o diretório virtual dos arquivos de saída. Você pode usar a propriedade de caminho para nomear o blob ou diretório virtual de forma que os arquivos de saída com o mesmo nome são nomeados exclusivamente no Armazenamento do Azure. Usando a ID da tarefa no caminho é uma boa maneira de garantir nomes exclusivos e identificar facilmente os arquivos.

Se a propriedade FilePattern é definida como uma expressão curinga, em seguida, todos os arquivos que correspondem ao padrão são carregados para o diretório virtual especificado pela propriedade caminho. Por exemplo, se o contêiner for mycontainer, a tarefa de ID é mytask, e o padrão de arquivo é ..\std*.txt, em seguida, os URIs absolutos para arquivos de saída no Armazenamento do Azure serão semelhantes a:

https://myaccount.blob.core.windows.net/mycontainer/mytask/stderr.txt
https://myaccount.blob.core.windows.net/mycontainer/mytask/stdout.txt

Se a propriedade FilePattern está definida para corresponder a um nome de arquivo único, que significa que ele não contém caracteres curinga, o valor da propriedade caminho especifica o nome totalmente qualificado do blob. Se você antecipar conflitos de nomes com um único arquivo de várias tarefas, em seguida, inclua o nome do diretório virtual como parte do nome do arquivo para resolver a ambiguidade desses arquivos. Por exemplo, definir a propriedade caminho para incluir a ID da tarefa, o caractere delimitador (normalmente uma barra invertida) e o nome do arquivo:

path: taskId + @"/output.txt"

O URIs absolutos para arquivos de saída para um conjunto de tarefas será semelhante a:

https://myaccount.blob.core.windows.net/mycontainer/task1/output.txt
https://myaccount.blob.core.windows.net/mycontainer/task2/output.txt

Para obter mais informações sobre diretórios virtuais no Armazenamento do Azure, consulte Listar os blobs em um contêiner.

Vários arquivos de saída

Quando uma tarefa especifica vários arquivos de saída, você pode encontrar limites impostos pela API do Lote do Azure. É aconselhável manter suas tarefas pequenas e manter o número de arquivos de saída baixo.

Se você encontrar limites, considere reduzir o número de arquivos de saída empregando Padrões de Arquivo ou usando contêineres de arquivos, como tar ou zip, para consolidar os arquivos de saída. Como alternativa, utilize a montagem ou outras abordagens para persistir os dados de saída (consulte Persistir saída de tarefa e de trabalho).

Diagnosticar erros de carregamento de arquivo

Se o upload de arquivos de saída no Armazenamento do Azure falhar, a tarefa passará para o estado concluído e a propriedade Task​Execution​Information.​Failure​Information será definida. Analise a propriedade FailureInformation para determinar o erro. Por exemplo, aqui está um erro que ocorre no upload do arquivo se o contêiner não pode ser encontrado:

Category: UserError
Code: FileUploadContainerNotFound
Message: One of the specified Azure container(s) was not found while attempting to upload an output file

Em cada upload de arquivo, o lote grava dois arquivos de log para o nó de computação, fileuploadout.txt e fileuploaderr.txt. Você pode analisar esses arquivos de log para saber mais sobre uma falha específica. Nos casos em que o upload do arquivo nunca foi tentado, por exemplo, porque não foi possível executar a tarefa em si, esses arquivos de log não existirão.

Diagnosticar desempenho de carregamento de arquivo

O arquivo fileuploadout.txt registra em log o progresso de upload. Você pode analisar este arquivo para obter mais informações sobre o quanto os carregamentos de arquivo estão demorando. Tenha em mente que há muitos fatores que contribuem para o desempenho de upload, incluindo o tamanho do nó, outra atividade no nó no momento do upload, se o contêiner de destino está na mesma região que o pool de lote, quantos nós estão carregando a conta de armazenamento ao mesmo tempo, e assim por diante.

Usar a API de serviço de lote com o padrão de convenções de arquivo em lotes

Quando você mantém a saída da tarefa com a API de serviço de lote, você pode nomear o contêiner de destino e os blobs como desejar. Você também pode optar por nomeá-los de acordo com o padrão de Convenções de Arquivo em Lotes. O padrão de Convenções de Arquivo determina os nomes do contêiner de destino e do blob no Armazenamento do Azure para um determinado arquivo de saída com base nos nomes do trabalho e da tarefa. Se você usar o padrão de Convenções de Arquivo para nomear arquivos de saída, os arquivos de saída estarão disponíveis para exibição no portal do Azure.

Se você estiver desenvolvendo em C#, poderá usar os métodos incorporados à biblioteca Convenções de Arquivo em Lote do Azure para .NET. Essa biblioteca cria os contêineres corretamente nomeados e os caminhos de blob para você. Por exemplo, você pode chamar a API para obter o nome correto para o contêiner, com base no nome do trabalho:

string containerName = job.OutputStorageContainerName();

Você pode usar o método CloudJobExtensions.GetOutputStorageContainerUrl para retornar uma URL de assinatura de acesso compartilhado (SAS) que é usada para gravar no contêiner. Você pode passar esse SAS ao construtor Output​File​Blob​Container​Destination.

Se você estiver desenvolvendo com uma linguagem que não C#, precisará implantar o padrão de Convenções de Arquivo você mesmo.

Exemplo de código

O projeto de exemplo PersistOutputs é um dos exemplos de código do Lote do Azure no GitHub. Essa solução do Visual Studio demonstra como usar a biblioteca de cliente em lotes para .NET para manter a saída da tarefa para o armazenamento durável. Para executar o exemplo, siga estas etapas:

  1. Abra o projeto no Visual Studio 2019.
  2. Adicione suas credenciais de conta do Lote e do Armazenamento a AccountSettings.settings no projeto Microsoft.Azure.Batch.Samples.Common.
  3. Compile (mas não execute) a solução. Restaure todos os pacotes NuGet, se solicitado.
  4. Use o portal do Azure para carregar um pacote de aplicativos para PersistOutputsTask. Inclua o PersistOutputsTask.exe e seus assemblies dependentes no pacote .zip, defina a ID do aplicativo como "PersistOutputsTask" e a versão do pacote de aplicativos como "1.0".
  5. Inicie (execute) o projeto PersistOutputs.
  6. Quando solicitado a escolher a tecnologia de persistência a usar para executar o exemplo, digite 2 para executar o exemplo usando a API do serviço de lote para manter a saída da tarefa.
  7. Se desejado, execute o exemplo novamente, inserindo 3 para manter a saída com a API de serviço de lote e também para nomear o contêiner de destino e o caminho de blob de acordo com o padrão de Convenções de Arquivo.

Próximas etapas