Контейнеры служб
Azure DevOps Services
Если конвейеру требуется поддержка одной или нескольких служб, может потребоваться создать, подключиться и очистить службы для каждого задания. Например, конвейер может выполнять тесты интеграции, требующие доступа к созданной базе данных и кэшу памяти для каждого задания в конвейере.
Контейнер предоставляет простой и переносимый способ запуска службы, от которой зависит ваш поток. Контейнер
Требования
Контейнеры служб должны определять
CMD
илиENTRYPOINT
. Пайплайн выполняетсяdocker run
для предоставленного контейнера без каких-либо аргументов.Azure Pipelines может запускать контейнеры Linux или Windows. Вы можете использовать размещенный пул контейнеров Ubuntu для контейнеров Linux или размещенный пул Windows для контейнеров Windows. Облачный пул macOS не поддерживает запуск контейнеров.
Примечание.
Контейнеры служб не поддерживаются в классических конвейерах.
Задание в одном контейнере
В следующем примере определения конвейера YAML показано задание с одним контейнером.
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
Предыдущий конвейер извлекает контейнеры nginx
и buildpack-deps
из Docker Hub, а затем запускает контейнеры. Контейнеры объединяются в одну сеть, чтобы они могли связываться друг с другом по services
имени.
Из этого контейнера заданий nginx
имя узла разрешается в корректные службы с использованием сети Docker. Все контейнеры в сети автоматически предоставляют все порты друг другу.
Одно неконтайнерное задание
Контейнеры служб также можно использовать без контейнера заданий, как показано в следующем примере.
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
Предыдущий конвейер запускает последние nginx
контейнеры. Поскольку задание не выполняется в контейнере, автоматическое разрешение имён отсутствует. Вместо этого можно получить доступ к службам с помощью localhost
. В примере явно предоставляется 8080:80
порт.
Альтернативный подход — позволить случайному порту динамически назначаться во время выполнения. Затем вы можете получить доступ к этим динамическим портам с помощью переменных. Эти переменные принимают форму: agent.services.<serviceName>.ports.<port>
В скрипте Bash можно получить доступ к переменным с помощью среды процесса.
В предыдущем примере redis
назначается случайный доступный порт на узле. Переменная agent.services.redis.ports.6379
содержит номер порта.
Несколько заданий
Контейнеры служб также полезны для выполнения одних и тех же шагов в нескольких версиях одной службы. В следующем примере те же действия выполняются в нескольких версиях 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
Порты
При вызове ресурса контейнера или встроенного контейнера можно указать массив ports
для предоставления в контейнере, как показано в следующем примере.
resources:
containers:
- container: my_service
image: my_service:latest
ports:
- 8080:80
- 5432
services:
redis:
image: redis
ports:
- 6379/tcp
Указание ports
не требуется, если задание выполняется в контейнере, так как контейнеры в одной сети Docker автоматически предоставляют все порты друг другу по умолчанию.
Если задание выполняется на узле, ports
требуется для доступа к службе. Порт принимает форму <hostPort>:<containerPort>
или просто <containerPort>
с необязательным /<protocol>
в конце. Например, 6379/tcp
открывает tcp
через порт 6379
, связанный с случайным портом на хост-машине.
Для портов, привязанных к случайному порту на хост-компьютере, конвейер создает переменную формы agent.services.<serviceName>.ports.<port>
, чтобы задание могло получить доступ к порту. Например, agent.services.redis.ports.6379
разрешается в случайно назначенный порт на хост-машине.
Объемы
Тома данных полезны для совместного использования данных между службами или для постоянного хранения данных между несколькими запусками задания. Вы указываете точки монтирования томов в виде массива volumes
формы <source>:<destinationPath>
, где <source>
может быть либо именованным томом, либо абсолютным путем на хост-компьютере, и <destinationPath>
является абсолютным путем в контейнере. Тома могут быть названы томами Docker, анонимными томами Docker или привязками к узлу хоста.
services:
my_service:
image: myservice:latest
volumes:
- mydockervolume:/data/dir
- /data/dir
- /src/dir:/dst/dir
Примечание.
Если вы используете пулы, размещенные на серверах Microsoft, ваши объемы не сохраняются между заданиями, так как хост очищается после выполнения каждого задания.
Параметры запуска
Контейнеры служб используют одинаковые контейнерные ресурсы, что и контейнерные задания. Это означает, что можно использовать те же параметры запуска.
Проверка состояния
Если какой-либо контейнер службы указывает HEALTHCHECK, агент может по необходимости подождать, пока контейнер не будет в рабочем состоянии, прежде чем выполнять задание.
Пример нескольких контейнеров со службами
В следующем примере есть веб-контейнер Django Python, подключенный к контейнерам баз данных PostgreSQL и MySQL.
- База данных PostgreSQL — это база данных-источник, а ее контейнер называется
db
. - Контейнер
db
использует том/data/db:/var/lib/postgresql/data
, и через контейнерenv
передаются три переменные базы данных. - Контейнер
mysql
использует порт3306:3306
, а также передаются переменные базы данных черезenv
. - Контейнер
web
открыт на порту8000
.
На этом шаге pip
устанавливаются зависимости, а затем выполняются тесты Django.
Чтобы настроить рабочий пример, необходимо настроить сайт Django с двумя базами данных. В примере предполагается , что файл manage.py находится в корневом каталоге, а проект Django также находится в этом каталоге. В противном случае может потребоваться обновить /__w/1/s/
путь /__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