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

Azure DevOps Services

Если для конвейера требуется поддержка одной или нескольких служб, во многих случаях требуется создать, подключиться к каждой службе и очистить каждую службу по каждому заданию. Например, конвейер может выполнять тесты интеграции, которым требуется доступ к базе данных и кэшу памяти. Для каждого задания в конвейере необходимо создать базу данных и кэш памяти.

Контейнер позволяет легко запускать службу, от которой зависит конвейер, с возможностью переноса. Контейнер службы позволяет автоматически создать контейнерную службу, подключить ее, а также управлять ею. Каждый контейнер службы доступен только заданию, которому он требуется. Контейнеры служб работают с заданиями любого типа, но чаще всего используются с заданиями контейнеров.

Требования

Контейнеры служб должны определять CMD или ENTRYPOINT. Конвейер будет выполняться docker run для предоставленного контейнера без дополнительных аргументов.

Azure Pipelines может запускать контейнеры Linux или Windows. Используйте размещенные контейнеры Ubuntu для Linux или размещенный пул контейнеров Windows для контейнеров Windows. (Размещенный пул macOS не поддерживает запущенные контейнеры.)

Задание одного контейнера

Простой пример использования заданий контейнера:

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).

Альтернативный подход — позволить случайным портам динамически назначаться во время выполнения. Затем вы можете получить доступ к этим динамическим портам с помощью переменных. В скрипте Bash можно получить доступ к переменной с помощью среды процессов. Эти переменные принимают вид : agent.services.<serviceName>.ports.<port>. В приведенном выше примере 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. Тома могут называться томами Docker, анонимными томами Docker или привязывать подключения на узле.

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

Тома принимают форму <source>:<destinationPath>, где <source> может быть именованным томом или абсолютным путем на хост-компьютере, а <destinationPath> — абсолютным путем в контейнере.

Примечание

Если вы используете наши размещенные пулы, то тома не будут сохраняться между заданиями, так как главный компьютер будет очищен после завершения задания.

Другие варианты

Контейнеры служб используют те же ресурсы контейнеров, что и задания контейнеров. Это означает, что можно использовать те же дополнительные параметры.

Проверка работоспособности

При необходимости, если какой-либо контейнер службы указывает 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