Docker でセルフホステッド エージェントを実行する
Azure DevOps Services | Azure DevOps Server 2022 - Azure DevOps Server 2019
この記事では、Docker で Azure Pipelines エージェントを実行する手順について説明します。 Docker を使って Windows Server Core (Windows ホストの場合) または Ubuntu コンテナー (Linux ホストの場合) 内で実行するセルフホステッド エージェントを Azure Pipelines に設定できます。 これは、Azure Container Instances などの外部オーケストレーションを使ってエージェントを実行する場合に便利です。 この記事では、エージェントの自己更新の処理など、完全なコンテナーの例について説明します。
Windows と Linux の両方がコンテナー ホストとしてサポートされています。 Windows コンテナーは Windows vmImage
で実行する必要があります。
Docker でエージェントを実行するには、いくつかの環境変数を docker run
に渡します。これにより、Azure Pipelines または Azure DevOps Server に接続するようにエージェントが構成されます。 最後に、ニーズに合わせてコンテナーをカスタマイズします。 タスクとスクリプトによっては、コンテナーの PATH
で使用できる特定のツールに依存する場合があり、これらのツールが使用可能であることを確認するのはユーザーの責任です。
この機能には、エージェント バージョン 2.149 以降が必要です。 Azure DevOps 2019 には、互換性のあるエージェント バージョンが付属していませんでした。 ただし、Docker エージェントを実行する場合は、適切なエージェント パッケージをアプリケーション層にアップロードできます。
Windows
Hyper-V の有効化
Hyper-V は、Windows では既定では有効になっていません。 コンテナー間の分離を提供する場合は、Hyper-V を有効にする必要があります。 それ以外の場合、Docker for Windows は起動しません。
注意
マシンで仮想化を有効にする必要があります。 これは通常、既定で有効になっています。 ただし、Hyper-V のインストールが失敗する場合は、システムのドキュメントで仮想化を有効にする方法を参照してください。
Docker for Windows のインストール
Windows 10を使っている場合は、Docker Community Edition をインストールできます。 Windows Server 2016 の場合は、Docker Enterprise Edition をインストールします。
Windows コンテナーを使用するように Docker を切り替える
既定では、Docker for Windows は Linux コンテナーを使用するように構成されています。 Windows コンテナーの実行を許可するには、Docker for Windows が 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" < . . . >
Ctrl
+ C
キーを使用して、コンテナーを停止してエージェントを削除できるようにする場合は、--interactive
と--tty
フラグ (または単に-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
ユーザーを追加して削除します。Note
タスクは、コンテナーが提供すると予想される実行可能ファイルに依存する場合があります。 たとえば、
ArchiveFiles
およびExtractFiles
タスクを実行するために、zip
およびunzip
パッケージをRUN apt install -y
コマンドに追加する必要があります。 また、これはエージェントが使用する Linux Ubuntu イメージであるため、必要に応じてイメージをカスタマイズできます。 例: .NET アプリケーションをビルドする必要がある場合は、「Ubuntu に .NET SDK または .NET ランタイムをインストールする」のドキュメントに従って、これをイメージに追加できます。必ず Unix スタイル (LF) の改行コードを使って、次の内容を
~/azp-agent-in-docker/start.sh
に保存します。#!/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
Ctrl
+ C
キーを使用して、コンテナーを停止してエージェントを削除できるようにする場合は、--interactive
と--tty
フラグ (または単に-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 | エージェント プール (読み取り、管理) スコープを伴う個人用アクセス トークン (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 in docker の制限により、Docker ベースのタスクは AKS 1.19 以降では機能しないことに注意してください。 Docker は Kubernetes 1.19 にコンテナー化されたものに置き換えられ、Docker-in-Docker は使用できなくなりました。
Azure Kubernetes Service をデプロイし構成する
「クイック スタート: Azure portal を使用して Azure Kubernetes Service (AKS) クラスターをデプロイする」の手順に従います。 その後、PowerShell または Shell コンソールで kubectl
コマンド ラインを使用できます。
Azure Container Registry をデプロイし構成する
「クイック スタート: Azure portal を使用して Azure コンテナー レジストリを作成する」の手順に従います。 その後、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
このコマンドを実行して、コンテナーを Container Registry にプッシュします。
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
やり直してください。 エラーは表示されなくなります。