Learn (Almost) Anything
КаталогСкачать курс

Автоматизация в GitHub

Job containers, Docker service containers для PostgreSQL/Redis/queues, health checks, ports, volumes, credentials и отличия от Docker container actions.

ruЭнциклопедия33 урока6 модулей9
GitHub actionsАвтоматизация в GitHubМодель workflowсостояние и артефактыматрицы и runtimeрелизы и environmentsПереиспользование и собственные actionsgovernance и эксплуатацияgithubactions

Containers и service containers

Job containers, Docker service containers для PostgreSQL/Redis/queues, health checks, ports, volumes, credentials и отличия от Docker container actions.

Containers и service containers

В GitHub Actions слово «container» встречается в нескольких разных местах, и из-за этого легко перепутать три механизма. jobs.<job_id>.container задаёт container image, внутри которого выполняются run steps job. jobs.<job_id>.services поднимает соседние service containers: PostgreSQL, Redis, MySQL, MinIO, Elasticsearch, очереди, mock API. Docker container action — это уже переиспользуемый action с action.yml, который запускается как отдельный action, а не как база данных рядом с тестами.

Если коротко: job container — это «где выполняется мой test runner», service container — «какие временные зависимости нужны тестам», Docker container action — «упакованная команда, которую я вызываю через uses». Эта граница важна и для CI pipeline, и для Self-hosted runners, потому что контейнеры упрощают runtime, но не превращают workflow в полноценный Kubernetes или Docker Compose.

flowchart TD R[Ubuntu runner / Docker host] --> JC[Job container: node:20] R --> PG[Service container: postgres:16] R --> RD[Service container: redis:7] JC <-->|hostname postgres:5432| PG JC <-->|hostname redis:6379| RD JC --> ST[run steps: install, migrate, test] subgraph Same Docker network JC PG RD end
flowchart TD
  R[Ubuntu runner / Docker host] --> JC[Job container: node:20]
  R --> PG[Service container: postgres:16]
  R --> RD[Service container: redis:7]
  JC <-->|hostname postgres:5432| PG
  JC <-->|hostname redis:6379| RD
  JC --> ST[run steps: install, migrate, test]

  subgraph Same Docker network
    JC
    PG
    RD
  end
Job container выполняет steps, а service containers живут рядом с ним в той же Docker-сети и доступны по label.

Job container: зафиксировать runtime job

Job container полезен, когда тестам нужна конкретная среда: например, node:20-bookworm-slim, python:3.12, ruby:3.3 или внутренний image с уже установленными browser dependencies. Runner всё равно нужен: на GitHub-hosted это обычно Ubuntu runner, который становится Docker host. Но run steps выполняются уже внутри указанного container image.

name: integration-ci

on:
  pull_request:
  push:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-24.04
    container:
      image: node:20-bookworm-slim
      env:
        NODE_ENV: test
      options: --cpus 2

    steps:
      - uses: actions/checkout@v4
      - run: node --version
      - run: npm ci
      - run: npm test

Есть два практичных нюанса. Во-первых, default shell для run steps внутри container — sh, а не bash; если скрипты используют bash-синтаксис, задавайте shell: bash или defaults.run.shell. Во-вторых, если step вызывает container action, такой action запускается как sibling container на той же Docker-сети и с теми же volume mounts, а не «внутри» вашего job container.

Service containers: временная инфраструктура для одного job

Service container живёт только в рамках одного job. GitHub создаёт отдельный Docker container для каждого сервиса из services, даёт steps доступ к этим сервисам и удаляет их после завершения job. Это хорошо подходит для integration tests: база не production, Redis пустой, состояние воспроизводимое, секреты минимальны.

Пример: Node-тесты внутри job container, рядом PostgreSQL и Redis.

