Menjalankan agen yang dihost sendiri di Docker

Azure DevOps | Azure DevOps Server 2020 | Azure DevOps Server 2019

Artikel ini menyediakan instruksi untuk menjalankan agen Azure Pipelines Anda di Docker. Anda dapat menyiapkan agen yang dihost sendiri di Azure Pipelines untuk berjalan di dalam Windows Server Core (untuk host Windows), atau kontainer Ubuntu (untuk host Linux) dengan Docker. Ini berguna ketika Anda ingin menjalankan agen dengan orkestrasi luar, seperti Azure Container Instances. Dalam artikel ini, Anda akan menelusuri contoh kontainer lengkap, termasuk menangani pembaruan mandiri agen.

Windows dan Linux didukung sebagai host kontainer. Kontainer Windows harus berjalan pada Windows vmImage. Untuk menjalankan agen Anda di Docker, Anda akan meneruskan beberapa variabel lingkungan ke docker run, yang mengonfigurasi agen untuk terhubung ke Azure Pipelines atau Azure DevOps Server. Terakhir, Anda menyesuaikan kontainer agar sesuai dengan kebutuhan Anda. Tugas dan skrip mungkin bergantung pada alat tertentu yang tersedia di kontainer PATH, dan Anda bertanggung jawab untuk memastikan bahwa alat-alat ini tersedia.

Fitur ini memerlukan agen versi 2.149 atau yang lebih baru. Azure DevOps 2019 tidak dikirim dengan versi agen yang kompatibel. Namun, Anda dapat mengunggah paket agen yang benar ke tingkat aplikasi Jika Anda ingin menjalankan agen Docker.

Windows

Mengaktifkan Hyper-V

Hyper-V tidak diaktifkan secara default di Windows. Jika Anda ingin memberikan isolasi antar kontainer, Anda harus mengaktifkan Hyper-V. Jika tidak, Docker untuk Windows tidak akan dimulai.

Catatan

Anda harus mengaktifkan virtualisasi pada komputer Anda. Ini biasanya diaktifkan secara default. Namun, jika penginstalan Hyper-V gagal, lihat dokumentasi sistem Anda tentang cara mengaktifkan virtualisasi.

Menginstal Docker untuk Windows

Jika Anda menggunakan Windows 10, Anda dapat menginstal Docker Community Edition. Untuk Windows Server 2016, instal Docker Enterprise Edition.

Beralih Docker untuk menggunakan kontainer Windows

Secara default, Docker untuk Windows dikonfigurasi untuk menggunakan kontainer Linux. Untuk memperbolehkan menjalankan kontainer Windows, konfirmasikan bahwa Docker untuk Windows menjalankan daemon Windows.

Membuat dan membangun Dockerfile

Selanjutnya, buat Dockerfile.

  1. Buka prompt perintah.

  2. Buat direktori baru:

    mkdir C:\dockeragent
    
  3. Ubah direktori ke direktori baru ini:

    cd C:\dockeragent
    
  4. Simpan konten berikut ke file yang disebut C:\dockeragent\Dockerfile (tidak ada ekstensi file):

    FROM mcr.microsoft.com/windows/servercore:ltsc2019
    
    WORKDIR /azp
    
    COPY start.ps1 .
    
    CMD powershell .\start.ps1
    
  5. Simpan konten berikut ke 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})"
    }
    
  6. Jalankan perintah berikut dalam direktori tersebut:

    docker build -t dockeragent:latest .
    

    Perintah ini membangun Dockerfile di direktori saat ini.

    Gambar akhir ditandai dockeragent:latest. Anda dapat dengan mudah menjalankannya dalam kontainer sebagai dockeragent, karena latest tag adalah default jika tidak ada tag yang ditentukan.

Memulai gambar

Setelah membuat gambar, Anda dapat menjalankan kontainer.

  1. Buka prompt perintah.

  2. Jalankan kontainer. Ini menginstal versi terbaru agen, mengonfigurasinya, dan menjalankan agen. Ini menargetkan kumpulan Default instans Azure DevOps atau Azure DevOps Server pilihan Anda yang ditentukan:

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

