GitHub Actions спростив service containers: навіщо потрібні entrypoint і command і як прибрати CI-костилі

CI/CDGitHubDockerDevOpsTesting

Коротко: GitHub додав entrypoint і command для service containers у GitHub Actions. Якщо ти коли-небудь збирав окремий Docker-образ тільки щоб передати один прапорець базі даних у CI — ця зміна саме для тебе. Частину старих обхідних рішень тепер можна просто вимкнути.

Що саме змінилося

У квітні 2026 року GitHub оновив модель service containers у GitHub Actions: у секції jobs.<job_id>.services з’явилися два нових ключі — entrypoint і command. Вони працюють за моделлю, знайомою кожному, хто писав docker compose: entrypoint заміняє головну команду старту контейнера, а command передає їй аргументи.

Це означає, що для більшості типових сценаріїв — налаштування конфігурації БД, зміна портів, передача спеціальних прапорців — вже не потрібен окремий wrapper-образ чи додатковий крок у workflow.

Який біль це прибирає

Уявімо типову ситуацію: тобі потрібен Postgres у CI з певними налаштуваннями — наприклад, змінити shared_buffers, увімкнути розширення або поставити специфічну кодування. Раніше існувало кілька варіантів, і жоден не був ідеальним.

Варіант перший — зібрати кастомний Docker-образ, в якому в ENTRYPOINT або CMD зашиті потрібні прапорці. Це працює, але це зайва підтримка: образ треба білдити, пушити в registry, слідкувати за оновленнями базового образу Postgres.

Варіант другий — написати shell-скрипт у workflow, який піднімає сервіс руками через docker run. Це дає контроль, але робить workflow бруднішим і менш переносимим.

Варіант третій — покладатися на env і options, сподіваючись, що сервіс зрозуміє те, що ти йому передав. Часто це працює, але не завжди, особливо коли треба змінити саме команду запуску, а не лише змінні середовища.

Тепер усе це закривається двома рядками в YAML. Без wrapper-образів. Без shell-hacks. Без магії.

Приклади: як це виглядає на практиці

Postgres із специфічними прапорцями

Як було — кастомний образ або options:

services:
  postgres:
    image: my-registry/postgres-custom:16-with-extensions
    env:
      POSTGRES_PASSWORD: test
    ports:
      - 5432:5432

Як стало — нативний command:

services:
  postgres:
    image: postgres:16
    env:
      POSTGRES_PASSWORD: test
    ports:
      - 5432:5432
    command: >-
      postgres
      -c shared_buffers=256MB
      -c max_connections=200

Тобі більше не потрібно підтримувати окремий Docker-образ з одним Dockerfile, який лише додає -c у CMD.

MySQL з нестандартним entrypoint

Припустимо, тобі треба, щоб MySQL стартував із певним init-скриптом або з обходом дефолтного entrypoint:

services:
  mysql:
    image: mysql:8
    env:
      MYSQL_ROOT_PASSWORD: test
    ports:
      - 3306:3306
    entrypoint: ["docker-entrypoint.sh"]
    command: ["--character-set-server=utf8mb4", "--collation-server=utf8mb4_unicode_ci"]

Коли entrypoint відрізняється від дефолтного, command передається йому як аргументи — так само, як у Docker Compose.

etcd для distributed-тестів

etcd часто потребує специфічних прапорців при старті, особливо якщо тести перевіряють розподілену логіку:

services:
  etcd:
    image: bitnami/etcd:latest
    env:
      ALLOW_NONE_AUTHENTICATION: "yes"
    ports:
      - 2379:2379
    command: ["etcd", "--advertise-client-urls", "http://localhost:2379"]

Без цього раніше доводилося або знаходити спеціальний образ, або городити shell-скрипт.

Redis з кастомним конфігом

services:
  redis:
    image: redis:7
    ports:
      - 6379:6379
    command: ["redis-server", "--maxmemory", "256mb", "--maxmemory-policy", "noeviction"]

Коли command достатньо, а коли треба міняти entrypoint

Правило просте.

Досить command, якщо:

  • сервіс стартує нормально з дефолтного entrypoint;
  • тобі треба лише передати аргументи або прапорці;
  • ти змінюєш конфігурацію, яка підтримується через командний рядок.

Це покриває 80–90% випадків із базами даних і кешами в CI.

