Persistir dados de tarefas no Armazenamento do Azure com a API de serviço em lote

Uma tarefa em execução no Lote do Azure pode produzir dados de saída quando é executada. Os dados de saída da tarefa geralmente precisam ser armazenados para recuperação por outras tarefas no trabalho, no aplicativo cliente que executou o trabalho ou em ambos. As tarefas gravam dados de saída no sistema de arquivos de um nó de computação em lote, mas todos os dados no nó são perdidos quando ele é recriado ou quando o nó sai do pool. As tarefas também podem ter um período de retenção de arquivos, após o qual os arquivos criados pela tarefa são excluídos. Por esses motivos, é importante persistir a saída de tarefas que você precisará mais tarde para um armazenamento de dados, como o Armazenamento do Azure.

Para obter opções de conta de armazenamento em lote, consulte Contas em lote e Contas de armazenamento do Azure.

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

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

Importante

A persistência de dados de tarefas no Armazenamento do Azure com a API de serviço em lote não funciona com pools criados antes de 1º de fevereiro de 2018.

Quando devo usar a API de serviço em lote para persistir a saída da tarefa?

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

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

Se o seu cenário for diferente dos listados acima, talvez seja necessário considerar uma abordagem diferente. Por exemplo, a API do serviço de lote atualmente não oferece suporte à saída de streaming para o Armazenamento do Azure enquanto a tarefa está em execução. Para transmitir a saída, considere usar a biblioteca Batch File Conventions, disponível para .NET. Para outros idiomas, você precisará implementar sua própria solução. Para obter mais informações sobre outras opções, consulte Persistir saída de trabalho e tarefa para o Armazenamento do Azure.

Criar um contêiner no Armazenamento do Azure

Para persistir a saída da tarefa no Armazenamento do Azure, você precisará criar um contêiner que sirva como 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 ou SDK de Armazenamento do Azure apropriado. 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 do 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 saída de tarefas

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

Usando 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 ao contêiner. O SAS concede acesso com um conjunto especificado de permissões e em um intervalo de tempo especificado. O serviço Batch precisa de uma SAS com permissões de gravação para gravar a saída da tarefa no contêiner. Para obter mais informações sobre SAS, consulte Usando assinaturas de acesso compartilhado (SAS) no Armazenamento do Azure.

Quando você obtém 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 da SAS, incluindo as permissões e o intervalo durante o qual a SAS é válida. Para usar a SAS para acessar um contêiner no Armazenamento do Azure, você precisa acrescentar a cadeia de caracteres do token SAS ao URI do recurso. O URI do recurso, juntamente com o token SAS anexado, fornece acesso autenticado ao Armazenamento do Azure.

O exemplo a seguir mostra como obter uma cadeia de caracteres de token SAS somente gravação para o contêiner e, em seguida, acrescenta o SAS ao 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 em um arquivo chamado output.txt. O exemplo cria um arquivo de saída para output.txt ser gravado no contêiner. O exemplo também cria arquivos de saída para quaisquer arquivos de log que correspondam ao padrão std*.txt de arquivo (por exemplo, stdout.txt e stderr.txt). A URL do contêiner requer a SAS que foi criada anteriormente para o contêiner. O serviço de lote usa a 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)),
}

Nota

Se estiver usando este exemplo com o Linux, certifique-se de alterar as barras invertidas para a frente.

Usando a identidade gerenciada

Em vez de gerar e passar uma SAS com acesso de gravação ao contêiner para o Batch, uma identidade gerenciada pode ser usada para autenticar com o Armazenamento do Azure. A identidade deve ser atribuída ao Pool de Lotes e também ter a atribuição de função para o contêiner a Storage Blob Data Contributor ser gravado. O serviço Batch 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))
    }
}

Especificar um padrão de arquivo para correspondência

Ao especificar 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 criados pela tarefa.

A propriedade FilePattern oferece suporte a curingas padrão do sistema de arquivos, como * (para correspondências não recursivas) e ** (para correspondências recursivas). Por exemplo, o exemplo de código acima especifica o padrão de arquivo para corresponder std*.txt não recursivamente:

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 carregamento

A propriedade OutputFileUploadOptions.UploadCondition permite o carregamento 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 falhar. Por exemplo, você pode querer carregar arquivos de log detalhados somente quando a tarefa falhar e sair com um código de saída diferente de zero. Da mesma forma, você pode querer carregar arquivos de resultados 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 como TaskCompletion. Essa configuração especifica que o arquivo deve ser carregado após a conclusão das tarefas, independentemente do valor do código de saída.

uploadCondition: OutputFileUploadCondition.TaskCompletion

Para outras configurações, consulte o enum OutputFileUploadCondition .

Desambiguar ficheiros com o mesmo nome

