Trabalhos de contêiner em dutos YAML

Serviços de DevOps do Azure | Azure DevOps Server | Azure DevOps Server 2022

Este artigo explica os trabalhos de contêiner no Azure Pipelines. Os contêineres são abstrações leves do sistema operacional host que fornecem todos os elementos necessários para executar um trabalho em um ambiente específico.

Por padrão, os trabalhos do Azure Pipelines são executados diretamente em agentes instalados em máquinas host. Os trabalhos de agente hospedado são convenientes, exigem pouca configuração inicial ou manutenção de infraestrutura e são adequados para projetos básicos. Para obter mais controle sobre o contexto da tarefa, você pode definir e executar trabalhos de pipeline em contêineres para obter as versões exatas de sistemas operacionais, ferramentas e dependências desejadas.

Para um trabalho de contêiner, o agente primeiro busca e inicia o contêiner e, em seguida, executa cada etapa do trabalho dentro do contêiner. Se você precisar de um controle mais refinado de etapas de compilação individuais, poderá usar destinos de etapa para escolher um contêiner ou host para cada etapa.

Requisitos para trabalhos de contêiner

  • Um pipeline baseado em YAML. Os pipelines clássicos não suportam trabalhos de contêiner.
  • Um agente hospedado no Windows ou Ubuntu. Os agentes do MacOS não suportam contêineres. Para usar agentes Linux que não sejam do Ubuntu, consulte Contêineres baseados em Nonglibc.
  • Docker instalado no agente, com permissão para acessar o daemon do Docker.
  • Agente em execução diretamente no host, ainda não dentro de um contêiner. Não há suporte para contêineres aninhados.

Os contêineres baseados em Linux também têm os seguintes requisitos:

  • Bash instalado.
  • Baseado na Biblioteca GNU C (glibc). Os contentores sem glibc exigem configuração adicional. Para obter mais informações, consulte Contêineres não baseados em glibc.
  • Não ENTRYPOINT. Contêineres com um ENTRYPOINT podem não funcionar, porque o docker exec espera que o contêiner esteja sempre em execução.
  • USER a quem foi fornecido acesso a groupadd e outros comandos privilegiados sem utilizar sudo.
  • Capacidade de executar Node.js, que o agente fornece.

    Nota

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

Alguns contêineres despojados disponíveis no Docker Hub, especialmente contêineres baseados no Alpine Linux, não atendem a esses requisitos. Para obter mais informações, consulte Contêineres não baseados em glibc.

Emprego único

O exemplo a seguir define um contêiner de trabalho único do Windows ou Linux.

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

pool:
  vmImage: 'ubuntu-latest'

container: ubuntu:18.04

steps:
- script: printenv

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 várias versões do Ubuntu Linux. Você não precisa usar a jobs palavra-chave, 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 em um único host de agente

Um trabalho de contêiner usa o arquivo de configuração do Docker do agente de host subjacente para autorização de registro de imagem. Este ficheiro termina a execução no final da inicialização do contentor do Registo do Docker.

A obtenção de imagens de um registo para tarefas de contentor poderá ser negada devido a autenticação não autorizada, se outra tarefa em execução em paralelo no agente já tiver terminado a sessão no ficheiro de configuração do Docker. A solução é definir uma variável de ambiente do Docker chamada DOCKER_CONFIG para cada pool de agentes em execução no agente hospedado.

Exporte o DOCKER_CONFIG script runsvc.sh de cada pool de agentes da seguinte maneira:

export DOCKER_CONFIG=./.docker

Opções de inicialização

Você pode usar a propriedade para especificar opções para inicialização do options contêiner.

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

steps:
- script: echo hello

Execute docker create --help para obter 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 Pipelines. Verifique primeiro se pode usar uma propriedade container para o mesmo propósito.

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

Mapeamento de sockets Docker

A partir da versão 5.279.0 do agente, os jobs em contentor nos agentes Linux não montam, por predefinição, o socket Docker do anfitrião (/var/run/docker.sock) no contentor do job. Este padrão segue o princípio do menor privilégio, por isso trabalhos que não necessitam de comportamento Docker em contentor não têm acesso ao daemon Docker anfitrião.

Se a sua tarefa em contentor executar comandos Docker no daemon do anfitrião (Docker-in-container), ative explicitamente essa opção definindo mapDockerSocket como true no recurso do contentor.

resources:
  containers:
  - container: my_container
    image: ubuntu:22.04
    mapDockerSocket: true

Para implementações em larga escala, em que atualizar o YAML em todo o lado é impraticável, pode definir temporariamente a variável de ambiente AZP_AGENT_DEFAULT_MAP_DOCKER_SOCKET_TO_FALSE=false do anfitrião do agente para restaurar o comportamento predefinido anterior, em que as tarefas de contentor Linux mapeiam /var/run/docker.sock, a menos que mapDockerSocket seja explicitamente configurado. Utilize esta definição apenas como uma mitigação de curto prazo, pois reativa por defeito o mapeamento do socket do Docker.

Atenção

Mapear o socket Docker tem sérias implicações de segurança. O código dentro do contentor pode correr como root no teu host Docker. Só define mapDockerSocket: true quando o teu trabalho exigir comportamento Docker-in-container.

Definição de recipiente reutilizável

O exemplo YAML seguinte define os contentores na secção resources e, em seguida, faz referência aos mesmos através dos aliases que lhes foram atribuídos. A jobs palavra-chave é usada 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 finais 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 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ê pode fazer referência ao ponto de extremidade na definição de 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

Nota

O Azure Pipelines não pode configurar uma conexão de serviço para o Amazon Elastic Container Registry (ECR), porque o Amazon ECR requer outras ferramentas de cliente para converter credenciais da Amazon Web Services (AWS) para ser utilizável para autenticação do Docker.

Contentores não baseados em glibc

Os agentes hospedados do Azure Pipelines fornecem Node.js, que é necessário para executar tarefas e scripts. A versão Node.js compila em relação ao tempo de execução C usado na nuvem hospedada, normalmente glibc. Algumas variantes do Linux usam outros tempos de execução C. Por exemplo, o Alpine Linux usa musl. Para obter mais informações, consulte Agentes hospedados pela Microsoft.

Se você quiser usar um contêiner não baseado em glibc em um pipeline, você deve:

  • Forneça a sua própria cópia do Node.js.
  • Adicione um rótulo à sua imagem apontando para o local do binário Node.js.
  • Forneça as dependências do Azure Pipelines bash, sudo, which e groupadd.

Forneça o seu próprio Node.js

Se utilizar um contentor sem glibc, tem de adicionar um binário do Node.js ao seu contentor. Node.js 18 é uma escolha segura. Comece pela node:18-alpine imagem.

Direcione o agente para Node.js

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

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

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

Adicionar pacotes necessários

O Azure Pipelines requer um sistema baseado em Bash para ter pacotes administrativos comuns instalados. O Alpine Linux não tem vários dos pacotes necessários. Instalar bash, sudoe shadow para cobrir necessidades básicas.

RUN apk add bash sudo shadow

Se depender de tarefas incorporadas ou do Marketplace, forneça também os binários necessários.

Exemplo completo do 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" ]