Треба міняти entrypoint, якщо:

  • сервіс ігнорує command через свій Dockerfile (не які все Dockerfile, але буває);
  • тобі треба запустити wrapper-скрипт перед основним процесом;
  • ти міняєш саму команду старту, а не лише її аргументи.

Custom image все ще потрібен, якщо:

  • потрібні попередньо встановлені розширення або бібліотеки;
  • треба змінити файлову систему контейнера до запуску;
  • є складна процедура ініціалізації, яку не можна передати через командний рядок.

Чесні обмеження

Це не революція і не вирішення всіх проблем з CI. Варто тримати в голові кілька моментів:

  • Service containers у GitHub Actions працюють тільки на Linux-раннерах. Якщо твій workflow запускається на Windows або macOS runner — це не працюватиме.
  • entrypoint і command не замінюють options. Для монтування volume, налаштування мережі або інших Docker-flagів все ще потрібен options.
  • Не всі сервіси добре поводяться зі зміною entrypoint чи command через CI. Деякі офіційні образи мають дуже специфічну логіку в docker-entrypoint.sh, і її зміна може зламати ініціалізацію.
  • Це не стосується containerized jobs (де весь job працює в контейнері) — тільки service containers.

Що перевірити у своїх workflows завтра

Пройдися по своїх .github/workflows/ і постав собі кілька простих питань:

  1. Чи є у мене service containers, які використовують кастомні образи тільки для передачі прапорців?
  2. Чи є у мене docker run всередині workflow, який можна замінити на нативний service container з command?
  3. Чи мій wrapper-образ містить лише зміну CMD або ENTRYPOINT? Якщо так — пора його вимкнути.
  4. Чи перевіряю я тести після зміни? Сервіс може стартувати інакше, і це варто побачити на CI, а не на production.

Якщо знайшов хоча б один випадок, де кастомний образ можна замінити двома рядками YAML — це вже перемога. Менше образів — менше підтримки — менше місць, де щось тихо ламається.

Що не робити

  • Не видаляй кастомні образи одразу — спочатку перевір, що новий підхід дає однаковий результат у тестах.
  • Не міняй entrypoint без гострої потреби. Якщо досить command — використовуй command.
  • Не забувай, що options все ще потрібен для монтування volume, мережі та інших Docker-налаштувань.
  • Не припускай, що всі офіційні образи однаково реагують на зміну entrypoint — завжди тестуй.

Практичний висновок

GitHub прибрав один із найбільш дратівливих костилів у CI. Якщо твоя команда тримає інтеграційні тести в GitHub Actions і піднімає там БД або кеш — варто витратити 15 хвилин і перевірити, які wrapper-образи чи shell-hacks тепер можна прибрати без ризику.

Найкраще, що ця зміна не вимагає нічого нового: ти продовжуєш писати YAML, просто без зайвих шарів абстракції навколо сервісів, які й так вміють говорити через командний рядок.

Короткий чеклист

  • Пройдися по всіх workflows і знайди wrapper-образи, створені лише для передачі прапорців сервісу
  • Заміни їх на нативні entrypoint / command де це можливо
  • Переконайся, що кастомний образ дійсно потрібен у випадках зі складною підготовкою середовища
  • Продивись тести після кожного workflow — переконайся, що сервіс стартує і відповідає коректно
  • Задокументуй зміни, щоб колеги розуміли новий підхід до налаштування service containers

Prompt Pack: аудит service containers у GitHub Actions

Ти — платформовий інженер, який допомагає командам підтримувати CI/CD у GitHub Actions. Твоє завдання — переглянути поточні GitHub Actions workflows проєкту і знайти місця, де service containers (Postgres, Redis, MySQL, etcd тощо) запускаються через кастомні wrapper-образи або додаткові кроки тільки для того, щоб передати прапорці чи змінити команду запуску сервісу. Вхідні дані: - список файлів .github/workflows/*.yml; - поточні service containers у кожному workflow; - перелік кастомних Docker-образів, що використовуються лише для передачі аргументів сервісу. Потрібно: - для кожного workflow визначити, чи можна замінити wrapper-образ на нативні entrypoint / command; - показати диф "як було → як стало" із новим YAML; - відзначити випадки, коли custom image все ще потрібен (наприклад, складна підготовка середовища); - коротко описати ризики і що перевірити після зміни. Формат відповіді: - таблиця або список: workflow → поточний стан → що змінити → очікуваний результат; - окремий блок "де custom image ще виправданий"; - чіткий checklist для безпечного переходу.