jobs:
  integration:
    runs-on: ubuntu-24.04
    container: node:20-bookworm-slim

    services:
      postgres:
        image: postgres:16
        env:
          POSTGRES_USER: app
          POSTGRES_PASSWORD: app
          POSTGRES_DB: app_test
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

      redis:
        image: redis:7
        options: >-
          --health-cmd "redis-cli ping"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm run test:integration
        env:
          DATABASE_URL: postgres://app:app@postgres:5432/app_test
          REDIS_URL: redis://redis:6379

Обратите внимание на hostnames: postgres и redis — это labels из services. Когда job сам работает в container, GitHub соединяет job container и service containers через Docker bridge network. Внутри этой сети сервис доступен по label, а порты между контейнерами открыты без явного ports. Поэтому postgres:5432, а не localhost:5432.

Когда нужен localhost и ports

Если job выполняется прямо на runner machine, без container:, картина меняется. Ваши run steps работают на host, а PostgreSQL или Redis сидят в Docker container. Тогда порт нужно опубликовать на Docker host, и приложение подключается к localhost.

jobs:
  integration:
    runs-on: ubuntu-24.04

    services:
      postgres:
        image: postgres:16
        env:
          POSTGRES_PASSWORD: app
          POSTGRES_DB: app_test
        ports:
          - 5432:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - run: npm run test:integration
        env:
          DATABASE_URL: postgres://postgres:app@localhost:5432/app_test

Это частый источник падений: workflow переносит тесты из runner job в job container, но оставляет localhost; или наоборот, убирает container:, но продолжает ходить на postgres. Проверка простая: если job container есть — hostname равен service label. Если job container нет — публикуйте порт и используйте localhost:<port>.

Если host port не указан, GitHub может выбрать свободный порт сам, а номер доступен через context вроде job.services.redis.ports[6379]. Это удобно, когда нужно избежать конфликтов, но в обычном CI чаще проще явно закрепить порт.

ИнтерактивКак подключаться к service container#int-1
Выберите, какой hostname обычно должен использовать test step.
service label
localhost
Разложите ситуации по правильному адресу подключения: label сервиса или localhost.

Health checks: ждать готовность сервиса, а не только старт контейнера

Docker container может быть «запущен», но база ещё применяет init scripts, Redis ещё не отвечает, Elasticsearch ещё прогревается. Поэтому для сервисов почти всегда стоит задавать options с Docker health check: --health-cmd, --health-interval, --health-timeout, --health-retries.

Для PostgreSQL типичный минимум:

options: >-
  --health-cmd pg_isready
  --health-interval 10s
  --health-timeout 5s
  --health-retries 5

Для Redis:

options: >-
  --health-cmd "redis-cli ping"
  --health-interval 10s
  --health-timeout 5s
  --health-retries 5

Health check не заменяет миграции и seed. Он только отвечает на вопрос «сервис принимает соединения?». Если тестам нужна схема БД, добавьте отдельный step: npm run db:migrate, rails db:test:prepare, alembic upgrade head, prisma migrate deploy — что принято в вашем стеке.

Volumes, credentials и private images

У container и services можно задавать env, ports, volumes, options, а для private registry — credentials. Для job container GitHub поддерживает named volumes, anonymous volumes и bind mounts с абсолютным host path. Но в CI лучше относиться к volumes осторожно: чем больше локального state, тем труднее понять, почему job проходит на одном runner и падает на другом. Это особенно заметно на Self-hosted runners, где host не обязательно чистый между jobs.

Private image выглядит так:

container:
  image: ghcr.io/acme/ci-node:20
  credentials:
    username: ${{ github.actor }}
    password: ${{ secrets.GHCR_READ_TOKEN }}

Для service container синтаксис похож:

services:
  testdb:
    image: ghcr.io/acme/postgres-with-extensions:16
    credentials:
      username: ${{ github.repository_owner }}
      password: ${{ secrets.GHCR_READ_TOKEN }}

