Definieren von Containeraufträgen (YAML)

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

Standardmäßig werden Aufträge auf dem Hostcomputer ausgeführt, auf dem der Agent installiert ist. Dies ist praktisch und eignet sich in der Regel gut für Projekte, bei denen gerade erst mit der Einführung von Azure Pipelines begonnen wird. Im Laufe der Zeit stellen Sie möglicherweise fest, dass Sie mehr Kontrolle über den Kontext wünschen, in dem Ihre Aufgaben ausgeführt werden. YAML-Pipelines bieten für diesen Grad an Kontrolle Containeraufträge.

Bei Linux- und Windows-Agents können Aufträge auf dem Host oder in einem Container ausgeführt werden. (Unter macOS und Red Hat Enterprise Linux 6 sind keine Containeraufträge verfügbar.) Container bieten Isolation vom Host und ermöglichen das Anheften bestimmter Versionen von Tools und Abhängigkeiten. Hostaufträge erfordern weniger anfängliche Einrichtung und zu wartende Infrastruktur.

Container bieten eine schlanke Abstraktion des Hostbetriebssystems. Sie können die genauen Versionen von Betriebssystemen, Tools und Abhängigkeiten auswählen, die für Ihren Build erforderlich sind. Wenn Sie in Ihrer Pipeline einen Container angeben, ruft der Agent zuerst den Container ab und startet ihn dann. Anschließend wird jeder Schritt des Auftrags innerhalb des Containers ausgeführt. Geschachtelte Container sind nicht möglich. Container werden nicht unterstützt, wenn ein Agent bereits in einem Container ausgeführt wird.

Wenn Sie eine differenzierte Steuerung auf der Ebene der einzelnen Schritte benötigen, können Sie mit Schrittzielen den Container oder Host für jeden Schritt auswählen.

Anforderungen

Linux-Container

Für das Azure Pipelines-System müssen in Linux-Containern einige Anforderungen erfüllt sein:

  • Bash
  • glibc-basiert
  • Ausführen von Node.js (das der Agent bereitstellt) möglich
  • Definiert keinen ENTRYPOINT
  • USER hat Zugriff auf groupadd und andere Berechtigungsbefehle ohne sudo

Und auf Ihrem Agent-Host gilt Folgendes:

  • Docker muss installiert sein.
  • Der Agent muss über die Berechtigung für Zugriff auf den Docker-Daemon verfügen.

Stellen Sie sicher, dass für Ihren Container jedes dieser Tools verfügbar ist. Einige der auf Docker Hub verfügbaren funktionsreduzierten Container, insbesondere diejenigen, die auf Alpine Linux basieren, erfüllen diese Mindestanforderungen nicht. Container mit ENTRYPOINT funktionieren möglicherweise nicht, da Azure Pipelines mit docker create einen wartenden Container erstellt und mit docker exec eine Reihe von Befehlen ausführt, die erwarten, dass der Container stets in Betrieb ist.

Hinweis

Für Windows-basierte Linux-Container muss Node.js vorinstalliert sein.

Windows-Container

Azure Pipelines kann auch Windows-Container ausführen. Die Windows Server-Version 1803 ist mindestens erforderlich. Docker muss installiert sein. Stellen Sie sicher, dass Ihr Pipelines-Agent über die Berechtigung für den Zugriff auf den Docker-Daemon verfügt.

Der Windows-Container muss die Ausführung von Node.js unterstützen. In einem Windows Nano Server-Basiscontainer fehlen Abhängigkeiten, die zum Ausführen von Node.js erforderlich sind.

Gehostete Agents

Nur windows-2019- und ubuntu-*-Images unterstützen das Ausführen von Containern. Das macOS-Image unterstützt das Ausführen von Containern nicht.

Einzelner Auftrag

Ein einfaches Beispiel:

pool:
  vmImage: 'ubuntu-latest'

container: ubuntu:18.04

steps:
- script: printenv

