Definición de trabajos de contenedor (YAML)

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

De forma predeterminada, los trabajos se ejecutan en la máquina host donde está instalado el agente. Este sistema resulta práctico y es adecuado normalmente para los proyectos que están empezando a adoptar Azure Pipelines. Con el tiempo, es posible que desee un mayor control sobre el contexto donde se ejecutan las tareas. Las canalizaciones de YAML ofrecen trabajos de contenedor para este nivel de control.

En los agentes de Linux y Windows, los trabajos se pueden ejecutar en el host o en un contenedor. (En macOS y Red Hat Enterprise Linux 6, los trabajos de contenedor no están disponibles). Los contenedores proporcionan aislamiento del host y permiten anclar versiones específicas de herramientas y dependencias. Los trabajos de host no necesitan tanto mantenimiento de la instalación y la infraestructura.

Los contenedores ofrecen una abstracción ligera sobre el sistema operativo host. Puede seleccionar las versiones exactas de los sistemas operativos, las herramientas y las dependencias que requiere la compilación. Al especificar un contenedor en la canalización, el agente primero lo capturará e iniciará. A continuación, cada paso del trabajo se ejecutará dentro del contenedor. No se pueden tener contenedores anidados. No se admiten contenedores cuando un agente ya se ejecuta dentro de un contenedor.

Si necesita un control específico a nivel de paso individual, los destinos de paso le permiten elegir el contenedor o el host de cada paso.

Requisitos

Contenedores basados en Linux

El sistema de Azure Pipelines necesita algunos elementos en contenedores basados en Linux:

  • Bash
  • Basado en glibc
  • Puede ejecutar Node.js (que proporciona el agente).
  • No define un objeto ENTRYPOINT.
  • USER tiene acceso a groupadd y otros comandos de privilegios sin sudo.

Y en el host del agente:

  • Asegúrese de que Docker está instalado.
  • El agente debe tener permiso para acceder al demonio de Docker.

Asegúrese de que el contenedor tiene cada una de estas herramientas disponibles. Algunos de los contenedores eliminados disponibles en Docker Hub, especialmente aquellos basados en Alpine Linux, no cumplen estos requisitos mínimos. Es posible que los contenedores con un objeto ENTRYPOINT no funcionen, ya que Azure Pipelines aplicará docker create al contenedor en espera y docker exec a una serie de comandos, que esperan que el contenedor esté siempre en funcionamiento.

Nota:

En el caso de los contenedores de Linux basados en Windows, Node.js debe estar preinstalado.

Contenedores de Windows

Azure Pipelines también puede ejecutar contenedores de Windows. Se requiere Windows Server versión 1803 o posterior. Docker debe estar instalado. Asegúrese de que el agente de canalizaciones tiene permiso para acceder al demonio de Docker.

El contenedor de Windows debe admitir la ejecución de Node.js. A un contenedor base Windows Nano Server le faltan dependencias necesarias para ejecutar Node.

Agentes hospedados

Solo las imágenes de windows-2019 y ubuntu-* admiten la ejecución de contenedores. La imagen de macOS no admite la ejecución de contenedores.

Trabajo único

Un ejemplo sencillo:

pool:
  vmImage: 'ubuntu-latest'

container: ubuntu:18.04

steps:
- script: printenv

Esto indica al sistema que capture la imagen de ubuntu etiquetada como 18.04 de Docker Hub y, que, luego, inicie el contenedor. Cuando se ejecute el comando printenv, sucederá dentro del contenedor ubuntu:18.04.

Ejemplo de Windows:

pool:
  vmImage: 'windows-2019'

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

steps:
- script: set

Nota:

Windows requiere que la versión del kernel del host y el contenedor coincidan. Dado que en este ejemplo se usa la imagen de Windows 2019, se utilizará la etiqueta 2019 para el contenedor.

Varios trabajos

Los contenedores también son útiles para ejecutar los mismos pasos en varios trabajos. En el ejemplo siguiente, los mismos pasos se ejecutan en varias versiones de Ubuntu Linux. (Y no tenemos que mencionar la palabra clave jobs, ya que solo hay un único trabajo definido).

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

Puntos de conexión