Secara opsional, Anda dapat mengontrol kumpulan dan direktori kerja agen dengan menggunakan variabel lingkungan tambahan.

Jika Anda menginginkan kontainer agen baru untuk setiap eksekusi alur, teruskan --once bendera ke run perintah . Anda juga harus menggunakan sistem orkestrasi kontainer, seperti Kubernetes atau Azure Container Instances, untuk memulai salinan baru kontainer saat pekerjaan selesai.

Linux

Memasang Docker

Bergantung pada Distribusi Linux, Anda dapat menginstal Docker Community Edition atau Docker Enterprise Edition.

Membuat dan membangun Dockerfile

Selanjutnya, buat Dockerfile.

  1. Buka terminal.

  2. Buat direktori baru (disarankan):

    mkdir ~/dockeragent
    
  3. Ubah direktori ke direktori baru ini:

    cd ~/dockeragent
    
  4. Simpan konten berikut ke ~/dockeragent/Dockerfile:

    • Untuk 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" ]
      
    • Untuk 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"]
      

    Catatan

    Tugas mungkin bergantung pada executable yang diharapkan untuk disediakan kontainer Anda. Misalnya, Anda harus menambahkan zip paket dan unzip ke RUN apt-get perintah untuk menjalankan ArchiveFiles tugas dan ExtractFiles . Selain itu, karena ini adalah gambar Ubuntu Linux untuk digunakan agen, Anda dapat menyesuaikan gambar sesuai kebutuhan Anda. Misalnya: jika Anda perlu membangun aplikasi .NET, Anda dapat mengikuti dokumen Instal .NET SDK atau .NET Runtime di Ubuntu dan tambahkan itu ke gambar Anda.

  5. Simpan konten berikut ke ~/dockeragent/start.sh, pastikan untuk menggunakan akhiran baris gaya 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
    
    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-docker.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-docker.sh "$@" & wait $!
    

    Catatan

    Anda juga harus menggunakan sistem orkestrasi kontainer, seperti Kubernetes atau Azure Container Instances, untuk memulai salinan baru kontainer saat pekerjaan selesai.

  6. Jalankan perintah berikut dalam direktori tersebut:

    docker build -t dockeragent:latest .
    

    Perintah ini membangun Dockerfile di direktori saat ini.

    Gambar akhir ditandai dockeragent:latest. Anda dapat dengan mudah menjalankannya dalam kontainer sebagai dockeragent, karena latest tag adalah default jika tidak ada tag yang ditentukan.

Memulai gambar

Setelah membuat gambar, Anda dapat menjalankan kontainer.

  1. Buka terminal.

  2. Jalankan kontainer. Ini menginstal versi terbaru agen, mengonfigurasinya, dan menjalankan agen. Ini menargetkan kumpulan Default instans Azure DevOps atau Azure DevOps Server pilihan Anda yang ditentukan:

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

    Jika Anda menginginkan kontainer agen baru untuk setiap pekerjaan alur, teruskan --once bendera ke run perintah .

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

Secara opsional, Anda dapat mengontrol kumpulan dan direktori kerja agen dengan menggunakan variabel lingkungan tambahan.

Variabel lingkungan

Variabel lingkungan Deskripsi
AZP_URL URL instans Azure DevOps atau Azure DevOps Server.
AZP_TOKEN Token Akses Pribadi (PAT) dengan cakupan Kumpulan Agen (baca, kelola), dibuat oleh pengguna yang memiliki izin untuk mengonfigurasi agen, di AZP_URL.
AZP_AGENT_NAME Nama agen (nilai default: nama host kontainer).
AZP_POOL Nama kumpulan agen (nilai default: Default).
AZP_WORK Direktori kerja (nilai default: _work).

Menambahkan alat dan menyesuaikan kontainer

Anda telah membuat agen build dasar. Anda dapat memperluas Dockerfile untuk menyertakan alat tambahan dan dependensinya, atau membangun kontainer Anda sendiri dengan menggunakan yang ini sebagai lapisan dasar. Pastikan bahwa berikut ini tidak tersentuh:

  • start.sh Skrip dipanggil oleh Dockerfile.
  • start.sh Skrip adalah perintah terakhir di Dockerfile.
  • Pastikan bahwa kontainer turunan tidak menghapus salah satu dependensi yang dinyatakan oleh Dockerfile.

