Définir des travaux de conteneur (YAML)

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

Par défaut, les travaux s’exécutent sur la machine hôte où l’agent est installé. C’est pratique et généralement bien adapté aux projets qui commencent tout juste à adopter Azure Pipelines. Au fil du temps, vous pouvez avoir envie de mieux contrôler le contexte d’exécution de vos tâches. Les pipelines YAML offrent des travaux de conteneur pour ce niveau de contrôle.

Sur les agents Linux et Windows, les travaux peuvent être exécutés sur l’hôte ou dans un conteneur. (Sur macOS et Red Hat Enterprise Linux 6, les travaux de conteneur ne sont pas disponibles.) Les conteneurs fournissent une isolation de l’hôte et vous permettent d’épingler des versions spécifiques d’outils et de dépendances. Les travaux hôtes nécessitent moins d’installation initiale et d’infrastructure à gérer.

Les conteneurs offrent une abstraction légère sur le système d’exploitation hôte. Vous pouvez sélectionner les versions exactes des systèmes d’exploitation, des outils et des dépendances dont votre build a besoin. Lorsque vous spécifiez un conteneur dans votre pipeline, l’agent récupère et démarre d’abord le conteneur. Ensuite, chaque étape du travail s’exécute à l’intérieur du conteneur. Vous ne pouvez pas avoir de conteneurs imbriqués. Les conteneurs ne sont pas pris en charge lorsqu’un agent s’exécute déjà à l’intérieur d’un conteneur.

Si vous avez besoin d’un contrôle affiné au niveau de chaque étape, les cibles d’étape vous permettent de choisir un conteneur ou un hôte pour chaque étape.

Configuration requise

Conteneurs basés sur Linux

Le système Azure Pipelines nécessite quelques critères pour les conteneurs Linux :

  • Bash
  • Basé sur glibc
  • Peut exécuter Node.js (fourni par l’agent)
  • Ne définit pas de ENTRYPOINT
  • USER a accès aux commandes groupadd et à d’autres privilèges sans sudo

Et sur votre hôte d’agent :

  • Vérifier que Docker est installé
  • L’agent doit être autorisé à accéder au démon Docker

Vérifiez que chacun de ces outils est disponible dans votre conteneur. Certains des conteneurs dépouillés disponibles sur Docker Hub, en particulier ceux basés sur Alpine Linux, ne répondent pas à ces exigences minimales. Les conteneurs avec un ENTRYPOINT peut ne pas fonctionner, car Azure Pipelines exécutera docker create sur un conteneur et docker exec pour une série de commandes, qui s’attendent à ce que le conteneur soit toujours opérationnel.

Notes

Pour les conteneurs Linux basés sur Windows, Node.js doit être préinstallé.

Conteneurs Windows

Azure Pipelines peut également exécuter des conteneurs Windows. Windows Server version 1803 ou ultérieure est requis. Docker doit être installé. Vérifiez que votre agent de pipelines est autorisé à accéder au démon Docker.

Le conteneur Windows doit prendre en charge l’exécution de Node.js. Un conteneur Windows Nano Server de base ne contient pas les dépendances requises pour exécuter Node.

Agents hébergés

Seules les images windows-2019 et ubuntu-* prennent en charge l’exécution de conteneurs. L’image macOS ne prend pas en charge les conteneurs en cours d’exécution.

Travail unique

Voici un exemple simple :

pool:
  vmImage: 'ubuntu-latest'

container: ubuntu:18.04

steps:
- script: printenv

Cela indique au système d’extraire l’image ubuntu étiquetée 18.04 à partir de Docker Hub, puis de démarrer le conteneur. Lorsque la commande printenv s’exécute, elle se produit à l’intérieur du conteneur ubuntu:18.04.

Exemple Windows :

pool:
  vmImage: 'windows-2019'

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

steps:
- script: set

Notes

Windows exige que la version du noyau de l’hôte et celle du conteneur correspondent. Dans la mesure où cet exemple utilise l’image Windows 2019, nous allons utiliser l’étiquette 2019 pour le conteneur.

Plusieurs travaux

Les conteneurs sont également utiles pour exécuter les mêmes étapes dans plusieurs travaux. Dans l’exemple suivant, les mêmes étapes s’exécutent dans plusieurs versions d’Ubuntu Linux. (Et nous n’avons pas à mentionner le mot clé jobs, car il n’existe qu’un seul travail défini.)

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

Points de terminaison