Los contenedores se pueden hospedar en registros distintos de los registros de Docker Hub públicos. Para hospedar una imagen en Azure Container Registry u otro registro de contenedor privado (incluido un registro de Docker Hub privado), agregue una conexión de servicio al registro privado. A continuación, puede hacer referencia a ella en una especificación de contenedor:

container:
  image: registry:ubuntu1804
  endpoint: private_dockerhub_connection

steps:
- script: echo hello

o

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

steps:
- script: echo hello

Otros registros de contenedor también pueden funcionar. Amazon ECR no funciona actualmente, ya que hay otras herramientas de cliente necesarias para convertir las credenciales de AWS en algo que Docker puede usar para autenticarse.

Nota:

La compilación de Red Hat Enterprise Linux 6 del agente no ejecutará el trabajo de contenedor. Elija otra versión de Linux, como Red Hat Enterprise Linux 7 o superior.

Opciones

Si necesita controlar el inicio del contenedor, puede especificar options.

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

steps:
- script: echo hello

La ejecución de docker create --help le proporcionará la lista de opciones que se pueden pasar a la invocación de Docker. No todas estas opciones están garantizadas para trabajar con Azure DevOps. Compruebe primero si puede usar una propiedad de contenedor para lograr el mismo objetivo. Para obtener más información, consulte resources.containers.container en el esquema YAML y la referencia de comandos docker create .

Definición de contenedor reutilizable

En el ejemplo siguiente, los contenedores se definen en la sección de recursos. Luego, se hace referencia a cada contenedor más adelante, con una referencia a su alias asignado. (Por claridad, aquí se muestra explícitamente la palabra clave jobs).

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

Contenedores no basados en glibc

El agente de Azure Pipelines proporciona una copia de Node.js, que es necesaria para ejecutar tareas y scripts. Para averiguar la versión de Node.js de un agente hospedado, consulte Agentes hospedados por Microsoft. La versión de Node.js se compila en el tiempo de ejecución de C que usamos en la nube hospedada, normalmente glibc. Algunas variantes de Linux usan otros entornos de ejecución de C. Por ejemplo, Alpine Linux usa musl.

Si quiere usar un contenedor no basado en glibc como contenedor de trabajos, deberá organizar algunas cosas por su cuenta. En primer lugar, debe proporcionar su propia copia de Node.js. En segundo lugar, debe agregar una etiqueta a la imagen que indique al agente dónde encontrar el archivo binario Node.js. Por último, Stock Alpine no viene con otras dependencias de las que Azure Pipelines depende: bash, sudo, which y groupadd.

Traiga su propio archivo de Node.js

Es responsable de agregar un archivo binario de Node al contenedor. Node 14 es una opción segura. Puede empezar a partir de la imagen de node:14-alpine.

Informar al agente sobre Node.js

El agente leerá la etiqueta de contenedor "com.azure.dev.pipelines.handler.node.path". Si esta etiqueta existe, debe ser la ruta de acceso al archivo binario de Node.js. Por ejemplo, en una imagen basada en node:10-alpine, agregue esta línea al Dockerfile:

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

Agregar requisitos

Azure Pipelines da por hecho un sistema basado en Bash con paquetes de administración comunes instalados. Alpine Linux en particular no incluye varios de los paquetes necesarios. La instalación de bash, sudo y shadow cubre las necesidades básicas.

RUN apk add bash sudo shadow

Si depende de cualquier tarea integrada o de Marketplace, también deberá proporcionar los archivos binarios que estos requieran.

Ejemplo completo de un 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" ]

Varios trabajos con grupos de agentes en un único agente hospedado

El trabajo de contenedor usa el archivo config.json de Docker del agente host subyacente para la autorización del registro de imágenes, que cierra la sesión al final de la inicialización del contenedor de registro de Docker. La autorización de las extracciones de imágenes del registro subsiguientes podría denegarse por "autenticación no autorizada" porque uno de los otros trabajos de contenedor que se ejecutan en paralelo ya ha cerrado la sesión del archivo config.json de Docker registrado en el sistema para la autenticación.

La solución consiste en establecer la variable de entorno DOCKER_CONFIG de Docker específica de cada servicio de grupo de agentes que se ejecuta en el agente hospedado. Exporte DOCKER_CONFIG en cada script de runsvc.sh de cada grupo de agentes:

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