Секреты здесь остаются обычными secrets из Variables, env и secrets. Не кладите production credentials в test services. Если image нужен только для CI, лучше дать токен с read-only доступом к registry, чем универсальный PAT.

Отличие от Docker container actions

Docker container action создаётся как action с metadata-файлом action.yml или action.yaml, где описаны inputs, outputs и runs. Его вызывают через uses, как любую другую action из Marketplace actions и pinning. Он хорош для переиспользуемой логики: генерация отчёта, custom scanner, упаковка артефакта, специфичный CLI с зависимостями.

Service container не вызывается через uses и не имеет action.yml. Он не «выполняет шаг», а предоставляет сетевой сервис для steps. Поэтому PostgreSQL как services.postgres — нормально. PostgreSQL как Docker container action — почти всегда неверная модель.

Есть ещё ограничение по платформе: job containers, service containers и Docker container actions требуют Linux runner. На GitHub-hosted runners это означает Ubuntu. На self-hosted — Linux machine с установленным Docker. Для Windows/macOS jobs используйте обычные setup-actions, внешние тестовые сервисы или отдельные Linux jobs, которые передают результат через Artifacts и reports.

Практичная эвристика

Используйте job container, когда важен одинаковый runtime для команд: версия языка, системные библиотеки, CLI, headless browser dependencies. Используйте service containers, когда тестам нужна временная инфраструктура: база, cache, queue, fake S3, SMTP catcher, browser grid. Используйте Docker container action, когда хотите переиспользовать команду как action в разных repositories.

Если схема становится похожа на большой docker-compose.yml с пятью сервисами, кастомными сетями, bind mounts и init-order, остановитесь и пересмотрите дизайн. Иногда лучше собрать отдельный test image. Иногда — поднять ephemeral environment перед job. Иногда — разделить Matrix strategy, чтобы тяжёлые integration tests не тормозили быстрый feedback loop. Containers в Actions должны делать CI pipeline воспроизводимее, а не превращать его в скрытую staging-инфраструктуру.

See also

  • CI pipeline — где service containers обычно появляются в build/test цепочке.
  • GitHub-hosted runners — Ubuntu runners как Docker host для container jobs.
  • Self-hosted runners — почему Docker на своём runner требует отдельной эксплуатационной дисциплины.
  • Matrix strategy — как тестировать несколько версий PostgreSQL, Redis или runtime.
  • Variables, env и secrets — как передавать connection strings и registry credentials.
  • Artifacts и reports — как передавать результаты между jobs вместо шаринга локального state.
  • Docker container actions — когда container становится переиспользуемым action, а не сервисом для тестов.
  • Secure use и threat model — почему контейнеры не отменяют модель угроз CI.

Внешние справки: GitHub Docs по job containers, service containers, примерам PostgreSQL, Redis и metadata syntax для actions.

Быстрое повторение #rc-3

Что произойдёт со service containers после завершения job?

Показать ответ

GitHub удалит service containers после завершения этого job. Их состояние не предназначено для шаринга между jobs.

Быстрое повторение #rc-2

Почему localhost:5432 часто ломает тесты после переноса job в jobs.<job_id>.container?

Показать ответ

Внутри job container localhost указывает на сам job container, а не на service container. Для сервиса нужно использовать hostname по label, например postgres:5432.

Быстрое повторение #rc-1

В GitHub Actions job использует container: node:20-bookworm-slim и services.postgres. Какой hostname должен быть в DATABASE_URL для PostgreSQL?

Показать ответ

Нужно подключаться к postgres:5432, где postgres — label из services. В job container сервисы доступны по service label внутри общей Docker bridge network.

Источники

  1. GitHub Docs — Running jobs in a container
  2. GitHub Docs — Communicating with Docker service containers
  3. GitHub Docs — Creating PostgreSQL service containers
  4. GitHub Docs — Creating Redis service containers
  5. GitHub Docs — Creating a Docker container action
  6. GitHub Docs — Metadata syntax for GitHub Actions