Как собрать pod с учетом зависимостей в Podman - Академия Selectel

Как собрать pod с учетом зависимостей в Podman

Тирекс
Тирекс Самый зубастый автор
7 апреля 2026

Podman почти как Docker, только Podman. В этой статье разберемся, как работать с зависимостями, чтобы ничего не сломать и все запустить.

Изображение записи

Когда ваше приложение состоит из нескольких сервисов, например бэкенда, БД и кэша, невольно возникает вопрос — а как гарантировать, что они запустятся в правильно порядке и вообще увидят друг друга? В Docker это решается с помощью depends_on, тут ничего нового. А вот в Podman подход немного другой. Ну кто бы сомневался!

Автор статьи — Роман Шубин, CTO и автор Telegram-канала Bash Days.

В Podman, насколько я знаю, нет аналога depends_on, как в том же Docker Compose. Но есть механизмы, с помощью которых это все успешно заменяется. У меня получилась такая схема:

  • pods — общая среда для контейнеров,
  • systemd / Quadlet — управление порядком запуска,
  • healthcheck + ожидание готовности,
  • скрипты ожидания (wait-for-it и т. д.).

Звучит страшно, но кое-что из этого мы уже разбирали в прошлых статьях. Healthcheck и systemd / Quadlet — база, которая необходима для полного понимания процессов.

Простой пример

Поехали ковыряться. Допустим, у нас есть максимально понятный кейс: база должна стартовать первая, потому что клиент зависит от нее:


      podman run -d --name db -e POSTGRES_PASSWORD=secret postgres
podman run --rm postgres pg_isready -h db -p 5432

При выполнении второй команды вы увидите:


      db:5432 - no response

Это может показаться странным, ведь контейнер db уже запущен. В чем тогда проблема? Запущенный контейнер ≠ сервис готов. Когда вы запускаете PostgreSQL, контейнер стартует быстро, но внутри идет инициализация, создаются файлы, поднимается сервер. Это может занимать продолжительное время.

Соответственно, вторая команда пытается подключиться к базе, но ничего не получается. Порт 5432 еще не активен и не принимает соединения. Podman — это не Kubernetes, который ждет готовности другого контейнера. Если вы не учитываете это явно, то получаете нестабильный запуск.

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

Создаем сеть:


      podman network create mynet

Снова запускаем:


      podman run -d --name db --network mynet -e POSTGRES_PASSWORD=secret postgres

И пробуем подключиться:


      podman run --rm --network mynet postgres pg_isready -h db -p 5432

Не забываем указать сеть, в которой будут работать контейнеры. У меня это mynet.

Команда проверки готовности базы данных pg_isready внутри пода Podman. Результат: localhost:5432 - accepting connections.

Все, добились правды. В первые пять секунд получаем no response, затем все хорошо.

В Podman зависимости между контейнерами — это не только порядок запуска. Сначала нужно обеспечить, чтобы контейнеры вообще «видели» друг друга и только потом решать проблему готовности сервисов. Едем дальше.

Пример посложнее

Что такое pod, я рассказывать не буду. Это базовая база, о которой не написал еще только ленивый. Если коротко, pod’ы делят одну сеть, видят друг друга как localhost и могут использовать общие ресурсы. Внутри pod’ов контейнеры не общаются по имени, они общаются через localhost внутри одного неймспейса. По сути, это концепция Kubernetes.

Создаем pod:


      podman pod create --name mypod -p 5432:5432

Здесь создался общий network namespace, порт 5432 прокинут наружу.

Проверка доступности PostgreSQL в поде Podman с помощью утилиты pg_isready.

Запускаем контейнер внутри нашего нового pod’а:


      podman run -d --pod mypod --name db -e POSTGRES_PASSWORD=secret postgres

Проверяем подключение внутри pod’а:


      podman run --rm --pod mypod postgres pg_isready -h localhost -p 5432
Скриншот терминала с командой podman run --pod mypod и успешным ответом localhost:5432 - accepting connections.

Магия! Все работает через localhost, никаких DNS, создания сетей и обращений по именам.

Это все прекрасно. Правда, pod решает проблему сети, но не решает проблему готовности. Вот этой командой, например, можно запустить все сразу:


      podman run --pod mypod postgres pg_isready -h localhost