As tarefas em um trabalho podem produzir arquivos com o mesmo nome. Por exemplo, stdout.txt e stderr.txt são criados para cada tarefa executada em um trabalho. Como cada tarefa é executada em seu próprio contexto, esses arquivos não entram em conflito no sistema de arquivos do nó. No entanto, ao carregar arquivos de várias tarefas para um contêiner compartilhado, você precisará desambiguar arquivos com o mesmo nome.

O OutputFileBlobContainerDestination.A propriedade Path especifica o blob de destino ou o diretório virtual para arquivos de saída. Você pode usar a propriedade Path para nomear o blob ou o diretório virtual de tal forma que os arquivos de saída com o mesmo nome sejam nomeados exclusivamente no Armazenamento do Azure. Usar o ID da tarefa no caminho é uma boa maneira de garantir nomes exclusivos e identificar facilmente os arquivos.

Se a propriedade FilePattern estiver definida como uma expressão curinga , todos os arquivos que correspondem ao padrão serão carregados no diretório virtual especificado pela propriedade Path . Por exemplo, se o contêiner for , a ID da tarefa for , e o padrão de arquivo for mycontainermytask..\std*.txt, os URIs absolutos para os 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 estiver definida para corresponder a um único nome de arquivo, o que significa que não contém caracteres curinga, o valor da propriedade Path especifica o nome de blob totalmente qualificado. Se você antecipar conflitos de nomenclatura com um único arquivo de várias tarefas, inclua o nome do diretório virtual como parte do nome do arquivo para desambiguar esses arquivos. Por exemplo, defina a propriedade Path para incluir a ID da tarefa, o caractere delimitador (normalmente uma barra para frente) e o nome do arquivo:

path: taskId + @"/output.txt"

Os URIs absolutos para os arquivos de saída para um conjunto de tarefas serão semelhantes 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.

Muitos 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 a saída de tarefas e tarefas).

Diagnosticar erros de carregamento de ficheiros

Se o carregamento de arquivos de saída no Armazenamento do Azure falhar, a tarefa será movida para o estado Concluído e para TaskExecutionInformation .A propriedade FailureInformation está definida. Examine a propriedade FailureInformation para determinar qual erro ocorreu. Por exemplo, aqui está um erro que ocorre no carregamento do arquivo se o contêiner não puder 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 carregamento de arquivo, fileuploadout.txt o Batch grava dois arquivos de log no nó de computação e fileuploaderr.txt. Você pode examinar 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 a tarefa em si não pôde ser executada, esses arquivos de log não existirão.

Diagnosticar o desempenho do carregamento de arquivos

O arquivo registra o fileuploadout.txt progresso do upload. Pode examinar este ficheiro para saber mais sobre a duração dos carregamentos de ficheiros. Lembre-se de que há muitos fatores que contribuem para o desempenho do carregamento, incluindo o tamanho do nó, outra atividade no nó no momento do upload, se o contêiner de destino está na mesma região do pool de lotes, quantos nós estão sendo carregados para a conta de armazenamento ao mesmo tempo e assim por diante.

Usar a API de serviço em lote com o padrão Batch File Conventions

Ao persistir a saída da tarefa com a API do serviço em lote, você pode nomear o contêiner de destino e os blobs como quiser. Você também pode optar por nomeá-los de acordo com o padrão Batch File Conventions. O padrão Convenções de Arquivo determina os nomes do contêiner e blob de destino 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 Convenções de Arquivo para nomear arquivos de saída, seus 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 na biblioteca de convenções de arquivo em lote para .NET. Esta biblioteca cria os contêineres e caminhos de blob nomeados corretamente para você. Por exemplo, você pode chamar a API para obter o nome correto para o contêiner, com base no nome da tarefa:

string containerName = job.OutputStorageContainerName();

Você pode usar o método CloudJobExtensions.GetOutputStorageContainerUrl para retornar uma URL de assinatura de acesso compartilhado (SAS) usada para gravar no contêiner. Em seguida, você pode passar essa SAS para o construtor OutputFileBlobContainerDestination .

Se você estiver desenvolvendo em uma linguagem diferente de C#, precisará implementar o padrão File Conventions por conta própria.

Exemplo de código

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

  1. Abra o projeto no Visual Studio 2019.
  2. Adicione suas credenciais de conta de Lote e Armazenamento a AccountSettings.settings no projeto Microsoft.Azure.Batch.Samples.Common.
  3. Crie (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 aplicativo para PersistOutputsTask. Inclua os PersistOutputsTask.exe assemblies e seus dependentes no pacote .zip, defina o ID do aplicativo como "PersistOutputsTask" e a versão do pacote do aplicativo como "1.0".
  5. Inicie (execute) o projeto PersistOutputs .
  6. Quando solicitado a escolher a tecnologia de persistência a ser usada para executar o exemplo, digite 2 para executar o exemplo usando a API de serviço em lote para persistir a saída da tarefa.
  7. Se desejar, execute o exemplo novamente, inserindo 3 para persistir a saída com a API de serviço em lote e também para nomear o contêiner de destino e o caminho de blob de acordo com o padrão File Conventions.

Próximos passos