Es weist das System an, das ubuntu-Image mit dem Tag 18.04 von Docker Hub abzurufen und dann den Container zu starten. Wenn der Befehl printenv ausgeführt wird, geschieht dies innerhalb des ubuntu:18.04-Containers.

Windows-Beispiel:

pool:
  vmImage: 'windows-2019'

container: mcr.microsoft.com/windows/servercore:ltsc2019

steps:
- script: set

Hinweis

Windows erfordert, dass die Kernelversion von Host und Container übereinstimmen. In diesem Beispiel mit dem Windows 2019-Image verwenden wir das Tag 2019 für den Container.

Mehrere Aufträge

Container sind auch nützlich, um dieselben Schritte in mehreren Aufträgen auszuführen. Im folgenden Beispiel werden dieselben Schritte in mehreren Versionen von Ubuntu Linux ausgeführt. (Und wir müssen das Schlüsselwort jobs nicht erwähnen, da nur ein einzelner Auftrag definiert ist.)

pool:
  vmImage: 'ubuntu-latest'

strategy:
  matrix:
    ubuntu16:
      containerImage: ubuntu:16.04
    ubuntu18:
      containerImage: ubuntu:18.04
    ubuntu20:
      containerImage: ubuntu:20.04

container: $[ variables['containerImage'] ]

steps:
- script: printenv

Endpunkte

Container können in anderen Registrierungen als öffentlichen Docker Hub-Registrierungen gehostet werden. Um ein Image in Azure Container Registry oder einer anderen privaten Containerregistrierung (einschließlich einer privaten Docker Hub-Registrierung) zu hosten, fügen Sie eine Dienstverbindung zur privaten Registrierung hinzu. Anschließend können Sie in einer Containerspezifikation darauf verweisen:

container:
  image: registry:ubuntu1804
  endpoint: private_dockerhub_connection

steps:
- script: echo hello

oder

container:
  image: myprivate.azurecr.io/windowsservercore:1803
  endpoint: my_acr_connection

steps:
- script: echo hello

Andere Containerregistrierungen können ebenfalls funktionieren. Amazon ECR funktioniert derzeit nicht, da es andere Clienttools gibt, die erforderlich sind, um AWS-Anmeldeinformationen so zu konvertieren, das Docker sie zur Authentifizierung nutzen kann.

Hinweis

Der Red Hat Enterprise Linux 6-Build des Agents kann keinen Containerauftrag ausführen. Wählen Sie eine andere Linux-Variante aus, z. B. Red Hat Enterprise Linux 7 oder höher.

Tastatur

Wenn Sie den Containerstart steuern müssen, können Sie options angeben.

container:
  image: ubuntu:18.04
  options: --hostname container-test --ip 192.168.0.1

steps:
- script: echo hello

Beim Ausführen von docker create --help erhalten Sie die Liste der Optionen, die an den Docker-Aufruf übergeben werden können. Nicht alle diese Optionen sind garantiert mit Azure DevOps kompatibel. Überprüfen Sie zuerst, ob Sie eine Containereigenschaft verwenden können, um dasselbe Ziel zu erreichen. Weitere Informationen finden Sie im resources.containers.containerYAML-Schema und in der docker create Befehlsreferenz.

Definition wiederverwendbarer Container

Im folgenden Beispiel werden die Container im Abschnitt „resources“ definiert. Auf jeden Container wird später verwiesen, indem auf den ihm zugewiesenen Alias verwiesen wird. (Hier führen wir das Schlüsselwort jobs der Klarheit halber explizit auf.)

resources:
  containers:
  - container: u16
    image: ubuntu:16.04

  - container: u18
    image: ubuntu:18.04

  - container: u20
    image: ubuntu:20.04

jobs:
- job: RunInContainer
  pool:
    vmImage: 'ubuntu-latest'

  strategy:
    matrix:
      ubuntu16:
        containerResource: u16
      ubuntu18:
        containerResource: u18
      ubuntu20:
        containerResource: u20

  container: $[ variables['containerResource'] ]

  steps:
  - script: printenv

