Executar um agente autoalojado no Docker

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

Este artigo fornece instruções para executar seu agente do Azure Pipelines no Docker. Você pode configurar um agente auto-hospedado no Azure Pipelines para ser executado dentro de um contêiner Windows Server Core (para hosts Windows) ou Ubuntu (para hosts Linux) com o Docker. Isso é útil quando você deseja executar agentes com orquestração externa, como Instâncias de Contêiner do Azure. Neste artigo, você percorrerá um exemplo completo de contêiner, incluindo a autoatualização do agente de manipulação.

O Windows e o Linux são suportados como hosts de contêiner. Os contêineres do Windows devem ser executados em um Windows vmImage. Para executar seu agente no Docker, você passará algumas variáveis de ambiente para docker runo , que configura o agente para se conectar ao Azure Pipelines ou ao Azure DevOps Server. Finalmente, você personaliza o contêiner para atender às suas necessidades. As tarefas e os scripts podem depender da disponibilidade de PATHferramentas específicas no contêiner, e é sua responsabilidade garantir que essas ferramentas estejam disponíveis.

Esse recurso requer a versão 2.149 ou posterior do agente. O Azure DevOps 2019 não foi fornecido com uma versão de agente compatível. No entanto, você pode carregar o pacote de agente correto para sua camada de aplicativo se quiser executar agentes do Docker.

Windows

Ativar Hyper-V

O Hyper-V não está habilitado por padrão no Windows. Se quiser fornecer isolamento entre contêineres, habilite o Hyper-V. Caso contrário, o Docker para Windows não será iniciado.

Nota

Você deve habilitar a virtualização em sua máquina. Normalmente, é ativado por padrão. No entanto, se a instalação do Hyper-V falhar, consulte a documentação do sistema para saber como habilitar a virtualização.

Instalar o Docker para Windows

Se estiver a utilizar o Windows 10, pode instalar o Docker Community Edition. Para o Windows Server 2016, instale o Docker Enterprise Edition.

Alternar o Docker para usar contêineres do Windows

Por padrão, o Docker para Windows está configurado para usar contêineres Linux. Para permitir a execução do contêiner do Windows, confirme se o Docker para Windows está executando o daemon do Windows.

Criar e construir o Dockerfile