Но мы получим ожидаемый результат — no_response:

Попытка обращения к базе данных внутри пода, возвращающая no response.

Ошибочка. База не успела запуститься. Podman решает проблему связности контейнеров через pod, но ответственность за порядок запуска и готовность сервисов остается на вас. Опять никакой магии.

Давайте решать эту проблему. Можно все обернуть в wait-логику:


      until pg_isready -h localhost -p 5432; do
   echo "Waiting for database..."
   sleep 1
 done

 echo "Database is ready!"

Для Podman это будет выглядеть так:


      podman run --rm --pod mypod postgres sh -c 'until pg_isready -h localhost -p 5432; do sleep 1; done; echo ready'

Кишка какая-то получилась, но рабочая. По итогу получаем, что в цикле будет крутиться проверка, а как только база запустится, клиент выдаст ready:

Скриншот терминала с выполнением Bash-скрипта внутри пода. Виден цикл ожидания с многократными сообщениями no response, который завершается статусом accepting connections и финальным словом ready.

Получился так называемый «ручной depends_on». То есть, костыль. Но поверьте моему опыту, такие костыли постоянно делают, потому что это просто, быстро и очевидно. А самое главное — это работает. Что еще надо?

Надежность запуска в Podman достигается не порядком запуска контейнеров, а проверкой готовности зависимых сервисов.

Нет, так не пойдет. Надо сделать по-взрослому, без условной Bash-кишки и циклов. Давайте посмотрим в сторону systemd и Quadlet.

Можно, конечно, сделать так для app.service:


      [Unit]
 Description=My App
 Requires=db.service
 After=db.service

 [Container]
 Image=postgres
 Exec=sh -c '
 until pg_isready -h localhost -p 5432; do
   echo "waiting for db"
   sleep 1
 done

 echo "DB is ready"
 sleep infinity
 '

 [Install]
 WantedBy=multi-user.target

По сути, мы поменяем шило на мыло. Вариант рабочий, но его отбрасываем и смотрим немного в другую сторону. В сторону Notify=healthy. Эта штука будет связывать healthcheck контейнера с systemd. Systemd будет считать сервис «запущенным», только когда контейнер стал healthy.

Как это будет работать

Systemd запускает db, Podman ждет, пока healthcheck станет healthy, и только потом запустится app.service. Вот тут мы практически приблизились к концепту depends_on. Пилим реализацию.

db.container


      [Unit]
Description=PostgreSQL container

[Container]
Image=postgres
ContainerName=db
Environment=POSTGRES_PASSWORD=secret

HealthCmd=pg_isready -U postgres
HealthInterval=5s
HealthRetries=5

Notify=healthy

[Install]
WantedBy=multi-user.target

app.container


      [Unit]
Description=My App
Requires=db.service
After=db.service

[Service]
Restart=always

[Container]
Image=postgres
Exec=sh -c "echo app started && sleep infinity"

[Install]
WantedBy=multi-user.target

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

Podman дает гибкость, можно решать зависимости вручную, а можно делегировать их systemd через healthcheck.
Почему нельзя просто сделать depends_on, как в Docker? Потому что даже depends_on не ждет готовности. Он следит только за порядком запуска.

Ни Docker, ни Podman, ни systemd не решат проблему готовности за вас. Единственный надежный способ — проверять ее явно.

Решаем проблему управления зависимостями

Так, мы начинали про pod’ы, но может показаться, что как-то плавно скатились в контейнерную рутину. Но это не так. Ключевая мысль тут в том, что pod и зависимости — это разные уровни задачи.

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

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


      [Unit]
Description=My Pod

[Pod]
PodName=mypod
PublishPort=5432:5432

[Install]
WantedBy=multi-user.target

И уже к этому pod’у привязываем нужные контейнеры:


      [Container]
Image=postgres
ContainerName=db
Pod=mypod
Environment=POSTGRES_PASSWORD=secret

Абстракция на абстракции. Но если вы работаете с Podman, то наверняка с этим регулярно сталкиваетесь.

В Podman зависимости между контейнерами — это комбинация нескольких уровней:

  • systemd управляет порядком запуска,
  • само приложение отвечает за ожидание зависимостей,
  • healthcheck показывает реальное состояние.

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