Docker의 자체 호스팅 에이전트 실행
Azure DevOps Services | Azure DevOps Server 2022 - Azure DevOps Server 2019
이 문서에서는 Docker에서 Azure Pipelines 에이전트를 실행하기 위한 지침을 제공합니다. Azure Pipelines에서 자체 호스팅 에이전트를 설정하여 Docker를 사용하여 Windows Server Core(Windows 호스트용) 또는 Ubuntu 컨테이너(Linux 호스트용) 내에서 실행할 수 있습니다. 이 기능은 Azure Container Instances와 같은 외부 오케스트레이션을 사용하여 에이전트를 실행하려는 경우에 유용합니다. 이 문서에서는 에이전트 자체 업데이트 처리를 포함하여 전체 컨테이너 예제를 안내합니다.
Windows와 Linux는 모두 컨테이너 호스트로 지원됩니다. Windows 컨테이너는 Windows vmImage
에서 실행되어야 합니다.
Docker에서 에이전트를 실행하려면 Azure Pipelines 또는 Azure DevOps Server에 docker run
연결하도록 에이전트를 구성하는 몇 가지 환경 변수를 전달합니다. 마지막으로 필요에 맞게 컨테이너 를 사용자 지정합니다. 작업 및 스크립트는 컨테이너 PATH
에서 사용할 수 있는 특정 도구에 따라 달라질 수 있으며 이러한 도구를 사용할 수 있는지 확인해야 합니다.
이 기능을 사용하려면 에이전트 버전 2.149 이상이 필요합니다. Azure DevOps 2019는 호환되는 에이전트 버전과 함께 제공되지 않았습니다. 그러나 Docker 에이전트를 실행하려는 경우 올바른 에이전트 패키지를 애플리케이션 계층에 업로드할 수 있습니다.
Windows
Hyper-V 사용
Hyper-V는 Windows에서 기본적으로 사용하도록 설정되지 않습니다. 컨테이너 간에 격리를 제공하려면 Hyper-V를 사용하도록 설정해야 합니다. 그렇지 않으면 Windows용 Docker가 시작되지 않습니다.
참고 항목
머신에서 가상화를 사용하도록 설정해야 합니다. 일반적으로 기본적으로 사용하도록 설정됩니다. 그러나 Hyper-V 설치가 실패하는 경우 가상화를 사용하도록 설정하는 방법에 대한 시스템 설명서를 참조하세요.
Windows용 Docker 설치
Windows 10을 사용하는 경우 Docker Community Edition을 설치할 수 있습니다. Windows Server 2016의 경우 Docker Enterprise Edition을 설치합니다.
Windows 컨테이너를 사용하도록 Docker 전환
기본적으로 Windows용 Docker는 Linux 컨테이너를 사용하도록 구성됩니다. Windows 컨테이너 실행을 허용하려면 Windows용 Docker에서 Windows 디먼을 실행하고 있는지 확인합니다.
Dockerfile 만들기 및 빌드
다음으로 Dockerfile을 만듭니다.
명령 프롬프트가 엽니다.
새 디렉터리를 만듭니다.
mkdir "C:\azp-agent-in-docker\"
이 새 디렉터리로 이동합니다.
cd "C:\azp-agent-in-docker\"
다음 콘텐츠를 라는
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
다음 콘텐츠를 다음과 같이 저장합니다
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})" }
해당 디렉터리 내에서 다음 명령을 실행합니다.
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
를 제거할 수 있도록 하려면 지정하고 --tty
플래그를 지정 --interactive
해야 할 수도 있습니다(또는 간단히-it
).
docker run --interactive --tty < . . . >
모든 파이프라인 작업에 대한 새 에이전트 컨테이너를 원하는 경우 명령에 플래그를 전달 --once
합니다run
.
docker run < . . . > --once
플래그를 --once
사용하면 Kubernetes 또는 Azure Container Instances와 같은 컨테이너 오케스트레이션 시스템을 사용하여 작업이 완료되면 컨테이너의 새 복사본을 시작할 수 있습니다.
선택적 환경 변수를 사용하여 에이전트 이름, 에이전트 풀 및 에이전트 작업 디렉터리를 제어할 수 있습니다.
Linux
Docker 설치
Linux 배포판에 따라 Docker Community Edition 또는 Docker Enterprise Edition을 설치할 수 있습니다.
Dockerfile 만들기 및 빌드
다음으로 Dockerfile을 만듭니다.
터미널을 엽니다.
새 디렉터리를 만듭니다(권장).
mkdir ~/azp-agent-in-docker/
이 새 디렉터리로 이동합니다.
cd ~/azp-agent-in-docker/
다음 콘텐츠를 다음과 같이 저장합니다
~/azp-agent-in-docker/azp-agent-linux.dockerfile
.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" ]
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" ]
에이전트를 루트로 실행하려면 줄의
ENV AGENT_ALLOW_RUNASROOT="true"
주석 처리를 제거하고 이 줄 앞에 사용자를 추가agent
하지 않습니다.참고 항목
태스크는 컨테이너에서 제공해야 하는 실행 파일에 따라 달라질 수 있습니다. 예를 들어 명령 및 태스크를
zip
RUN apt install -y
실행ArchiveFiles
하려면 명령과unzip
ExtractFiles
패키지를 추가해야 합니다. 또한 에이전트가 사용할 Linux Ubuntu 이미지이므로 필요에 따라 이미지를 사용자 지정할 수 있습니다. 예: .NET 애플리케이션을 빌드해야 하는 경우 Ubuntu에 .NET SDK 또는 .NET 런타임 설치 문서를 따라 이미지에 추가할 수 있습니다.다음 콘텐츠를 저장하여
~/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 Container Instances와 같은 컨테이너 오케스트레이션 시스템을 사용하여 작업이 완료되면 컨테이너의 새 복사본을 시작해야 합니다.
해당 디렉터리 내에서 다음 명령을 실행합니다.
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
를 제거할 수 있도록 하려면 지정하고 --tty
플래그를 지정 --interactive
해야 할 수도 있습니다(또는 간단히-it
).
docker run --interactive --tty < . . . >
모든 파이프라인 작업에 대한 새 에이전트 컨테이너를 원하는 경우 명령에 플래그를 전달 --once
합니다run
.
docker run < . . . > --once
플래그를 --once
사용하면 Kubernetes 또는 Azure Container Instances와 같은 컨테이너 오케스트레이션 시스템을 사용하여 작업이 완료되면 컨테이너의 새 복사본을 시작할 수 있습니다.
선택적 환경 변수를 사용하여 에이전트 이름, 에이전트 풀 및 에이전트 작업 디렉터리를 제어할 수 있습니다.
환경 변수
환경 변수 | 설명 |
---|---|
AZP_URL | Azure DevOps 또는 Azure DevOps Server 인스턴스의 URL입니다. |
AZP_TOKEN | 에이전트를 구성할 AZP_URL 수 있는 권한이 있는 사용자가 만든 에이전트 풀(읽기, 관리) 범위가 있는 PAT(개인 액세스 토큰). |
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 제한의 Docker로 인해 AKS 1.19 이상에서는 Docker 기반 작업이 작동하지 않는다는 점을 고려하세요. Docker가 Kubernetes 1.19에서 컨테이너로 대체되었고 Docker-in-Docker를 사용할 수 없게 되었습니다.
Azure Kubernetes Service 배포 및 구성
빠른 시작의 단계를 수행합니다. Azure Portal을 사용하여 AKS(Azure Kubernetes Service) 클러스터를 배포합니다. 그런 다음 PowerShell 또는 셸 콘솔에서 명령줄을 kubectl
사용할 수 있습니다.
Azure Container Registry 배포 및 구성
빠른 시작: Azure Portal을 사용하여 Azure Container Registry에서 새 레지스트리 만들기의 단계를 수행합니다. 그런 다음 Azure Container Registry에서 컨테이너를 푸시하고 끌어올 수 있습니다.
비밀 구성 및 복제본 집합 배포
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>/azp-agent:<tag>"
기존 AKS 클러스터에 대한 Container Registry 통합을 구성합니다.
참고 항목
Azure Portal에 여러 구독이 있는 경우 먼저 이 명령을 사용하여 구독을 선택하세요.
az account set --subscription "<subscription id or subscription name>"
az aks update -n "<myAKSCluster>" -g "<myResourceGroup>" --attach-acr "<acr-name>"
다음 콘텐츠를 다음과 같이 저장합니다
~/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
를 만듭니다.다음 명령을 실행합니다.
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
다시 시도하세요. 더 이상 오류가 발생하지 않습니다.