Trabalhos de contêiner em pipelines YAML

Azure DevOps Services | Azure DevOps Server 2022 - Azure DevOps Server 2019

Este artigo explica os trabalhos de contêiner no Azure Pipelines.

Por padrão, os trabalhos do Azure Pipelines são executados diretamente nos computadores host em que o agente está instalado. Os trabalhos de agente hospedado são convenientes, exigem pouca configuração inicial e infraestrutura para manutenção e são adequados para projetos básicos.

Se você quiser mais controle sobre o contexto da tarefa, poderá definir e executar trabalhos em contêineres. Os contêineres são uma abstração leve sobre o sistema operacional do host que fornece isolamento do host. Quando executa trabalhos em contêineres, você pode selecionar as versões exatas de sistemas operacionais, de ferramentas e de dependências que o build requer.

Os agentes do Linux e do Windows podem executar trabalhos de pipeline diretamente no host ou em contêineres. Os trabalhos de contêiner não estão disponíveis no macOS.

Para um trabalho de contêiner, o agente primeiro busca e inicia o contêiner. Em seguida, cada etapa do trabalho é executada dentro do contêiner.

Se você precisar de controle refinado no nível da etapa do build individual, os destinos das etapas permitirão que você escolha contêiner ou host de cada etapa.

Pré-requisitos

  • Use um pipeline YAML. Os pipelines clássicos não dão suporte a trabalhos de contêiner.
  • Use um agente hospedado do Windows ou do Ubuntu. Somente os agentes windows-* e ubuntu-* dão suporte à execução de contêineres. Os agentes macos-* não dão suporte à execução de contêineres.
  • Seu agente está configurado para trabalhos de contêiner.
    • Os agentes do Windows e do Linux devem ter o Docker instalado e precisam de permissão para acessar o daemon do Docker.
    • Não há suporte para contêineres quando o agente já está em execução dentro de um contêiner. Você não pode ter contêineres aninhados.

Requisitos adicionais de contêiner

Os contêineres baseados em Linux têm os requisitos a seguir. Para soluções alternativas, consulte Contêineres baseados em Nonglibc.

  • Bash instalado
  • Baseado na Biblioteca GNU C (glibc)
  • Nenhum ENTRYPOINT
  • Forneça USER com acesso a groupadd e a outros comandos privilegiados sem usar sudo
  • Pode executar o Node.js, que o agente fornece

    Observação

    O Node.js deve ser pré-instalado para contêineres do Linux em hosts do Windows.

Alguns dos contêineres despojados disponíveis no Docker Hub, especialmente os contêineres baseados no Alpine Linux, não atendem a esses requisitos. Os contêineres com um ENTRYPOINT podem não funcionar porque docker create e docker exec do Azure Pipelines esperam que o contêiner esteja sempre em execução.

Exemplos de trabalho único

Os exemplos a seguir definem um contêiner do Windows ou do Linux para um único trabalho.

O exemplo simples a seguir define um contêiner do Linux:

pool:
  vmImage: 'ubuntu-latest'

container: ubuntu:18.04

steps:
- script: printenv

O exemplo a seguir informa ao sistema para buscar a ubuntu imagem marcada 18.04 do Docker Hub e, em seguida, iniciar o contêiner. O comando printenv é executado dentro do contêiner ubuntu:18.04.

Vários trabalhos

Você pode usar contêineres para executar a mesma etapa em vários trabalhos. O exemplo a seguir executa a mesma etapa em diversas versões do Ubuntu Linux. Você não precisa mencionar a palavra-chave jobs porque apenas um único trabalho é definido.

pool:
  vmImage: 'ubuntu-latest'

strategy:
  matrix:
    ubuntu16:
      containerImage: ubuntu:16.04
    ubuntu18:
      containerImage: ubuntu:18.04
    ubuntu20:
      containerImage: ubuntu:20.04

container: $[ variables['containerImage'] ]

steps:
- script: printenv

Vários trabalhos com pools de agentes em um único host e agente

Um trabalho de contêiner usa o arquivo de configuração do Docker do agente de host subjacente para autorização do registro de imagem. Esse arquivo é desconectado no final da inicialização do contêiner de registro do Docker. As extrações de imagem do Registro para trabalhos de contêiner subsequentes podem ser negadas para unauthorized authentication porque outro trabalho em execução em paralelo já desconectou o arquivo de configuração do Docker.

A solução é definir uma variável de ambiente do Docker DOCKER_CONFIG específica para cada pool de agentes em execução no agente hospedado. Exporte o DOCKER_CONFIG no script runsvc.sh de cada pool de agentes:

export DOCKER_CONFIG=./.docker

Opções de inicialização

Você pode especificar options para controlar a inicialização do contêiner, como no exemplo a seguir:

container:
  image: ubuntu:18.04
  options: --hostname container-test --ip 192.168.0.1

steps:
- script: echo hello

A execução de docker create --help fornece a lista de opções que você pode passar para a chamada do Docker. Nem todas essas opções têm garantia de funcionar com o Azure DevOps. Verifique primeiro se você pode usar uma propriedade container para atingir a mesma meta.

Para obter mais informações, consulte a referência de comando docker create e a definição resources.containers.container na referência de esquema YAML do Azure DevOps.

Definição de contêiner reutilizável

O exemplo a seguir define os contêineres na seção resources e, em seguida, faz referência a eles por seus aliases atribuídos. A palavra-chave jobs está explicitamente listada para maior clareza.

resources:
  containers:
  - container: u16
    image: ubuntu:16.04

  - container: u18
    image: ubuntu:18.04

  - container: u20
    image: ubuntu:20.04

jobs:
- job: RunInContainer
  pool:
    vmImage: 'ubuntu-latest'

  strategy:
    matrix:
      ubuntu16:
        containerResource: u16
      ubuntu18:
        containerResource: u18
      ubuntu20:
        containerResource: u20

  container: $[ variables['containerResource'] ]

  steps:
  - script: printenv

Pontos de extremidade de serviço

Você pode hospedar contêineres em registros diferentes do Docker Hub público. Para hospedar uma imagem no Registro de Contêiner do Azure ou em outro registro de contêiner privado (incluindo um registro privado do Docker Hub), adicione uma conexão de serviço para acessar o registro. Em seguida, você poderá fazer referência ao ponto de extremidade na definição do contêiner.

Conexão privada do Docker Hub:

container:
  image: registry:ubuntu1804
  endpoint: private_dockerhub_connection

Conexão do Registro de Contêiner do Azure:

container:
  image: myprivate.azurecr.io/windowsservercore:1803
  endpoint: my_acr_connection

Observação

O Azure Pipelines não pode configurar uma conexão de serviço para o Amazon Elastic Container Registry (ECR), pois o Amazon ECR requer outras ferramentas de cliente para converter credenciais da AWS em algo que o Docker possa usar para autenticar.

Contêineres não baseados em glibc

O agente do Azure Pipelines fornece uma cópia do Node.js, que é necessária para executar tarefas e scripts. Descubra a versão da Node.js de um agente hospedado, consulte Agentes hospedados pela Microsoft.

A versão do Node.js é compilada no runtime C usado na nuvem hospedada, normalmente o glibc. Algumas variantes do Linux usam outros runtimes C. Por exemplo, o Alpine Linux usa musl.

Se você quiser usar um contêiner não baseado em glibc, será necessário:

  • Forneça sua própria cópia do Node.js.
  • Adicione um rótulo à sua imagem informando ao agente onde encontrar o binário do Node.js.
  • Forneça outras dependências das quais o Azure Pipelines depende: bash, sudo, which e groupadd.

Forneça seu próprio Node.js

Se usar um contêiner não baseado em glibc, você será responsável por adicionar um binário do Node ao contêiner. O Node.js 18 é uma opção segura. Comece pela imagem node:18-alpine.

Conte ao agente sobre o Node.js

O agente lê o rótulo do contêiner "com.azure.dev.pipelines.handler.node.path". Se esse rótulo existir, ele deverá ser o caminho para o binário do Node.js.

Por exemplo, em uma imagem com base em node:18-alpine, adicione a seguinte linha ao Dockerfile:

LABEL "com.azure.dev.pipelines.agent.handler.node.path"="/usr/local/bin/node"

Adicionar os pacotes necessários

O Azure Pipelines pressupõe um sistema baseado em Bash com pacotes administrativos comuns instalados. O Alpine Linux, em particular, não vem com vários dos pacotes necessários. Instale bash, sudo e shadow para abranger as necessidades básicas.

RUN apk add bash sudo shadow

Se depender de tarefas internas ou do Marketplace, você também fornecerá os binários necessários.

Exemplo completo de Dockerfile

FROM node:18-alpine

RUN apk add --no-cache --virtual .pipeline-deps readline linux-pam \
  && apk add bash sudo shadow \
  && apk del .pipeline-deps

LABEL "com.azure.dev.pipelines.agent.handler.node.path"="/usr/local/bin/node"

CMD [ "node" ]