Eseguire un agente self-hosted in Docker

Questo articolo fornisce istruzioni per l'esecuzione dell'Azure Pipelines in Docker. È possibile configurare un agente self-hosted in Azure Pipelines per l'esecuzione all'interno di un Windows Server Core (per gli host Windows) o di un contenitore Ubuntu (per gli host Linux) con Docker. Ciò è utile quando si desidera eseguire agenti con orchestrazione esterna, ad esempio Istanze di Azure Container. In questo articolo verrà illustrato un esempio di contenitore completo, inclusa la gestione dell'aggiornamento automatico dell'agente.

Sia Windows che Linux sono supportati come host contenitore. Windows contenitori devono essere eseguiti in un Windows vmImage . Per eseguire l'agente in Docker, si passeranno alcune variabili di ambiente a , che configura l'agente per la connessione a Azure Pipelines o Azure DevOps Server. Infine, si personalizza il contenitore in base alle proprie esigenze. Le attività e gli script possono dipendere dalla disponibilità di strumenti specifici nell'oggetto del contenitore ed è responsabilità dell'utente assicurarsi che PATH questi strumenti siano disponibili.

Questa funzionalità richiede la versione 2.149 o successiva dell'agente. Azure DevOps 2019 non è stato disponibile con una versione dell'agente compatibile. Tuttavia, è possibile caricare il pacchetto dell'agente corretto nel livello applicazione se si desidera eseguire gli agenti Docker.

Windows

Abilitare Hyper-V

Hyper-V non è abilitato per impostazione predefinita in Windows. Se si vuole fornire l'isolamento tra i contenitori, è necessario abilitare Hyper-V. In caso contrario, Docker Windows non verrà avviato.

Nota

È necessario abilitare la virtualizzazione nel computer. In genere è abilitata per impostazione predefinita. Tuttavia, se l'installazione di Hyper-V non riesce, fare riferimento alla documentazione del sistema per informazioni su come abilitare la virtualizzazione.

Installare Docker per Windows

Se si usa Windows 10, è possibile installare Docker Community Edition. Per Windows Server 2016, installare docker edizione Enterprise.

Impostare Docker per l'uso Windows contenitori

Per impostazione predefinita, Docker per Windows è configurato per l'uso di contenitori Linux. Per consentire l'esecuzione Windows contenitore, verificare che Docker per Windows sia in esecuzione il daemon Windows.

Creare e compilare il Dockerfile

Creare quindi il Dockerfile.

  1. Aprire un prompt dei comandi.

  2. Creare una nuova directory:

    mkdir C:\dockeragent
    
  3. Passare alla nuova directory:

    cd C:\dockeragent
    
  4. Salvare il contenuto seguente in un file denominato C:\dockeragent\Dockerfile (nessuna estensione di file):

    FROM mcr.microsoft.com/windows/servercore:ltsc2019
    
    WORKDIR /azp
    
    COPY start.ps1 .
    
    CMD powershell .\start.ps1
    
  5. Salvare il contenuto seguente in C:\dockeragent\start.ps1 :

    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
    
    Write-Host "1. Determining matching Azure Pipelines agent..." -ForegroundColor Cyan
    
    $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
    
    Write-Host "2. Downloading and installing Azure Pipelines agent..." -ForegroundColor Cyan
    
    $wc = New-Object System.Net.WebClient
    $wc.DownloadFile($packageUrl, "$(Get-Location)\agent.zip")
    
    Expand-Archive -Path "agent.zip" -DestinationPath "\azp\agent"
    
    try
    {
      Write-Host "3. Configuring Azure Pipelines agent..." -ForegroundColor Cyan
    
      .\config.cmd --unattended `
        --agent "$(if (Test-Path Env:AZP_AGENT_NAME) { ${Env:AZP_AGENT_NAME} } else { ${Env:computername} })" `
        --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
    
      Write-Host "4. Running Azure Pipelines agent..." -ForegroundColor Cyan
    
      .\run.cmd
    }
    finally
    {
      Write-Host "Cleanup. Removing Azure Pipelines agent..." -ForegroundColor Cyan
    
      .\config.cmd remove --unattended `
        --auth PAT `
        --token "$(Get-Content ${Env:AZP_TOKEN_FILE})"
    }
    
  6. Eseguire il comando seguente all'interno di tale directory:

    docker build -t dockeragent:latest .
    

    Questo comando compila il Dockerfile nella directory corrente.

    L'immagine finale è contrassegnata con dockeragent:latest . È possibile eseguirlo facilmente in un contenitore come , perché il dockeragentlatest tag è l'impostazione predefinita se non viene specificato alcun tag.

Avviare l'immagine

Ora che è stata creata un'immagine, è possibile eseguire un contenitore.

  1. Aprire un prompt dei comandi.

  2. Eseguire il contenitore. Verrà installata la versione più recente dell'agente, configurata ed eseguita. È destinato al Default pool di un'istanza Azure DevOps o Azure DevOps Server di propria scelta:

    docker run -e AZP_URL=<Azure DevOps instance> -e AZP_TOKEN=<PAT token> -e AZP_AGENT_NAME=mydockeragent dockeragent:latest
    

