Les tâches des conteneurs dans les pipelines YAML

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

Cet article explique les tâches de conteneur dans Azure Pipelines.

Par défaut, les travaux Azure Pipelines s'exécutent directement sur les machines hôtes où l'agent est installé. Les tâches des agents hébergés sont pratiques, nécessitent peu de configuration initiale et d'infrastructure à maintenir, et sont bien adaptées aux projets de base.

Si vous souhaitez mieux contrôler le contexte des tâches, vous pouvez définir et exécuter des tâches dans des conteneurs. Les conteneurs sont une abstraction légère du système d'exploitation hôte qui permet de s'isoler de l'hôte. Lorsque vous exécutez des tâches dans des conteneurs, vous pouvez sélectionner les versions exactes des systèmes d'exploitation, des outils et des dépendances dont votre construction a besoin.

Les agents Linux et Windows peuvent exécuter des tâches de pipeline directement sur l'hôte ou dans des conteneurs. Les tâches de conteneur ne sont pas disponibles sur macOS.

Pour un travail dans un conteneur, 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.

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

Prérequis

  • Utilisez un pipeline YAML. Les pipelines classiques ne prennent pas en charge les tâches de conteneur.
  • Utilisez un agent Windows ou Ubuntu hébergé. Seules les agents windows-* et ubuntu-* prennent en charge l’exécution de conteneurs. Les agents macos-* ne prennent pas en charge l'exécution des conteneurs.
  • Votre agent est configuré pour les tâches de conteneur.
    • Les agents Windows et Linux doivent avoir installé Docker et avoir la permission d'accéder au démon Docker.
    • Les conteneurs ne sont pas pris en charge lorsque l'agent s'exécute déjà dans un conteneur. Vous ne pouvez pas avoir de conteneurs imbriqués.

Exigences supplémentaires pour les conteneurs

Les conteneurs basés sur Linux ont les exigences suivantes. Pour des solutions de contournement, voir Conteneurs basés sur Nonglibc.

  • Bash installé
  • Basé sur la bibliothèque GNU C (glibc)
  • Aucune ENTRYPOINT
  • Permettre à USER d'accéder à groupadd et à d'autres commandes privilégiées sans utiliser sudo
  • Peut exécuter Node.js, que l'agent fournit.

    Remarque

    Node.js doit être préinstallé pour les conteneurs Linux sur les hôtes Windows.

Certains conteneurs dépouillés disponibles sur Docker Hub, notamment les conteneurs basés sur Alpine Linux, ne satisfont pas à ces exigences. Les conteneurs avec un ENTRYPOINT risquent de ne pas fonctionner car Azure Pipelines docker create et docker exec s'attendent à ce que le conteneur soit toujours opérationnel.

Exemples de tâches uniques

Les exemples suivants définissent un conteneur Windows ou Linux pour une tâche unique.

L'exemple simple suivant définit un conteneur Linux :

pool:
  vmImage: 'ubuntu-latest'

container: ubuntu:18.04

steps:
- script: printenv

L'exemple précédent indique au système de récupérer l'image ubuntu balise 18.04 depuis Docker Hub, puis de démarrer le conteneur. La commande printenv s'exécute dans le conteneur ubuntu:18.04.

Plusieurs travaux

Vous pouvez utiliser des conteneurs pour exécuter la même étape dans plusieurs tâches. L'exemple suivant exécute la même étape dans plusieurs versions d'Ubuntu Linux. Vous ne devez pas mentionner le mot-clé jobs parce qu'un seul travail est 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

Plusieurs tâches avec des pools d'agents sur un seul hôte d'agent

Un job de conteneur utilise le fichier de configuration Docker de l'agent hôte sous-jacent pour l'autorisation du registre d'images. Ce fichier se déconnecte à la fin de l'initialisation du conteneur de registre Docker. Les tirages d'images de registre pour les tâches de conteneur ultérieures peuvent être refusés pour unauthorized authentication parce qu'une autre tâche exécutée en parallèle a déjà signé le fichier de configuration Docker.

La solution consiste à définir une variable d'environnement Docker DOCKER_CONFIG spécifique à chaque pool d'agents s'exécutant sur l'agent hébergé. Exportez le DOCKER_CONFIG dans le script runsvc.sh de chaque pool d'agents comme suit :

export DOCKER_CONFIG=./.docker

Options de démarrage

Vous pouvez spécifier options pour contrôler le démarrage du conteneur, comme dans l'exemple suivant :

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

steps:
- script: echo hello

Running docker create --help vous donne la liste des options que vous pouvez passer à l'invocation de Docker. Toutes ces options ne fonctionnent pas forcément avec Azure DevOps. Vérifiez d'abord si vous pouvez utiliser une propriété container pour atteindre le même objectif.

Pour plus d'informations, consultez la référence de la commande docker create et la définition de resources.containers.container dans la référence du schéma YAML d'Azure DevOps.

Définition de conteneur réutilisable

L'exemple suivant définit les conteneurs dans la section resources, puis les référence par les alias qui leur ont été attribués. Le mot-clé jobs est explicitement mentionné 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

Points de terminaison de service

Vous pouvez héberger des conteneurs sur d'autres registres que le Docker Registry public. 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 pour accéder au registre. Vous pouvez alors référencer le point de terminaison dans la définition du conteneur.

Connexion à Docker Hub privé :

container:
  image: registry:ubuntu1804
  endpoint: private_dockerhub_connection

Connexion Azure Container Registry :

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

Remarque

Azure Pipelines ne peut pas configurer une connexion de service pour Amazon Elastic Container Registry (ECR), car Amazon ECR nécessite d'autres outils clients pour convertir les informations d'identification AWS en quelque chose que Docker peut utiliser pour s'authentifier.

Conteneurs basés sur Nonglibc

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 se compile par rapport au runtime C utilisé dans le cloud hébergé, généralement glibc. Certaines variantes de Linux utilisent d'autres runtimes C. Par exemple, Alpine Linux utilise musl.

Si vous souhaitez utiliser un conteneur basé sur nonglibc, vous devez.. :

  • Fournir votre propre copie de Node.js.
  • Ajouter une étiquette à votre image indiquant à l'agent où trouver le binaire Node.js.
  • Fournissez d'autres dépendances dont Azure Pipelines dépend : bash, sudo, which et groupadd.

Fournir votre propre Node.js

Si vous utilisez un conteneur non basé sur la librairie, vous êtes responsable de l'ajout d'un binaire Node à votre conteneur. Node.js 18 est un choix sûr. Commencez par l’image node:18-alpine.

Informer l’agent concernant Node.js

L'agent lit l'étiquette du 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 la ligne suivante à votre Dockerfile :

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

Ajouter les packages nécessaires

Azure Pipelines suppose un système basé sur Bash avec des packages administratifs courants installés. Alpine Linux en particulier n’est pas équipé de plusieurs packages nécessaires. Installez bash, sudo et shadow pour couvrir les besoins de base.

RUN apk add bash sudo shadow

Si vous dépendez de tâches intégrées ou de tâches Marketplace, fournissez également les binaires dont elles ont besoin.

Exemple de fichier Docker complet

FROM node:18-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" ]