在 Docker 中运行自托管代理
Azure DevOps Services | Azure DevOps Server 2022 - Azure DevOps Server 2019
本文提供有关在 Docker 中运行 Azure Pipelines 代理的说明。 可以在 Azure Pipelines 中设置一个自承载代理,以便在 Windows 主机) 的 Windows Server Core (中运行,或使用 Docker) 适用于 Linux 主机的 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 容器,请确认 Docker for Windows 正在运行 Windows 守护程序。
创建和生成 Dockerfile
接下来,创建 Dockerfile。
打开命令提示符。
创建新目录:
mkdir C:\dockeragent
将目录更改为此新目录:
cd C:\dockeragent
将以下内容保存到名为
C:\dockeragent\Dockerfile
(无文件扩展名) 的文件:FROM mcr.microsoft.com/windows/servercore:ltsc2019 WORKDIR /azp COPY start.ps1 . CMD powershell .\start.ps1
将以下内容保存到
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 { 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 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})" }
在该目录中运行以下命令:
docker build -t dockeragent:latest .
此命令在当前目录中生成 Dockerfile。
最终图像标记为
dockeragent:latest
。 可以轻松地将其作为dockeragent
在容器中运行,因为如果未指定标记,latest
则标记是默认标记。
启动映像
创建映像后,可以运行容器。
打开命令提示符。
运行容器。 这会安装最新版本的代理,对其进行配置并运行代理。 它面向
Default
所选的指定 Azure DevOps 或Azure DevOps Server实例的池:docker run -e AZP_URL=<Azure DevOps instance> -e AZP_TOKEN=<PAT token> -e AZP_AGENT_NAME=mydockeragent dockeragent:latest
(可选)可以使用其他 环境变量来控制池和代理工作目录。
如果需要每次管道运行都有一个新的代理容器,请将 --once
标志 传递给 run
命令。
还必须使用容器业务流程系统(如 Kubernetes 或 Azure 容器实例)在工作完成时启动容器的新副本。
Linux
安装 Docker
根据 Linux 分发版,可以安装 Docker Community Edition 或 Docker Enterprise Edition。
创建和生成 Dockerfile
接下来,创建 Dockerfile。
打开终端。
创建新目录(推荐):
mkdir ~/dockeragent
将目录更改为此新目录:
cd ~/dockeragent
将以下内容保存到
~/dockeragent/Dockerfile
:- 对于 Ubuntu 20.04:
FROM ubuntu:20.04 RUN DEBIAN_FRONTEND=noninteractive apt-get update RUN DEBIAN_FRONTEND=noninteractive apt-get upgrade -y RUN DEBIAN_FRONTEND=noninteractive apt-get install -y -qq --no-install-recommends \ apt-transport-https \ apt-utils \ ca-certificates \ curl \ git \ iputils-ping \ jq \ lsb-release \ software-properties-common RUN curl -sL https://aka.ms/InstallAzureCLIDeb | bash # Can be 'linux-x64', 'linux-arm64', 'linux-arm', 'rhel.6-x64'. ENV TARGETARCH=linux-x64 WORKDIR /azp COPY ./start.sh . RUN chmod +x start.sh ENTRYPOINT [ "./start.sh" ]
- 对于 Ubuntu 18.04:
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/* # Can be 'linux-x64', 'linux-arm64', 'linux-arm', 'rhel.6-x64'. ENV TARGETARCH=linux-x64 WORKDIR /azp COPY ./start.sh . RUN chmod +x start.sh ENTRYPOINT ["./start.sh"]
注意
任务可能依赖于容器应提供的可执行文件。 例如,必须将 和
unzip
包添加到zip
RUN apt-get
命令才能运行ArchiveFiles
和ExtractFiles
任务。 此外,由于这是供代理使用的 Linux Ubuntu 映像,因此可以根据需要自定义该映像。 例如:如果需要生成 .NET 应用程序,可以按照文档 在 Ubuntu 上安装 .NET SDK 或 .NET 运行时 并将其添加到映像。- 对于 Ubuntu 20.04:
将以下内容保存到
~/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 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..." trap 'cleanup; exit 0' EXIT trap 'cleanup; exit 130' INT trap 'cleanup; exit 143' TERM 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 容器实例)在工作完成时启动容器的新副本。
在该目录中运行以下命令:
docker build -t dockeragent:latest .
此命令在当前目录中生成 Dockerfile。
最终图像标记为
dockeragent:latest
。 可以轻松地将其作为dockeragent
在容器中运行,因为如果未指定标记,latest
则标记是默认标记。
启动映像
创建映像后,可以运行容器。
打开终端。
运行容器。 这会安装最新版本的代理,对其进行配置并运行代理。 它面向
Default
所选的指定 Azure DevOps 或Azure DevOps Server实例的池:docker run -e AZP_URL=<Azure DevOps instance> -e AZP_TOKEN=<PAT token> -e AZP_AGENT_NAME=mydockeragent dockeragent:latest
如果需要为每个管道作业使用一个新的代理容器,请将
--once
标志 传递给run
命令。docker run -e AZP_URL=<Azure DevOps instance> -e AZP_TOKEN=<PAT token> -e AZP_AGENT_NAME=mydockeragent dockeragent:latest --once
(可选)可以使用其他 环境变量来控制池和代理工作目录。
环境变量
环境变量 | 说明 |
---|---|
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 主机上以 root 身份运行。
如果确定要执行此操作,请参阅 Docker.com 上的 绑定装载 文档。
使用 Azure Kubernetes 服务 群集
注意
请注意,由于 docker 中的 docker 限制,任何基于 docker 的任务将无法在 AKS 1.19 或更早版本上运行。 Docker 在 Kubernetes 1.19 中替换为容器化,Docker-in-Docker 变得不可用。
部署和配置Azure Kubernetes 服务
按照快速入门:使用 Azure 门户部署 Azure Kubernetes 服务 (AKS) 群集中的步骤操作。 在此之后,PowerShell 或 Shell 控制台可以使用 kubectl
命令行。
部署和配置Azure 容器注册表
按照快速入门:使用 Azure 门户创建 Azure 容器注册表中的步骤操作。 之后,可以从Azure 容器注册表推送和拉取容器。
配置机密并部署副本集
在 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
运行以下命令,将容器推送到容器注册表:
docker push <acr-server>/dockeragent:latest
为现有 AKS 群集配置容器注册表集成。
注意
如果在 Azure 门户中有多个订阅,请先使用此命令选择一个订阅
az account set --subscription <subscription id or >subscription name>
az aks update -n myAKSCluster -g myResourceGroup --attach-acr <acr-name>
将以下内容保存到
~/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
此 Kubernetes YAML 创建一个副本集和一个部署,其中
replicas: 1
指示群集上运行的代理数或数量。运行以下命令:
kubectl apply -f ReplicationController.yaml
现在,代理将运行 AKS 群集。
设置自定义 MTU 参数
允许为容器作业使用的网络指定 MTU 值 (适用于 k8s 群集) 中的 docker-in-docker 方案。
重启自承载代理后,需要设置环境变量AGENT_MTU_VALUE来设置 MTU 值。 可 在此处 找到有关代理重启以及为每个代理设置不同环境变量 的详细信息。
这允许你为作业容器设置网络参数,此命令的使用类似于在容器网络配置时使用下一个命令:
-o com.docker.network.driver.mtu=AGENT_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 ~/dockeragent/Dockerfile
dos2unix ~/dockeragent/start.sh
git add .
git commit -m 'Fixed CR'
git push
重试。 不再收到错误。
相关文章
- Self-hosted Windows agents(自托管 Windows 代理)
- Self-hosted Linux agents(自托管 Linux 代理)
- Self-hosted macOS agents(自托管 macOS 代理)
- Microsoft 托管代理