Facoltativamente, è possibile controllare la directory di lavoro del pool e dell'agente usando variabili di ambiente aggiuntive.

Se si vuole un nuovo contenitore dell'agente per ogni esecuzione della pipeline, passare il flag al run comando . È anche necessario usare un sistema di orchestrazione del contenitore, ad esempio Kubernetes o Istanze di Azure Container, per avviare nuove copie del contenitore al termine del lavoro.

Linux

Installare Docker

A seconda della distribuzione di Linux, è possibile installare Docker Community Edition oDocker edizione Enterprise.

Creare e compilare il Dockerfile

Creare quindi il Dockerfile.

  1. Aprire un terminale.

  2. Creare una nuova directory (operazione consigliata):

    mkdir ~/dockeragent
    
  3. Passare alla nuova directory:

    cd ~/dockeragent
    
  4. Salvare il contenuto seguente in ~/dockeragent/Dockerfile :

    FROM ubuntu:18.04
    
    # To make it easier for build and release pipelines to run apt-get,
    # configure apt to not require confirmation (assume the -y argument by default)
    ENV DEBIAN_FRONTEND=noninteractive
    RUN echo "APT::Get::Assume-Yes \"true\";" > /etc/apt/apt.conf.d/90assumeyes
    
    RUN apt-get update && apt-get install -y --no-install-recommends \
        ca-certificates \
        curl \
        jq \
        git \
        iputils-ping \
        libcurl4 \
        libicu60 \
        libunwind8 \
        netcat \
        libssl1.0 \
      && rm -rf /var/lib/apt/lists/*
    
    RUN curl -LsS https://aka.ms/InstallAzureCLIDeb | bash \
      && rm -rf /var/lib/apt/lists/*
    
    ARG TARGETARCH=amd64
    ARG AGENT_VERSION=2.194.0
    
    WORKDIR /azp
    RUN if [ "$TARGETARCH" = "amd64" ]; then \
          AZP_AGENTPACKAGE_URL=https://vstsagentpackage.azureedge.net/agent/${AGENT_VERSION}/vsts-agent-linux-x64-${AGENT_VERSION}.tar.gz; \
        else \
          AZP_AGENTPACKAGE_URL=https://vstsagentpackage.azureedge.net/agent/${AGENT_VERSION}/vsts-agent-linux-${TARGETARCH}-${AGENT_VERSION}.tar.gz; \
        fi; \
        curl -LsS "$AZP_AGENTPACKAGE_URL" | tar -xz
    
    COPY ./start.sh .
    RUN chmod +x start.sh
    
    ENTRYPOINT [ "./start.sh" ]
    

    Nota

    Le attività possono dipendere da eseguibili che il contenitore deve fornire. Ad esempio, è necessario aggiungere zip i pacchetti e al comando per eseguire le attività e unzipRUN apt-getArchiveFilesExtractFiles .

  5. Salvare il contenuto seguente in , assicurando di usare terminazioni di riga in stile ~/dockeragent/start.sh 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
    
    export AGENT_ALLOW_RUNASROOT="1"
    
    cleanup() {
      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 "${lightcyan}$1${nocolor}"
    }
    
    # Let the agent ignore the token env variables
    export VSO_AGENT_IGNORE=AZP_TOKEN,AZP_TOKEN_FILE
    
    source ./env.sh
    
    print_header "1. 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 "2. Running Azure Pipelines agent..."
    
    trap 'cleanup; exit 0' EXIT
    trap 'cleanup; exit 130' INT
    trap 'cleanup; exit 143' TERM
    
    # 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

    È anche necessario usare un sistema di orchestrazione del contenitore, ad esempio Kubernetes o Istanze di Azure Container, per avviare nuove copie del contenitore al termine del lavoro.

  6. Eseguire il comando seguente all'interno di tale directory:

    docker build -t dockeragent:latest .
    

    Questo comando compila il Dockerfile nella directory corrente.

    L'immagine finale è contrassegnata con dockeragent:latest . È possibile eseguirlo facilmente in un contenitore come , perché il dockeragentlatest tag è l'impostazione predefinita se non viene specificato alcun tag.

Avviare l'immagine

Ora che è stata creata un'immagine, è possibile eseguire un contenitore.

  1. Aprire un terminale.

  2. Eseguire il contenitore. Verrà installata la versione più recente dell'agente, configurata ed eseguita. È destinato al Default pool di un'istanza Azure DevOps o Azure DevOps Server di propria scelta:

    docker run -e AZP_URL=<Azure DevOps instance> -e AZP_TOKEN=<PAT token> -e AZP_AGENT_NAME=mydockeragent dockeragent:latest
    

    Se si vuole un nuovo contenitore dell'agente per ogni processo della pipeline, passare il flag al run comando .

    docker run -e AZP_URL=<Azure DevOps instance> -e AZP_TOKEN=<PAT token> -e AZP_AGENT_NAME=mydockeragent dockeragent:latest --once
    

Facoltativamente, è possibile controllare la directory di lavoro del pool e dell'agente usando variabili di ambiente aggiuntive.

Variabili di ambiente

Variabile di ambiente Descrizione
AZP_URL URL dell'istanza Azure DevOps o Azure DevOps Server specificata.
AZP_TOKEN Token di accesso personale con ambito pool di agenti (lettura, gestione), creato da un utente che dispone dell'autorizzazione per configurare gli agenti, all'indirizzo .
AZP_AGENT_NAME Nome agente (valore predefinito: nome host del contenitore).
AZP_POOL Nome del pool di agenti (valore predefinito: Default ).
AZP_WORK Directory di lavoro (valore predefinito: _work ).

Aggiungere strumenti e personalizzare il contenitore

È stato creato un agente di compilazione di base. È possibile estendere il Dockerfile per includere strumenti aggiuntivi e le relative dipendenze oppure creare un contenitore personalizzato usando questo come livello di base. È sufficiente assicurarsi che quanto segue non sia stato toccato:

  • Lo start.sh script viene chiamato dal Dockerfile.
  • Lo start.sh script è l'ultimo comando nel Dockerfile.
  • Assicurarsi che i contenitori derivati non rimuovono le dipendenze dichiarate dal Dockerfile.

Usare Docker all'interno di un contenitore Docker

Per usare Docker dall'interno di un contenitore Docker, è necessario associare e montare il socket Docker.

Attenzione

Questa operazione ha gravi implicazioni per la sicurezza. Il codice all'interno del contenitore può ora essere eseguito come radice nell'host Docker.

Se si è certi di voler eseguire questa operazione, vedere la documentazione relativa al montaggio delle associazioni Docker.com.

Usare servizio Azure Kubernetes cluster

Nota

Le istruzioni seguenti funzionano solo in AKS 1.18 e versione inferiore perché Docker è stato sostituito con il contenitore in Kubernetes 1.19 e Docker-in-Docker non è più disponibile.

Distribuire e configurare servizio Azure Kubernetes

Seguire la procedura descritta in Avvio rapido: Distribuire un cluster servizio Azure Kubernetes (AKS) usando ilportale di Azure . Successivamente, la console di PowerShell o Shell può usare la kubectl riga di comando.

Distribuire e configurare Registro Azure Container

Seguire la procedura descritta in Avvio rapido: Creare un registro Azure Container usando ilportale di Azure . Successivamente, è possibile eseguire il push e il pull dei contenitori Registro Azure Container.

Configurare i segreti e distribuire un set di repliche

  1. Creare i segreti nel cluster del servizio 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. Eseguire questo comando per eseguire il push del contenitore nel Registro Contenitori:

    docker push <acr-server>/dockeragent:latest
    
  3. Configurare l'integrazione del Registro Contenitori per i cluster del servizio Web Del servizio Container esistenti.

    az aks update -n myAKSCluster -g myResourceGroup --attach-acr <acr-name>
    
  4. Salvare il contenuto seguente in ~/AKS/ReplicationController.yaml :

    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>/dockeragent:latest
            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
    

    Questo YAML kubernetes crea un set di repliche e una distribuzione, in cui indica il numero o gli agenti in replicas: 1 esecuzione nel cluster.

  5. Eseguire questo comando:

    kubectl apply -f ReplicationController.yaml
    

A questo punto gli agenti eseguiranno il cluster del servizio Web Disatteso.

Impostare il parametro MTU personalizzato

Consente di specificare il valore MTU per le reti usate dai processi contenitore (utile per gli scenari docker-in-docker nel cluster k8s).

È necessario impostare la variabile di ambiente AGENT_MTU_VALUE impostare il valore MTU, dopo il riavvio dell'agente self-hosted. Altre informazioni sul riavvio dell'agente sono disponibili qui e sull'impostazione di variabili di ambiente diverse per ogni singolo agente.

In questo modo è possibile configurare un parametro di rete per il contenitore di processi. L'uso di questo comando è simile all'uso del comando successivo durante la configurazione di rete del contenitore:

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

Montaggio di volumi con Docker all'interno di un contenitore Docker

Se un contenitore Docker viene eseguito all'interno di un altro contenitore Docker, entrambi usano il daemon dell'host, quindi tutti i percorsi di montaggio fanno riferimento all'host, non al contenitore.

Ad esempio, se si vuole montare il percorso dall'host al contenitore Docker esterno, è possibile usare questo comando:

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

Se si vuole montare il percorso dall'host al contenitore Docker interno, è possibile usare questo comando:

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

Ma non è possibile montare i percorsi dal contenitore esterno a quello interno; Per risolvere questo problema, è necessario dichiarare una variabile ENV:

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

Successivamente, è possibile avviare il contenitore interno da quello esterno usando questo comando:

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

Errori comuni

Se si usa Windows e viene visualizzato l'errore seguente:

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

Installare Git Bash scaricando e installando git-scm.

Eseguire questo comando:

dos2unix ~/dockeragent/Dockerfile
dos2unix ~/dockeragent/start.sh
git add .
git commit -m 'Fixed CR'
git push

Riprovare. L'errore non viene più visualizzato.