Em seguida, crie o Dockerfile.

  1. Abra uma linha de comandos.

  2. Crie um novo diretório:

    mkdir "C:\azp-agent-in-docker\"
    
  3. Vá para este novo diretório:

    cd "C:\azp-agent-in-docker\"
    
  4. Salve o seguinte conteúdo em um arquivo chamado C:\azp-agent-in-docker\azp-agent-windows.dockerfile:

    FROM mcr.microsoft.com/windows/servercore:ltsc2022
    
    WORKDIR /azp/
    
    COPY ./start.ps1 ./
    
    CMD powershell .\start.ps1
    
  5. Salve o seguinte conteúdo em C:\azp-agent-in-docker\start.ps1:

    function Print-Header ($header) {
      Write-Host "`n${header}`n" -ForegroundColor Cyan
    }
    
    if (-not (Test-Path Env:AZP_URL)) {
      Write-Error "error: missing AZP_URL environment variable"
      exit 1
    }
    
    if (-not (Test-Path Env:AZP_TOKEN_FILE)) {
      if (-not (Test-Path Env:AZP_TOKEN)) {
        Write-Error "error: missing AZP_TOKEN environment variable"
        exit 1
      }
    
      $Env:AZP_TOKEN_FILE = "\azp\.token"
      $Env:AZP_TOKEN | Out-File -FilePath $Env:AZP_TOKEN_FILE
    }
    
    Remove-Item Env:AZP_TOKEN
    
    if ((Test-Path Env:AZP_WORK) -and -not (Test-Path $Env:AZP_WORK)) {
      New-Item $Env:AZP_WORK -ItemType directory | Out-Null
    }
    
    New-Item "\azp\agent" -ItemType directory | Out-Null
    
    # Let the agent ignore the token env variables
    $Env:VSO_AGENT_IGNORE = "AZP_TOKEN,AZP_TOKEN_FILE"
    
    Set-Location agent
    
    Print-Header "1. Determining matching Azure Pipelines agent..."
    
    $base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$(Get-Content ${Env:AZP_TOKEN_FILE})"))
    $package = Invoke-RestMethod -Headers @{Authorization=("Basic $base64AuthInfo")} "$(${Env:AZP_URL})/_apis/distributedtask/packages/agent?platform=win-x64&`$top=1"
    $packageUrl = $package[0].Value.downloadUrl
    
    Write-Host $packageUrl
    
    Print-Header "2. Downloading and installing Azure Pipelines agent..."
    
    $wc = New-Object System.Net.WebClient
    $wc.DownloadFile($packageUrl, "$(Get-Location)\agent.zip")
    
    Expand-Archive -Path "agent.zip" -DestinationPath "\azp\agent"
    
    try {
      Print-Header "3. Configuring Azure Pipelines agent..."
    
      .\config.cmd --unattended `
        --agent "$(if (Test-Path Env:AZP_AGENT_NAME) { ${Env:AZP_AGENT_NAME} } else { hostname })" `
        --url "$(${Env:AZP_URL})" `
        --auth PAT `
        --token "$(Get-Content ${Env:AZP_TOKEN_FILE})" `
        --pool "$(if (Test-Path Env:AZP_POOL) { ${Env:AZP_POOL} } else { 'Default' })" `
        --work "$(if (Test-Path Env:AZP_WORK) { ${Env:AZP_WORK} } else { '_work' })" `
        --replace
    
      Print-Header "4. Running Azure Pipelines agent..."
    
      .\run.cmd
    } finally {
      Print-Header "Cleanup. Removing Azure Pipelines agent..."
    
      .\config.cmd remove --unattended `
        --auth PAT `
        --token "$(Get-Content ${Env:AZP_TOKEN_FILE})"
    }
    
  6. Execute o seguinte comando dentro desse diretório:

    docker build --tag "azp-agent:windows" --file "./azp-agent-windows.dockerfile" .
    

    A imagem final está marcada com .azp-agent:windows

Iniciar a imagem

Agora que você criou uma imagem, você pode executar um contêiner. Isso instala a versão mais recente do agente, configura-a e executa o agente. Ele tem como alvo o pool de agentes especificado (o Default pool de agentes por padrão) de uma instância especificada do Azure DevOps ou do Azure DevOps Server de sua escolha:

docker run -e AZP_URL="<Azure DevOps instance>" -e AZP_TOKEN="<Personal Access Token>" -e AZP_POOL="<Agent Pool Name>" -e AZP_AGENT_NAME="Docker Agent - Windows" --name "azp-agent-windows" azp-agent:windows

Talvez seja necessário especificar o parâmetro se tiver problemas de --network rede.

docker run --network "Default Switch" < . . . >

Talvez seja necessário especificar --interactive e sinalizar (ou simplesmente -it) se quiser parar o contêiner e remover o agente com Ctrl + C.--tty

docker run --interactive --tty < . . . >

Se você quiser um novo contêiner de agente para cada trabalho de pipeline, passe o --once sinalizador para o run comando.

docker run < . . . > --once

Com o --once sinalizador, talvez você queira usar um sistema de orquestração de contêiner, como Kubernetes ou Instâncias de Contêiner do Azure, para iniciar uma nova cópia do contêiner quando o trabalho for concluído.

Você pode controlar o nome do agente, o pool de agentes e o diretório de trabalho do agente usando variáveis de ambiente opcionais.

Linux

Instalar o Docker

Dependendo da sua distribuição Linux, você pode instalar o Docker Community Edition ou o Docker Enterprise Edition.

Criar e construir o Dockerfile

Em seguida, crie o Dockerfile.

  1. Abra um terminal.

  2. Crie um novo diretório (recomendado):

    mkdir ~/azp-agent-in-docker/
    
  3. Vá para este novo diretório:

    cd ~/azp-agent-in-docker/
    
  4. Salve o seguinte conteúdo em ~/azp-agent-in-docker/azp-agent-linux.dockerfile:

    • Para a Alpine:

      FROM alpine
      ENV TARGETARCH="linux-musl-x64"
      
      # Another option:
      # FROM arm64v8/alpine
      # ENV TARGETARCH="linux-musl-arm64"
      
      RUN apk update
      RUN apk upgrade
      RUN apk add bash curl git icu-libs jq
      
      WORKDIR /azp/
      
      COPY ./start.sh ./
      RUN chmod +x ./start.sh
      
      RUN adduser -D agent
      RUN chown agent ./
      USER agent
      # Another option is to run the agent as root.
      # ENV AGENT_ALLOW_RUNASROOT="true"
      
      ENTRYPOINT [ "./start.sh" ]
      
    • Para Ubuntu 22.04:

      FROM ubuntu:22.04
      ENV TARGETARCH="linux-x64"
      # Also can be "linux-arm", "linux-arm64".
      
      RUN apt update
      RUN apt upgrade -y
      RUN apt install -y curl git jq libicu70
      
      WORKDIR /azp/
      
      COPY ./start.sh ./
      RUN chmod +x ./start.sh
      
      # Create agent user and set up home directory
      RUN useradd -m -d /home/agent agent
      RUN chown -R agent:agent /azp /home/agent
      
      USER agent
      # Another option is to run the agent as root.
      # ENV AGENT_ALLOW_RUNASROOT="true"
      
      ENTRYPOINT [ "./start.sh" ]
      

    Descomente a ENV AGENT_ALLOW_RUNASROOT="true" linha e remova a adição do agent usuário antes dessa linha se quiser executar o agente como root.

    Nota

    As tarefas podem depender dos executáveis que se espera que seu contêiner forneça. Por exemplo, você deve adicionar os zip pacotes e unzip ao RUN apt install -y comando para executar as ArchiveFiles tarefas e ExtractFiles . Além disso, como esta é uma imagem do Linux Ubuntu para o agente usar, você pode personalizar a imagem conforme necessário. Por exemplo: se você precisar criar aplicativos .NET, você pode seguir o documento Instalar o SDK do .NET ou o Tempo de Execução do .NET no Ubuntu e adicioná-lo à sua imagem.

  5. Salve o seguinte conteúdo no ~/azp-agent-in-docker/start.sh, certificando-se de usar terminações de linha no estilo Unix (LF):

    #!/bin/bash
    set -e
    
    if [ -z "${AZP_URL}" ]; then
      echo 1>&2 "error: missing AZP_URL environment variable"
      exit 1
    fi
    
    if [ -z "${AZP_TOKEN_FILE}" ]; then
      if [ -z "${AZP_TOKEN}" ]; then
        echo 1>&2 "error: missing AZP_TOKEN environment variable"
        exit 1
      fi
    
      AZP_TOKEN_FILE="/azp/.token"
      echo -n "${AZP_TOKEN}" > "${AZP_TOKEN_FILE}"
    fi
    
    unset AZP_TOKEN
    
    if [ -n "${AZP_WORK}" ]; then
      mkdir -p "${AZP_WORK}"
    fi
    
    cleanup() {
      trap "" EXIT
    
      if [ -e ./config.sh ]; then
        print_header "Cleanup. Removing Azure Pipelines agent..."
    
        # If the agent has some running jobs, the configuration removal process will fail.
        # So, give it some time to finish the job.
        while true; do
          ./config.sh remove --unattended --auth "PAT" --token $(cat "${AZP_TOKEN_FILE}") && break
    
          echo "Retrying in 30 seconds..."
          sleep 30
        done
      fi
    }
    
    print_header() {
      lightcyan="\033[1;36m"
      nocolor="\033[0m"
      echo -e "\n${lightcyan}$1${nocolor}\n"
    }
    
    # Let the agent ignore the token env variables
    export VSO_AGENT_IGNORE="AZP_TOKEN,AZP_TOKEN_FILE"
    
    print_header "1. Determining matching Azure Pipelines agent..."
    
    AZP_AGENT_PACKAGES=$(curl -LsS \
        -u user:$(cat "${AZP_TOKEN_FILE}") \
        -H "Accept:application/json" \
        "${AZP_URL}/_apis/distributedtask/packages/agent?platform=${TARGETARCH}&top=1")
    
    AZP_AGENT_PACKAGE_LATEST_URL=$(echo "${AZP_AGENT_PACKAGES}" | jq -r ".value[0].downloadUrl")
    
    if [ -z "${AZP_AGENT_PACKAGE_LATEST_URL}" -o "${AZP_AGENT_PACKAGE_LATEST_URL}" == "null" ]; then
      echo 1>&2 "error: could not determine a matching Azure Pipelines agent"
      echo 1>&2 "check that account "${AZP_URL}" is correct and the token is valid for that account"
      exit 1
    fi
    
    print_header "2. Downloading and extracting Azure Pipelines agent..."
    
    curl -LsS "${AZP_AGENT_PACKAGE_LATEST_URL}" | tar -xz & wait $!
    
    source ./env.sh
    
    trap "cleanup; exit 0" EXIT
    trap "cleanup; exit 130" INT
    trap "cleanup; exit 143" TERM
    
    print_header "3. Configuring Azure Pipelines agent..."
    
    ./config.sh --unattended \
      --agent "${AZP_AGENT_NAME:-$(hostname)}" \
      --url "${AZP_URL}" \
      --auth "PAT" \
      --token $(cat "${AZP_TOKEN_FILE}") \
      --pool "${AZP_POOL:-Default}" \
      --work "${AZP_WORK:-_work}" \
      --replace \
      --acceptTeeEula & wait $!
    
    print_header "4. Running Azure Pipelines agent..."
    
    chmod +x ./run.sh
    
    # To be aware of TERM and INT signals call ./run.sh
    # Running it with the --once flag at the end will shut down the agent after the build is executed
    ./run.sh "$@" & wait $!
    

    Nota

    Você também deve usar um sistema de orquestração de contêiner, como Kubernetes ou Instâncias de Contêiner do Azure, para iniciar novas cópias do contêiner quando o trabalho for concluído.

  6. Execute o seguinte comando dentro desse diretório:

    docker build --tag "azp-agent:linux" --file "./azp-agent-linux.dockerfile" .
    

    A imagem final está marcada com .azp-agent:linux

Iniciar a imagem

Agora que você criou uma imagem, você pode executar um contêiner. Isso instala a versão mais recente do agente, configura-a e executa o agente. Ele tem como alvo o pool de agentes especificado (o Default pool de agentes por padrão) de uma instância especificada do Azure DevOps ou do Azure DevOps Server de sua escolha:

docker run -e AZP_URL="<Azure DevOps instance>" -e AZP_TOKEN="<Personal Access Token>" -e AZP_POOL="<Agent Pool Name>" -e AZP_AGENT_NAME="Docker Agent - Linux" --name "azp-agent-linux" azp-agent:linux

Talvez seja necessário especificar --interactive e sinalizar (ou simplesmente -it) se quiser parar o contêiner e remover o agente com Ctrl + C.--tty

docker run --interactive --tty < . . . >

Se você quiser um novo contêiner de agente para cada trabalho de pipeline, passe o --once sinalizador para o run comando.

docker run < . . . > --once

Com o --once sinalizador, talvez você queira usar um sistema de orquestração de contêiner, como Kubernetes ou Instâncias de Contêiner do Azure, para iniciar uma nova cópia do contêiner quando o trabalho for concluído.

Você pode controlar o nome do agente, o pool de agentes e o diretório de trabalho do agente usando variáveis de ambiente opcionais.

Variáveis de ambiente

Variável de ambiente Description
AZP_URL A URL da instância do Azure DevOps ou do Azure DevOps Server.
AZP_TOKEN Token de Acesso Pessoal (PAT) com escopo de Pools de Agentes (ler, gerenciar), criado por um usuário que tem permissão para configurar agentes, em AZP_URL.
AZP_AGENT_NAME Nome do agente (valor padrão: o nome do host do contêiner).
AZP_POOL Nome do pool de agentes (valor padrão: Default).
AZP_WORK Diretório de trabalho (valor padrão: _work).

Adicionar ferramentas e personalizar o contêiner

Você criou um agente de compilação básico. Você pode estender o Dockerfile para incluir ferramentas adicionais e suas dependências ou criar seu próprio contêiner usando este como uma camada base. Apenas certifique-se de que os seguintes itens são deixados intocados:

  • O start.sh script é chamado pelo Dockerfile.
  • O start.sh script é o último comando no Dockerfile.
  • Certifique-se de que os contêineres derivados não removam nenhuma das dependências declaradas pelo Dockerfile.

Usar o Docker em um contêiner do Docker

Para usar o Docker de dentro de um contêiner do Docker, você vincula e monta o soquete do Docker.

Atenção

Fazer isso tem sérias implicações de segurança. O código dentro do contêiner agora pode ser executado como root no host do Docker.

Se tiver certeza de que deseja fazer isso, consulte a documentação de montagem de ligação em Docker.com.

Usar o cluster do Serviço Kubernetes do Azure

Atenção

Por favor, considere que qualquer tarefa baseada no docker não funcionará no AKS 1.19 ou posterior devido à restrição do docker no docker. O Docker foi substituído por containerd no Kubernetes 1.19 e o Docker-in-Docker ficou indisponível.

Implantar e configurar o Serviço Kubernetes do Azure

Siga as etapas em Guia de início rápido: implantar um cluster do Serviço Kubernetes do Azure (AKS) usando o portal do Azure. Depois disso, seu console do PowerShell ou do Shell pode usar a linha de kubectl comando.

Implantar e configurar o Registro de Contêiner do Azure

Siga as etapas em Guia de início rápido: criar um registro de contêiner do Azure usando o portal do Azure. Depois disso, você pode enviar e extrair contêineres do Registro de Contêiner do Azure.

Configurar segredos e implantar um conjunto de réplicas

  1. Crie os segredos no cluster AKS.

    kubectl create secret generic azdevops \
      --from-literal=AZP_URL=https://dev.azure.com/yourOrg \
      --from-literal=AZP_TOKEN=YourPAT \
      --from-literal=AZP_POOL=NameOfYourPool
    
  2. Execute este comando para enviar seu contêiner para o Registro de contêiner:

    docker push "<acr-server>/azp-agent:<tag>"
    
  3. Configure a integração do Registro de Contêiner para clusters AKS existentes.

    Nota

    Se você tiver várias assinaturas no Portal do Azure, use este comando primeiro para selecionar uma assinatura

    az account set --subscription "<subscription id or subscription name>"
    
    az aks update -n "<myAKSCluster>" -g "<myResourceGroup>" --attach-acr "<acr-name>"
    
  4. Salve o seguinte conteúdo em ~/AKS/ReplicationController.yml:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: azdevops-deployment
      labels:
        app: azdevops-agent
    spec:
      replicas: 1 # here is the configuration for the actual agent always running
      selector:
        matchLabels:
          app: azdevops-agent
      template:
        metadata:
          labels:
            app: azdevops-agent
        spec:
          containers:
          - name: kubepodcreation
            image: <acr-server>/azp-agent:<tag>
            env:
              - name: AZP_URL
                valueFrom:
                  secretKeyRef:
                    name: azdevops
                    key: AZP_URL
              - name: AZP_TOKEN
                valueFrom:
                  secretKeyRef:
                    name: azdevops
                    key: AZP_TOKEN
              - name: AZP_POOL
                valueFrom:
                  secretKeyRef:
                    name: azdevops
                    key: AZP_POOL
            volumeMounts:
            - mountPath: /var/run/docker.sock
              name: docker-volume
          volumes:
          - name: docker-volume
            hostPath:
              path: /var/run/docker.sock
    

    Este Kubernetes YAML cria um conjunto de réplicas e uma implantação, onde replicas: 1 indica o número ou os agentes que estão sendo executados no cluster.

  5. Execute este comando:

    kubectl apply -f ReplicationController.yml
    

Agora, seus agentes executarão o cluster AKS.

Definir parâmetro MTU personalizado

Permitir a especificação do valor MTU para redes usadas por trabalhos de contêiner (útil para cenários de docker-in-docker no cluster k8s).

Você precisa definir a variável de ambiente AGENT_DOCKER_MTU_VALUE definir o valor de MTU e, em seguida, reiniciar o agente auto-hospedado. Você pode encontrar mais sobre a reinicialização do agente aqui e sobre a configuração de variáveis de ambiente diferentes para cada agente individual aqui.

Isso permite que você configure um parâmetro de rede para o contêiner de trabalho, o uso deste comando é semelhante ao uso do próximo comando durante a configuração de rede de contêiner:

-o com.docker.network.driver.mtu=AGENT_DOCKER_MTU_VALUE

Montagem de volumes usando o Docker em um contêiner do Docker

Se um contêiner do Docker for executado dentro de outro contêiner do Docker, ambos usarão o daemon do host, portanto, todos os caminhos de montagem fazem referência ao host, não ao contêiner.

Por exemplo, se quisermos montar o caminho do host para o contêiner externo do Docker, podemos usar este comando:

docker run ... -v "<path-on-host>:<path-on-outer-container>" ...

E se quisermos montar o caminho do host para o contêiner interno do Docker, podemos usar este comando:

docker run ... -v "<path-on-host>:<path-on-inner-container>" ...

Mas não podemos montar caminhos do recipiente externo para o interno; para contornar isso, temos que declarar uma variável ENV:

docker run ... --env DIND_USER_HOME=$HOME ...

Depois disso, podemos iniciar o recipiente interno a partir do externo usando este comando:

docker run ... -v "${DIND_USER_HOME}:<path-on-inner-container>" ...

Erros comuns

Se estiver a utilizar o Windows e receber o seguinte erro:

standard_init_linux.go:178: exec user process caused "no such file or directory"

Instale o Git Bash baixando e instalando o git-scm.

Execute este comando:

dos2unix ~/azp-agent-in-docker/Dockerfile
dos2unix ~/azp-agent-in-docker/start.sh
git add .
git commit -m "Fixed CR"
git push

Tente novamente. Você não recebe mais o erro.