Compartir vía


Contenedores de servicio

Azure DevOps Services

Si la canalización requiere la compatibilidad de uno o varios servicios, es posible que tenga que crear, conectarse y limpiar los servicios por trabajo. Por ejemplo, la canalización podría ejecutar pruebas de integración que requieran acceso a una base de datos recién creada y a una caché de memoria para cada trabajo de la canalización.

Un contenedor proporciona una manera sencilla y portátil de ejecutar un servicio del que depende la canalización. Un contenedor de servicios le permite crear, red y administrar automáticamente el ciclo de vida de un servicio en contenedor. Solo se puede acceder a cada contenedor de servicios para el trabajo que lo requiere. Los contenedores de servicio funcionan con cualquier tipo de trabajo, pero se usan con más frecuencia con trabajos de contenedor.

Requisitos

  • Los contenedores de servicio deben definir CMD o ENTRYPOINT. La canalización se ejecuta docker run para el contenedor proporcionado sin ningún argumento.

  • Azure Pipelines puede ejecutar contenedores de Linux o Windows. Puede usar el grupo de contenedores de Ubuntu hospedado para contenedores de Linux o el grupo de Windows hospedado para contenedores de Windows. El grupo de macOS hospedado no admite la ejecución de contenedores.

Nota:

Los contenedores de servicio no se admiten en canalizaciones clásicas.

Trabajo en un contenedor único

En la siguiente definición de canalización YAML de ejemplo se muestra un único trabajo de contenedor.

resources:
  containers:
  - container: my_container
    image: buildpack-deps:focal
  - container: nginx
    image: nginx

pool:
  vmImage: 'ubuntu-latest'

container: my_container
services:
  nginx: nginx

steps:
- script: |
    curl nginx
  displayName: Show that nginx is running

La canalización anterior captura los nginx contenedores y buildpack-deps de Docker Hub y, a continuación, inicia los contenedores. Los contenedores están conectados en red para que puedan comunicarse entre sí por su nombre services.

Desde dentro de este contenedor de trabajos, el nombre de nginx host se resuelve en los servicios correctos mediante redes de Docker. Todos los contenedores de la red exponen automáticamente todos los puertos entre sí.

Trabajo de no contenedor único

También puede usar contenedores de servicio sin un contenedor de trabajos, como en el ejemplo siguiente.

resources:
  containers:
  - container: nginx
    image: nginx
    ports:
    - 8080:80
    env:
      NGINX_PORT: 80
  - container: redis
    image: redis
    ports:
    - 6379

pool:
  vmImage: 'ubuntu-latest'

services:
  nginx: nginx
  redis: redis

steps:
- script: |
    curl localhost:8080
    echo $AGENT_SERVICES_REDIS_PORTS_6379

La canalización anterior inicia los contenedores más recientes nginx . Dado que el trabajo no se está ejecutando en un contenedor, no hay ninguna resolución de nombres automática. En su lugar, puede acceder a los servicios mediante localhost. En el ejemplo se proporciona explícitamente el 8080:80 puerto.

Un enfoque alternativo consiste en permitir que un puerto aleatorio se asigne dinámicamente en tiempo de ejecución. A continuación, puede acceder a estos puertos dinámicos mediante variables. Estas variables tienen el formato: agent.services.<serviceName>.ports.<port>. En un script de Bash, puede acceder a variables mediante el entorno de proceso.

En el ejemplo anterior, redis se asigna un puerto disponible aleatorio en el host. La variable agent.services.redis.ports.6379 contiene el número de puerto.

Varios trabajos

Los contenedores de servicio también son útiles para ejecutar los mismos pasos en varias versiones del mismo servicio. En el siguiente ejemplo, los mismos pasos se ejecutan en varias versiones de PostgreSQL.

resources:
  containers:
  - container: my_container
    image: ubuntu:22.04
  - container: pg15
    image: postgres:15
  - container: pg14
    image: postgres:14

pool:
  vmImage: 'ubuntu-latest'

strategy:
  matrix:
    postgres15:
      postgresService: pg15
    postgres14:
      postgresService: pg14

container: my_container

services:
  postgres: $[ variables['postgresService'] ]
steps:
- script: printenv

Puertos

