Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Azure DevOps Services
En este artículo se describe el uso de contenedores de servicio en Azure Pipelines. Si la canalización requiere el soporte de uno o varios servicios, es posible que tenga que crear, conectarse y limpiar los servicios para cada 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 de servicios proporciona una manera sencilla y portátil de ejecutar servicios en la canalización. El contenedor de servicios solo es accesible para el trabajo que lo requiere.
Los contenedores de servicios permiten crear, red y administrar automáticamente los ciclos de vida de los servicios de los que dependen las canalizaciones. Los contenedores de servicio funcionan con cualquier tipo de trabajo, pero se usan con más frecuencia con trabajos de contenedor.
Nota:
Las canalizaciones clásicas no admiten contenedores de servicio.
Condiciones y limitaciones
Los contenedores de servicio deben definir
CMDoENTRYPOINT. El pipeline se ejecutadocker runsin argumentos para el contenedor proporcionado.Azure Pipelines puede ejecutar contenedores de Linux o Windows . Usas el grupo Ubuntu hospedado para contenedores Linux o el grupo Windows hospedado para contenedores Windows. El grupo de macOS hospedado no admite la ejecución de contenedores.
Los contenedores de servicio comparten los mismos recursos de contenedor que los trabajos de contenedor, por lo que pueden usar las mismas opciones de inicio.
Si un contenedor de servicios especifica un HEALTHCHECK, el agente puede esperar opcionalmente hasta que el contenedor esté en buen estado antes de ejecutar el trabajo.
Trabajo en un contenedor único
En el siguiente ejemplo, el pipeline YAML define un trabajo de contenedor único que utiliza un contenedor de servicio. La canalización obtiene los contenedores buildpack-deps y nginx de Docker Hub, y luego inicia todos los contenedores. Los contenedores están en red para que puedan comunicarse entre sí por sus services nombres.
Desde dentro del contenedor de trabajo, el nginx nombre de 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í.
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
Trabajo de no contenedor único
También se pueden usar contenedores de servicio en trabajos que no sean de contenedor. La canalización inicia los contenedores más recientes, pero dado que el trabajo no se ejecuta en un contenedor, no hay ninguna resolución automática de nombres. En su lugar, se llega a los servicios mediante localhost. La canalización de ejemplo siguiente especifica explícitamente el 8080:80 puerto para nginx.
Un enfoque alternativo consiste en asignar un puerto aleatorio dinámicamente en tiempo de ejecución. Para permitir que el trabajo acceda al puerto, la canalización crea una variable de la forma agent.services.<serviceName>.ports.<port>. Puede acceder al puerto dinámico mediante esta variable de entorno en un script de Bash.
En la canalización siguiente, redis obtiene un puerto disponible aleatorio en el host y la agent.services.redis.ports.6379 variable contiene el número de puerto.
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
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
Los trabajos que se ejecutan directamente en el host requieren ports para acceder al contenedor de servicios. 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.
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.
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
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 de volumes.
Cada volumen toma el formato <source>:<destinationPath>, donde <source> es un volumen con nombre o una ruta de acceso absoluta en el 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:
Los grupos hospedados por Microsoft no conservan los volúmenes entre trabajos, ya que la máquina host se limpia después de cada trabajo.
Ejemplo de varios contenedores con servicios
La siguiente canalización de ejemplo 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
dbcontenedor usa el volumen/data/db:/var/lib/postgresql/datay pasa tres variables de base de datos al contenedor a través deenv. - El
mysqlcontenedor usa el puerto3306:3306y también pasa las variables de base de datos a través deenv. - El contenedor
webestá abierto con el puerto8000.
En los pasos, pip instala las dependencias y luego 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 y el proyecto de Django están en el directorio raíz. 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