在 Docker 中執行自我裝載式代理程式

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

本文提供在 Docker 中執行 Azure Pipelines 代理程式的指示。 您可以在 Azure Pipelines 中設定自我裝載代理程式,以在 Windows Server Core 內執行(適用于 Windows 主機),或使用 Docker 在 Ubuntu 容器中執行。 當您想要執行具有外部協調流程的代理程式時,例如 Azure 容器執行個體 ,這非常有用。 在本文中,您將逐步解說完整的容器範例,包括處理代理程式自我更新。

Windows Linux 都 支援作為容器主機。 Windows 容器應在 Windows vmImage 上執行。 若要在 Docker 中執行代理程式,您會將幾個 環境變數 傳遞至 docker run ,其會將代理程式設定為連線到 Azure Pipelines 或 Azure DevOps Server。 最後,您可以 自訂容器 以符合您的需求。 工作和腳本可能取決於容器 PATH 上可用的特定工具,而且您必須負責確保這些工具可供使用。

此功能需要代理程式 2.149 版或更新版本。 Azure DevOps 2019 未隨附相容的代理程式版本。 不過,如果您想要執行 Docker 代理程式,您可以將 正確的代理程式套件上傳至應用層

Windows

啟用 Hyper-V

根據預設,Windows 上不會啟用 Hyper-V。 如果您想要在容器之間提供隔離,您必須啟用 Hyper-V。 否則,適用于 Windows 的 Docker 將不會啟動。

注意

您必須在電腦上啟用虛擬化。 它通常預設為啟用。 不過,如果 Hyper-V 安裝失敗,請參閱您的系統檔,以瞭解如何啟用虛擬化。

安裝 Docker for Windows

如果您使用 Windows 10,您可以安裝 Docker Community Edition 。 若為 Windows Server 2016,請安裝 Docker Enterprise Edition

將 Docker 切換為使用 Windows 容器

根據預設,適用于 Windows 的 Docker 會設定為使用 Linux 容器。 若要允許執行 Windows 容器,請確認適用于 Windows 的 Docker 正在執行 Windows 精靈

建立並建置 Dockerfile

接下來,建立 Dockerfile。

  1. 開啟命令提示字元。

  2. 建立新的目錄:

    mkdir "C:\azp-agent-in-docker\"
    
  3. 移至這個新目錄:

    cd "C:\azp-agent-in-docker\"
    
  4. 將下列內容儲存至名為 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. 將下列內容儲存至 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. 在該目錄中執行下列命令:

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

    最後一個影像會標記 azp-agent:windows

啟動映射

現在您已建立映射,您可以執行容器。 這會安裝最新版的代理程式、設定並執行代理程式。 它會以您選擇的指定 Azure DevOps 或 Azure DevOps Server 實例的指定代理程式組件區(預設代理 Default 程式集區)為目標:

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

如果您遇到網路問題,您可能需要指定 --network 參數。

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

如果您想要停止容器,並使用 移除代理程式 C + Ctrl ,您可能需要指定 --interactive--tty 旗標(或只是 )。 -it

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

如果您想要每個管線作業的全新代理程式容器,請將 --once 旗標 傳遞 run 命令。

docker run < . . . > --once

--once使用 旗標,您可能會想要使用容器協調流程系統,例如 Kubernetes 或 Azure 容器執行個體 ,在作業完成時啟動容器的新複本。

您可以使用選擇性 環境變數 來控制代理程式名稱、代理程式組件區和代理程式工作目錄。

Linux

安裝 Docker

根據您的 Linux 散發套件,您可以安裝 Docker Community Edition Docker Enterprise Edition

建立並建置 Dockerfile

接下來,建立 Dockerfile。

  1. 開啟終端機。

  2. 建立新的目錄 (建議):

    mkdir ~/azp-agent-in-docker/
    
  3. 移至這個新目錄:

    cd ~/azp-agent-in-docker/
    
  4. 將下列內容儲存至 ~/azp-agent-in-docker/azp-agent-linux.dockerfile

    • 針對 Alpine:

      FROM alpine
      
      RUN apk update
      RUN apk upgrade
      RUN apk add bash curl git icu-libs jq
      
      ENV TARGETARCH="linux-musl-x64"
      
      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" ]
      
    • 針對 Ubuntu 22.04:

      FROM ubuntu:22.04
      
      RUN apt update -y && apt upgrade -y && apt install curl git jq libicu70 -y
      
      # Also can be "linux-arm", "linux-arm64".
      ENV TARGETARCH="linux-x64"
      
      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" ]
      

    如果您想要以 root 身分執行代理程式, agentENV AGENT_ALLOW_RUNASROOT="true" 取消批註該行,並移除在此行之前新增使用者。

    注意

    工作可能取決於容器預期提供的可執行檔。 例如,您必須將 和 unzip 套件新增 zipRUN apt install -y 命令,才能執行 ArchiveFilesExtractFiles 工作。 此外,由於這是 Linux Ubuntu 映射供代理程式使用,因此您可以視需要自訂映射。 例如:如果您需要建置 .NET 應用程式,您可以遵循在 Ubuntu 上安裝 .NET SDK 或 .NET 執行時間檔 ,並將該檔新增至映射。

  5. 將下列內容儲存至 ~/azp-agent-in-docker/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
    
    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 $!
    

    注意

    您也必須使用容器協調流程系統,例如 Kubernetes 或 Azure 容器執行個體 ,在工作完成時啟動容器的新複本。

  6. 在該目錄中執行下列命令:

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

    最後一個影像會標記 azp-agent:linux