Les conteneurs peuvent être hébergés sur des registres autres que des registres Docker Hub publics. Pour héberger une image sur Azure Container Registry ou un autre registre de conteneurs privé (y compris un registre Docker Hub privé), ajoutez une connexion de service au registre privé. Vous pouvez ensuite le référencer dans une spécification de conteneur :

container:
  image: registry:ubuntu1804
  endpoint: private_dockerhub_connection

steps:
- script: echo hello

or

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

steps:
- script: echo hello

D’autres registres de conteneurs peuvent également fonctionner. Amazon ECR ne fonctionne pas actuellement, car d’autres outils clients sont nécessaires pour convertir les informations d’identification AWS en quelque chose que Docker peut utiliser pour s’authentifier.

Notes

La build Red Hat Enterprise Linux 6 de l’agent n’exécute pas le travail de conteneur. Choisissez une autre version de Linux, comme Red Hat Enterprise Linux 7 ou version ultérieure.

Options

Si vous devez contrôler le démarrage du conteneur, vous pouvez spécifier options.

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

steps:
- script: echo hello

L’exécution de docker create --help vous fournit la liste des options qui peuvent être transmises à l’appel Docker. Toutes ces options ne fonctionnent pas forcément avec Azure DevOps. Vérifiez d’abord si vous pouvez utiliser une propriété conteneur pour atteindre le même objectif. Pour plus d’informations, consultez resources.containers.container dans le schéma YAML et la référence de la commande docker create.

Définition de conteneur réutilisable

Dans l’exemple suivant, les conteneurs sont définis dans la section resources. Chaque conteneur est ensuite référencé plus tard, en faisant référence à son alias attribué. (Ici, nous listons explicitement le mot clé jobs pour plus de clarté.)

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

Conteneurs non basés sur glibc

L’agent Azure Pipelines fournit une copie de Node.js, qui est nécessaire pour exécuter des tâches et des scripts. Pour identifier la version de Node.js pour un agent hébergé, consultez Agents hébergés par Microsoft. La version de Node.js est compilée sur le runtime C que nous utilisons dans notre cloud hébergé, généralement glibc. Certaines variantes de Linux utilisent d’autres runtimes C. Par exemple, Alpine Linux utilise musl.

Si vous voulez utiliser un conteneur non-glibc comme conteneur de travail, vous devez prendre quelques dispositions. Tout d’abord, vous devez fournir votre propre copie de Node.js. Ensuite, vous devez ajouter une étiquette à votre image indiquant à l’agent où trouver le binaire Node.js. Enfin, par défaut Alpine n’est pas disponible avec d’autres dépendances dont dépend Azure Pipelines : bash, sudo, which et groupadd.

Apportez votre propre Node.js

Vous êtes responsable de l’ajout d’un binaire Node à votre conteneur. Node 18 est un choix sûr. Vous pouvez commencer à partir de l’image node:18-alpine.

Informer l’agent concernant Node.js

L’agent lira une étiquette de conteneur « com.azure.dev.pipelines.handler.node.path ». Si cette étiquette existe, il doit s’agir du chemin d’accès au binaire Node.js. Par exemple, dans une image basée sur node:18-alpine, ajoutez cette ligne à votre fichier Dockerfile :

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

Ajouter des spécifications

Azure Pipelines suppose un système Bash avec les packages d’administration courants installés. Alpine Linux en particulier n’est pas équipé de plusieurs packages nécessaires. L’installation de bash, sudo et shadow couvrira vos besoins de base.

RUN apk add bash sudo shadow

Si vous dépendez de tâches intégrées ou de la Place de marché, vous devez également fournir les fichiers binaires dont elles ont besoin.

Exemple complet d’un fichier 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" ]

Plusieurs travaux avec des pools d’agents sur un seul agent hébergé

Le travail de conteneur utilise l’agent d’hôte sous-jacent Docker config.json pour l’autorisation du registre d’images, qui se déconnecte à la fin de l’initialisation du conteneur du registre Docker. L’autorisation d’extraction d’image de registre ultérieure peut être refusée pour « authentification non autorisée », car le fichier Docker config.json inscrit dans le système pour l’authentification a déjà été déconnecté par l’un des autres travaux de conteneur qui s’exécutent en parallèle.

La solution consiste à définir la variable d’environnement Docker DOCKER_CONFIG spécifique à chaque service de pool d’agents exécuté sur l’agent hébergé. Exportez DOCKER_CONFIG dans chaque script runsvc.sh du pool d’agents :

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