Поделиться через


Контейнеры служб

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