Menggunakan Docker dalam kontainer Docker

Untuk menggunakan Docker dari dalam kontainer Docker, Anda mengikat-mount soket Docker.

Perhatian

Melakukan ini memiliki implikasi keamanan yang serius. Kode di dalam kontainer sekarang dapat berjalan sebagai root pada host Docker Anda.

Jika Anda yakin ingin melakukan ini, lihat dokumentasi pemasangan ikatan di Docker.com.

Menggunakan kluster Azure Kubernetes Service

Catatan

Instruksi berikut hanya berfungsi pada AKS 1.18 dan yang lebih rendah karena Docker diganti dengan kontainer di Kubernetes 1.19, dan Docker-in-Docker menjadi tidak tersedia.

Menyebarkan dan mengonfigurasi Azure Kubernetes Service

Ikuti langkah-langkah dalam Mulai Cepat: Menyebarkan kluster Azure Kubernetes Service (AKS) dengan menggunakan portal Microsoft Azure. Setelah ini, konsol PowerShell atau Shell Anda dapat menggunakan kubectl baris perintah.

Menyebarkan dan mengonfigurasi Azure Container Registry

Ikuti langkah-langkah di Mulai Cepat: Membuat registri kontainer Azure dengan menggunakan portal Microsoft Azure. Setelah ini, Anda dapat mendorong dan menarik kontainer dari Azure Container Registry.

Mengonfigurasi rahasia dan menyebarkan set replika

  1. Buat rahasia pada kluster 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. Jalankan perintah ini untuk mendorong kontainer Anda ke Container Registry:

    docker push <acr-server>/dockeragent:latest
    
  3. Konfigurasikan integrasi Container Registry untuk kluster AKS yang ada.

    az aks update -n myAKSCluster -g myResourceGroup --attach-acr <acr-name>
    
  4. Simpan konten berikut ke ~/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
    

    YAML Kubernetes ini membuat set replika dan penyebaran, di mana replicas: 1 menunjukkan jumlah atau agen yang berjalan pada kluster.

  5. Jalankan perintah ini:

    kubectl apply -f ReplicationController.yaml
    

Sekarang agen Anda akan menjalankan kluster AKS.

Mengatur parameter MTU kustom

Izinkan menentukan nilai MTU untuk jaringan yang digunakan oleh pekerjaan kontainer (berguna untuk skenario docker-in-docker di kluster k8s).

Anda perlu mengatur variabel lingkungan AGENT_MTU_VALUE untuk mengatur nilai MTU, setelah itu mulai ulang agen yang dihost sendiri. Anda dapat menemukan lebih lanjut tentang mulai ulang agen di sini dan tentang mengatur variabel lingkungan yang berbeda untuk setiap agen individu di sini.

Ini memungkinkan Anda menyiapkan parameter jaringan untuk kontainer pekerjaan, penggunaan perintah ini mirip dengan penggunaan perintah berikutnya saat konfigurasi jaringan kontainer:

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

Memasang volume menggunakan Docker dalam kontainer Docker

Jika kontainer Docker berjalan di dalam kontainer Docker lain, keduanya menggunakan daemon host, sehingga semua jalur pemasangan mereferensikan host, bukan kontainer.

Misalnya, jika kita ingin memasang jalur dari host ke kontainer Docker luar, kita dapat menggunakan perintah ini:

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

Dan jika kita ingin memasang jalur dari host ke kontainer Docker dalam, kita dapat menggunakan perintah ini:

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

Tetapi kita tidak dapat memasang jalur dari kontainer luar ke dalam; untuk mengatasinya, kita harus mendeklarasikan variabel ENV:

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

Setelah ini, kita dapat memulai kontainer dalam dari yang luar menggunakan perintah ini:

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

Kesalahan umum

Jika Anda menggunakan Windows, dan Anda mendapatkan kesalahan berikut:

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

Instal Git Bash dengan mengunduh dan menginstal git-scm.

Jalankan perintah ini:

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

Coba lagi. Anda tidak lagi mendapatkan kesalahan.