Al invocar un recurso de contenedor o un contenedor insertado, puede especificar una matriz de ports para exponerlo en el contenedor, como en el ejemplo siguiente.

resources:
  containers:
  - container: my_service
    image: my_service:latest
    ports:
    - 8080:80
    - 5432

services:
  redis:
    image: redis
    ports:
    - 6379/tcp

No es necesario especificar ports si el trabajo se ejecuta en un contenedor, ya que los contenedores de la misma red de Docker exponen automáticamente todos los puertos entre sí de forma predeterminada.

Si el trabajo se ejecuta en el host, ports es necesario acceder al servicio. Un puerto toma el formato <hostPort>:<containerPort> o simplemente <containerPort> con un opcional /<protocol> al final. Por ejemplo, 6379/tcp expone tcp a través del puerto 6379, enlazado a un puerto aleatorio en el equipo host.

Para los puertos enlazados a un puerto aleatorio en el equipo host, la canalización crea una variable del formulario agent.services.<serviceName>.ports.<port> para que el trabajo pueda acceder al puerto. Por ejemplo, agent.services.redis.ports.6379 se resuelve en el puerto asignado aleatoriamente en el equipo host.

Volúmenes

Los volúmenes son útiles para compartir datos entre servicios o para conservar datos entre varias ejecuciones de un trabajo. Especifique montajes de volumen como una matriz del volumes formulario <source>:<destinationPath>, donde <source> puede ser un volumen con nombre o una ruta de acceso absoluta en el equipo host, y <destinationPath> es una ruta de acceso absoluta en el contenedor. Los volúmenes se pueden denominar volúmenes de Docker, volúmenes anónimos de Docker o enlazar montajes en el host.

services:
  my_service:
    image: myservice:latest
    volumes:
    - mydockervolume:/data/dir
    - /data/dir
    - /src/dir:/dst/dir

Nota:

Si usa grupos hospedados por Microsoft, los volúmenes no se conservan entre trabajos, ya que la máquina host se limpia una vez completado cada trabajo.

Opciones de inicio

Los contenedores de servicio comparten los mismos recursos de contenedor que los trabajos de contenedor. Esto significa que puede usar las mismas opciones de inicio.

Comprobación de estado

Si algún contenedor de servicios especifica healthCHECK, el agente puede esperar opcionalmente hasta que el contenedor esté en buen estado antes de ejecutar el trabajo.

Ejemplo de varios contenedores con servicios

En el ejemplo siguiente se tiene un contenedor web de Python de Django conectado a los contenedores de base de datos postgreSQL y MySQL.

  • La base de datos PostgreSQL es la base de datos principal y su contenedor se denomina db.
  • El db contenedor usa el volumen /data/db:/var/lib/postgresql/datay hay tres variables de base de datos que se pasan al contenedor a través de env.
  • El mysql contenedor usa el puerto 3306:3306y también hay variables de base de datos pasadas a través de env.
  • El contenedor web está abierto con el puerto 8000.

En los pasos, instala las dependencias y, a continuación, pip se ejecutan las pruebas de Django.

Para configurar un ejemplo de trabajo, necesita un sitio de Django configurado con dos bases de datos. En el ejemplo se supone que el archivo manage.py está en el directorio raíz y el proyecto de Django también está dentro de ese directorio. Si no es así, es posible que tenga que actualizar la /__w/1/s/ ruta de acceso en /__w/1/s/manage.py test.

resources:
  containers:
    - container: db
      image: postgres
      volumes:
          - '/data/db:/var/lib/postgresql/data'
      env:
        POSTGRES_DB: postgres
        POSTGRES_USER: postgres
        POSTGRES_PASSWORD: postgres
    - container: mysql
      image: 'mysql:5.7'
      ports:
         - '3306:3306'
      env:
        MYSQL_DATABASE: users
        MYSQL_USER: mysql
        MYSQL_PASSWORD: mysql
        MYSQL_ROOT_PASSWORD: mysql
    - container: web
      image: python
      volumes:
      - '/code'
      ports:
        - '8000:8000'

pool:
  vmImage: 'ubuntu-latest'

container: web
services:
  db: db
  mysql: mysql

steps:
    - script: |
        pip install django
        pip install psycopg2
        pip install mysqlclient
      displayName: set up django
    - script: |
          python /__w/1/s/manage.py test