Nicht glibc-basierte Container

Der Azure Pipelines-Agent stellt eine Kopie von Node.js bereit, die zum Ausführen von Aufgaben und Skripts erforderlich ist. Informationen zum Ermitteln der Version von Node.js für einen gehosteten Agent finden Sie unter Von Microsoft gehostete Agents. Die Version von Node.js wird für die C-Runtime kompiliert, die wir in unserer gehosteten Cloud verwenden, in der Regel glibc. Einige Varianten von Linux verwenden andere C-Runtimes. Alpine Linux verwendet beispielsweise musl.

Wenn Sie einen nicht glibc-basierten Container als Auftragscontainer verwenden möchten, müssen Sie einige Dinge selbst in die Hand nehmen. Zunächst müssen Sie Ihre eigene Kopie von Node.js bereitstellen. Zweitens müssen Sie Ihrem Image eine Bezeichnung hinzufügen, die dem Agent mitteilt, wo er die Node.js-Binärdatei finden kann. Und schließlich fehlen im Funktionsumfang von Alpine weitere Abhängigkeiten, auf die Azure Pipelines angewiesen ist: bash, sudo, which und groupadd.

Nutzen einer eigenen Instanz von Node.js

Sie sind dafür verantwortlich, Ihrem Container eine Node.js-Binärdatei hinzuzufügen. Node.js 14 ist eine sichere Wahl. Sie können mit dem Image node:14-alpine beginnen.

Informieren des Agents über Node.js

Der Agent liest die Containerbezeichnung com.azure.dev.pipelines.handler.node.path. Wenn diese Bezeichnung vorhanden ist, muss sie der Pfad zur Node.js Binärdatei sein. In einem auf node:10-alpine basierenden Image fügen Sie beispielsweise diese Zeile in Ihre Dockerfile ein:

LABEL "com.azure.dev.pipelines.agent.handler.node.path"="/usr/local/bin/node"

Anforderungen hinzufügen

Azure Pipelines setzt ein Bash-basiertes System voraus, auf dem die üblichen Verwaltungspakete installiert sind. Insbesondere Alpine Linux verfügt nicht über mehrere der benötigten Pakete. Durch Installation von bash, sudo und shadow werden die grundlegenden Anforderungen erfüllt.

RUN apk add bash sudo shadow

Wenn Sie von integrierten oder Marketplace-Aufgaben abhängig sind, müssen Sie auch die dafür erforderlichen Binärdateien bereitstellen.

Vollständiges Beispiel einer Dockerfile

FROM node:10-alpine

RUN apk add --no-cache --virtual .pipeline-deps readline linux-pam \
  && apk add bash sudo shadow \
  && apk del .pipeline-deps

LABEL "com.azure.dev.pipelines.agent.handler.node.path"="/usr/local/bin/node"

CMD [ "node" ]

Mehrere Aufträge mit Agent-Pools auf einem einzelnen gehosteten Agent

Der Containerauftrag verwendet die zugrunde liegende Docker-Datei „config.json“ für den Host-Agent zur Autorisierung der Imageregistrierung, die sich am Ende der Containerinitialisierung der Docker-Registrierung abmeldet. Die Autorisierung für nachfolgende Pulls des Images aus der Registrierung wird möglicherweise wegen „nicht autorisierter Authentifizierung“ verweigert, weil die im System für die Authentifizierung registrierte Docker-Datei „config.json“ bereits von einem der anderen parallel ausgeführten Containeraufträge abgemeldet wurde.

Die Lösung besteht darin, die Docker-Umgebungsvariable DOCKER_CONFIG festzulegen, die für jeden Agent-Pooldienst spezifisch ist, der auf dem gehosteten Agent ausgeführt wird. Exportieren Sie DOCKER_CONFIG im Skript „runsvc.sh“ der einzelnen Agent-Pools:

#insert anything to set up env when running as a service
export DOCKER_CONFIG=./.docker