啟動映射

現在您已建立映射,您可以執行容器。 這會安裝最新版的代理程式、設定並執行代理程式。 它會以您選擇的指定 Azure DevOps 或 Azure DevOps Server 實例的指定代理程式組件區(預設代理 Default 程式集區)為目標:

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

如果您想要停止容器,並使用 移除代理程式 C + Ctrl ,您可能需要指定 --interactive--tty 旗標(或只是 )。 -it

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

如果您想要每個管線作業的全新代理程式容器,請將 --once 旗標 傳遞 run 命令。

docker run < . . . > --once

--once使用 旗標,您可能會想要使用容器協調流程系統,例如 Kubernetes 或 Azure 容器執行個體 ,在作業完成時啟動容器的新複本。

您可以使用選擇性 環境變數 來控制代理程式名稱、代理程式組件區和代理程式工作目錄。

環境變數

環境變數 描述
AZP_URL Azure DevOps 或 Azure DevOps Server 實例的 URL。
AZP_TOKEN 具有 代理程式組件區的個人存取權杖(PAT) 範圍(讀取、管理) 範圍,由有權 AZP_URL 上設定代理程式 的使用者所建立。
AZP_AGENT_NAME 代理程式名稱(預設值:容器主機名稱)。
AZP_POOL 代理程式組件區名稱(預設值: Default )。
AZP_WORK 工作目錄(預設值: _work )。

新增工具和自訂容器

您已建立基本組建代理程式。 您可以擴充 Dockerfile 以包含其他工具和其相依性,或使用此容器作為基底層來建置您自己的容器。 請確定下列專案保持不變:

  • 腳本 start.sh 是由 Dockerfile 呼叫。
  • 腳本 start.sh 是 Dockerfile 中的最後一個命令。
  • 確定衍生容器不會移除 Dockerfile 所陳述的任何相依性。

在 Docker 容器內使用 Docker

若要從 Docker 容器內使用 Docker,您可以系結掛接 Docker 通訊端。

警告

這樣做具有嚴重的安全性影響。 容器內的程式碼現在可以在 Docker 主機上以根目錄執行。

如果您確定要這樣做,請參閱 Docker.com 上的系結掛接 檔。

使用 Azure Kubernetes Service 叢集

警告

請考慮任何 Docker 型工作都無法在 AKS 1.19 或更新版本上運作,因為 Docker 限制中的 Docker。 Docker 已取代為 Kubernetes 1.19 中的容器 ,而 Docker-in-Docker 變得無法使用。

部署和設定 Azure Kubernetes Service

請遵循快速入門:使用 Azure 入口網站 部署 Azure Kubernetes Service (AKS) 叢集中的步驟 。 之後,您的 PowerShell 或 Shell 主控台可以使用 kubectl 命令列。

部署及設定 Azure Container Registry

請遵循快速入門:使用 Azure 入口網站建立 Azure 容器登錄中的步驟。 之後,您可以從 Azure Container Registry 推送和提取容器。

設定秘密並部署複本集

  1. 在 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. 執行此命令將您的容器推送至 Container Registry:

    docker push "<acr-server>/azp-agent:<tag>"
    
  3. 設定現有 AKS 叢集的 Container Registry 整合。

    注意

    如果您在 Azure 入口網站上有多個訂用帳戶,請先使用此命令來選取訂用帳戶

    az account set --subscription "<subscription id or subscription name>"
    
    az aks update -n "<myAKSCluster>" -g "<myResourceGroup>" --attach-acr "<acr-name>"
    
  4. 將下列內容儲存至 ~/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
    

    此 Kubernetes YAML 會建立複本集和部署,其中 replicas: 1 表示在叢集上執行的號碼或代理程式。

  5. 執行此命令:

    kubectl apply -f ReplicationController.yml
    

現在您的代理程式會執行 AKS 叢集。

設定自訂 MTU 參數

允許為容器作業所使用的網路指定 MTU 值(適用于 k8s 叢集中的 docker-in-docker 案例)。

您必須設定環境變數AGENT_DOCKER_MTU_VALUE來設定 MTU 值,然後重新開機自我裝載代理程式。 您可以在這裡 找到有關代理程式重新開機 的詳細資訊,以及在這裡 為每個個別代理程式 設定不同的環境變數。

這可讓您設定作業容器的網路參數,使用此命令類似于在容器網路設定時使用下一個命令:

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

在 Docker 容器中使用 Docker 掛接磁片區

如果 Docker 容器在另一個 Docker 容器內執行,它們都會使用主機的精靈,因此所有掛接路徑都會參考主機,而不是容器。

例如,如果我們想要將路徑從主機掛接至外部 Docker 容器,我們可以使用此命令:

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

如果我們想要將路徑從主機掛接至內部 Docker 容器,則可以使用此命令:

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

但是,我們無法將外部容器的路徑掛接至內部容器;若要解決此問題,我們必須宣告 ENV 變數:

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

在此之後,我們可以使用下列命令,從外部容器啟動內部容器:

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

常見錯誤

如果您使用 Windows,並收到下列錯誤:

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

下載並安裝 git-scm 以安裝 Git Bash。

執行此命令:

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

然後再試一次。 您不再收到錯誤。