Podman Quadlet позволяет описывать rootless-контейнеры, сети и тома в файлах конфигурации, а затем управлять ими через пользовательский systemd. Для одного сервиса или небольшого набора связанных контейнеров Docker Compose не требуется: достаточно создать файлы .container, .network и .volume, выполнить systemctl --user daemon-reload и запустить сгенерированный unit.
Практическая схема выглядит так: отдельный непривилегированный пользователь владеет конфигурацией и данными, Podman запускает контейнеры в rootless mode, systemd user manager отвечает за автозапуск, зависимости, перезапуск и журналы. Для запуска после выхода из SSH включают linger командой loginctl enable-linger USER.
В статье приведены рабочие шаблоны Quadlet, команды диагностики, правила работы с переменными окружения, healthcheck, резервными копиями и обновлением образов. Перед изменением production-конфигурации сохраните Quadlet-файлы, EnvironmentFile, данные томов, digest образа и вывод podman inspect.
Как Podman Quadlet запускает rootless-контейнеры через systemd
Quadlet использует декларативные файлы в каталоге ~/.config/containers/systemd. Пользовательский менеджер systemd читает эти описания и генерирует units, которыми можно управлять обычными командами systemctl --user. Сам Quadlet не запускает отдельный оркестратор: он связывает конфигурацию Podman с механизмами systemd.
Минимальный путь состоит из пяти действий:
- Создать файл
web.containerв~/.config/containers/systemd. - Выполнить
systemctl --user daemon-reload. - Запустить
web.service. - Проверить
systemctl --user status web.serviceиpodman ps. - Включить unit через
systemctl --user enable web.service.
Quadlet и Docker Compose: различия в модели управления
В Compose приложение обычно описывают в compose.yaml, а жизненный цикл контролируют командами Compose. Quadlet разделяет описание ресурсов по типам файлов и передает управление systemd. Это дает привычные для Linux-сервера механизмы: зависимости Requires и After, ограничения unit, журналирование через journalctl, ручной запуск и автозапуск через user manager.
Для одного веб-сервиса, прокси, агента мониторинга или небольшой связки из базы и приложения systemd часто удобнее. Сложная матрица переменных, масштабирование по узлам, rolling update и кластерное распределение требуют Compose, Kubernetes или другого оркестратора. Критерии выбора Docker, Podman и LXC разобраны в сравнении инструментов контейнеризации.
Какие файлы Quadlet используются в статье
| Файл | Назначение | Пример systemd unit |
|---|---|---|
.container | Описание контейнера, образа, портов, окружения и политики перезапуска | web.service |
.network | Описание пользовательской Podman-сети | app.network |
.volume | Описание named volume | data-volume.service |
Имя файла становится основой имени generated unit. Например, web.container превращается в web.service, а app.network описывает ресурс сети app. Поддерживаемые параметры зависят от версии Podman, поэтому перед переносом шаблона проверьте локальную документацию и результат генерации.
Подготовка rootless-среды Podman и systemd user manager
Выберите отдельного непривилегированного пользователя, например containers. Не запускайте его units через sudo systemctl: это обращается к system manager root и создает другую область конфигурации. Все команды Podman и systemctl --user выполняйте от владельца контейнеров.
Проверка Podman, rootless mapping и каталогов конфигурации
id containers
podman version
podman info
systemctl --user is-system-running
loginctl show-user containers
grep '^containers:' /etc/subuid
grep '^containers:' /etc/subgid
mkdir -p ~/.config/containers/systemd
mkdir -p ~/containers/{env,data,backup}
В /etc/subuid и /etc/subgid должны быть диапазоны идентификаторов для выбранного пользователя. Если mapping отсутствует, rootless-контейнеры могут не создавать пользователей, файлы или сетевые ресурсы. Проверьте владельца каталогов командой ls -ld ~/containers ~/.config/containers/systemd.
Сохраните версии Podman, ядра, systemd и сетевого backend до начала работы. Поддержка отдельных директив Quadlet меняется между релизами. Ошибка неизвестного ключа при daemon-reload обычно указывает на несовместимость синтаксиса.
Автозапуск пользователя через loginctl enable-linger
sudo loginctl enable-linger containers
loginctl show-user containers -p Linger
systemctl --user status
linger запускает systemd user manager без активной сессии пользователя. После этого unit может стартовать после перезагрузки сервера, если он включен через systemctl --user enable. Linger не исправляет ошибки Podman, права на тома, недоступный образ или неправильный healthcheck.
Пользовательские units работают в user.slice. Лимиты CPU и памяти можно задавать на уровне systemd, но сначала проверьте базовый запуск контейнера без ограничений.
Первый Podman Quadlet .container: запуск веб-сервиса
Минимальная структура файла .container
[Unit]
Description=Rootless web container
After=network-online.target
[Container]
Image= docker.io/library/nginx:1.27
ContainerName=web
PublishPort=8080:80
Environment=APP_ENV=production
Restart=on-failure
[Service]
Restart=on-failure
[Install]
WantedBy=default.target
В строке Image укажите образ без пробела после знака равенства. Фиксированный тег снижает риск непредсказуемого обновления, а digest дает более строгую фиксацию версии. PublishPort=8080:80 публикует порт контейнера 80 на непривилегированный порт 8080 хоста.
Создайте файл без расширения unit:
chmod 700 ~/.config/containers/systemd
chmod 600 ~/.config/containers/systemd/web.container
systemctl --user daemon-reload
systemctl --user enable --now web.service
systemctl --user status web.service
podman ps
В некоторых версиях Podman параметр Restart в секции [Container] и параметры systemd могут иметь разные поддерживаемые значения. Проверяйте generated unit и сообщения user manager после каждой правки.
Проверка сгенерированного systemd-юнита
systemctl --user cat web.service
systemctl --user show web.service
podman ps --format '{{.Names}} {{.Image}} {{.Status}}'
curl http://127.0.0.1:8080
systemctl --user cat показывает итоговый unit, который видит systemd. Это первый источник для проверки зависимостей, команды запуска и политики перезапуска. podman ps подтверждает состояние контейнера, а локальный curl отделяет проблему запуска от ошибки приложения или публикации порта.
Сеть и постоянные данные: .network и .volume
Пользовательская сеть через Quadlet .network
[Network]
NetworkName=app-net
Сохраните файл как app.network. Подключите контейнер:
[Container]
Image=docker.io/library/nginx:1.27
Network=app.network
PublishPort=8080:80
Контейнеры в одной пользовательской сети могут обращаться друг к другу по именам, если выбранный rootless-сетевой backend предоставляет нужное DNS-разрешение. Проверяйте это фактически, а не по одному факту запуска:
podman network ls
podman network inspect app-net
podman port web
Внутренний порт базы данных обычно не нужно публиковать на хост. Достаточно подключить приложение и базу к одной сети, а наружу вывести только порт прокси или веб-сервиса.
Том через Quadlet .volume и защита данных
[Volume]
VolumeName=web-data
Сохраните файл как web-data.volume, затем подключите volume в контейнере:
[Container]
Image=docker.io/library/nginx:1.27
Volume=web-data.volume:/usr/share/nginx/html:Z
Named volume отделяет данные от жизненного цикла контейнера. Удаление и пересоздание контейнера не должны удалять volume, но команду удаления ресурса всегда проверяйте перед выполнением:
podman volume ls
podman volume inspect web-data
podman volume mount web-data
Bind mount подходит, когда каталог должен находиться в заранее выбранном месте хоста. Для базы данных сохраните дамп, каталог данных, параметры доступа и процедуру восстановления. Резервная копия только Quadlet-файла не вернет содержимое volume.
Зависимости между .container, .network и .volume
Ссылка Network=app.network или Volume=web-data.volume:/path позволяет Quadlet создать связи между ресурсами. В секции [Unit] можно явно задать:
Requires=app.network
After=app.network
Wants=web-data-volume.service
After задает порядок запуска. Requires связывает успешный запуск зависимого ресурса с unit сервиса. Wants выражает мягкую зависимость. BindsTo используют, когда остановка или исчезновение связанного ресурса должна остановить текущий unit.
Порядок запуска не означает готовность приложения. Сеть может существовать, а база еще принимать только миграции. Для готовности используйте healthcheck и проверку приложения.
Переменные окружения, секреты и healthcheck
Environment и EnvironmentFile в .container
[Container]
Image=docker.io/library/nginx:1.27
Environment=APP_ENV=production
EnvironmentFile=%h/containers/env/web.env
APP_ENV=production
APP_PORT=80
Файл окружения должен принадлежать пользователю контейнера и иметь режим 600:
chown containers:containers ~/containers/env/web.env
chmod 600 ~/containers/env/web.env
systemctl --user daemon-reload
systemctl --user restart web.service
podman inspect web
Не помещайте пароли в командную строку, публичный репозиторий или отладочный вывод. Проверьте права EnvironmentFile, журналы приложения и доступ к сокету Podman. Секреты могут попасть в вывод systemctl show, inspect или диагностический архив.
Healthcheck контейнера и реакция systemd
[Container]
Image=docker.io/library/nginx:1.27
HealthCmd=curl -f http://127.0.0.1/ || exit 1
HealthInterval=30s
HealthTimeout=5s
HealthRetries=3
Команда проверки должна существовать внутри образа. Если в нем нет curl, используйте доступный инструмент или отдельную проверку через локальный HTTP-клиент.
podman inspect web --format '{{json .State.Health}}'
podman healthcheck run web
podman logs web
Состояние running означает, что основной процесс жив. Оно не подтверждает готовность HTTP API, соединение с базой или корректную загрузку конфигурации. Healthcheck сам по себе не во всех сценариях перезапускает контейнер. Свяжите его с политикой Restart и проверяйте поведение конкретной версии Podman.
Автозапуск, остановка и обслуживание сервисов
Команды жизненного цикла rootless-контейнера
systemctl --user enable web.service
systemctl --user start web.service
systemctl --user restart web.service
systemctl --user stop web.service
systemctl --user disable web.service
systemctl --user status web.service
systemctl --user list-units --type=service
systemctl --user reset-failed web.service
Используйте systemctl --user enable --now web.service, чтобы включить unit и запустить его одной командой. reload применяйте только тогда, когда конкретный сервис поддерживает перечитывание конфигурации. Для изменения Quadlet-файла обычно нужен daemon-reload и перезапуск.
Что происходит после изменения Quadlet-файла
- Скопируйте исходный файл в каталог резервных копий.
- Измените
.container,.networkили.volume. - Выполните
systemctl --user daemon-reload. - Проверьте результат через
systemctl --user cat web.serviceиsystemctl --user show web.service. - Перезапустите unit.
- Проверьте статус, логи, healthcheck и smoke-тест.
cp ~/.config/containers/systemd/web.container ~/containers/backup/web.container.$(date +%Y%m%d%H%M%S)
systemctl --user daemon-reload
systemctl --user restart web.service
journalctl --user -u web.service -n 100 --no-pager
Журналы и диагностика типовых ошибок
Ошибки генерации и запуска systemd-юнита
systemctl --user status web.service --no-pager
journalctl --user -u web.service -b --no-pager
systemctl --user cat web.service
systemctl --user show web.service -p LoadState -p ActiveState -p ExecStart
Начните с фактической ошибки. Если unit не найден, проверьте имя файла, каталог ~/.config/containers/systemd и результат daemon-reload. Если systemd отклоняет директиву, сверяйте синтаксис с установленной версией Podman. Если контейнер сразу завершается, продолжайте диагностику через Podman.
podman ps -a
podman logs web
podman inspect web
podman events --since 10m
Rootless-права, UID mapping и доступ к томам
id
ls -ln ~/containers/data
podman unshare cat /proc/self/uid_map
podman info --format '{{json .Host.Security}}'
getenforce
UID внутри контейнера может отличаться от UID на хосте. Проверьте владельца каталога, режимы доступа и путь монтирования. SELinux способен блокировать bind mount, а AppArmor может дополнительно запретить действие, которое Unix-права разрешают. При использовании профиля AppArmor собирайте фактические отказы в режиме наблюдения, затем переходите к enforce после штатного теста.
Проверяйте доступ к Unix-сокетам, устройствам, FUSE и специальным capabilities. Запуск от root иногда скрывает проблему с правами, но меняет модель изоляции и не подходит как универсальное исправление.
Порты, сеть и недоступность приложения
ss -ltnp
podman port web
podman network inspect app-net
curl -v http://127.0.0.1:8080
podman logs web
Разделите четыре причины: порт занят на хосте, контейнер не слушает внутренний порт, PublishPort задан неверно, приложение отклоняет запрос. Rootless-сценарий обычно использует непривилегированные порты выше 1024. Доступ к портам ниже 1024 требует отдельной настройки и меняет профиль риска.
В зависимости от конфигурации Podman использует slirp4netns или pasta. Их поведение по маршрутизации, производительности и доступу к хосту отличается, поэтому зафиксируйте backend в диагностическом отчете из podman info.
Контейнер запущен, но healthcheck не проходит
podman inspect web --format '{{json .Config.Healthcheck}}'
podman inspect web --format '{{json .State.Health}}'
podman exec web sh -c 'command -v curl'
podman exec web sh -c 'curl -v http://127.0.0.1/'
podman logs web
Проверьте путь проверки, наличие утилиты, задержку инициализации, DNS-имя зависимости и timeout. Увеличьте HealthTimeout только после измерения реального времени ответа. Длинный timeout скрывает зависание, а короткий создает ложные отказы.
Обновление образов с резервной копией и планом отката
Что сохранить перед изменением
- Quadlet-файлы
.container,.networkи.volume. - EnvironmentFile и список секретов без публикации их значений.
- Named volumes, bind mounts и дампы баз данных.
- Текущий image ID или digest.
- Вывод
podman inspect, статус unit и последние журналы. - Smoke-тест, подтверждающий рабочее состояние сервиса.
podman inspect web > ~/containers/backup/web.inspect.json
podman image inspect docker.io/library/nginx:1.27 > ~/containers/backup/web.image.json
podman volume inspect web-data > ~/containers/backup/web-data.volume.json
systemctl --user cat web.service > ~/containers/backup/web.service.txt
Для базы данных сначала создайте согласованный дамп. Файловая копия каталога работающей базы может оказаться непригодной для восстановления. Конфигурация без согласованной копии данных не образует полноценный backup.
Контролируемое обновление образа
Зафиксируйте рабочий digest в Quadlet или используйте проверенный тег. Перед обновлением скачайте образ и отдельно проверьте его метаданные:
podman pull docker.io/library/nginx:1.27
podman image inspect docker.io/library/nginx:1.27
podman ps --no-trunc
После проверки примените изменение через предусмотренный для вашей версии Podman способ пересоздания контейнера. Для простой конфигурации последовательность выглядит так:
systemctl --user stop web.service
systemctl --user daemon-reload
systemctl --user start web.service
systemctl --user status web.service
podman inspect web --format '{{.ImageName}} {{.Image}}'
Проверьте миграции схемы базы данных до обновления. Рестарт контейнера не заменяет миграцию, резервную копию и функциональную проверку.
Откат после неудачного обновления
- Остановите unit и зафиксируйте журналы сбоя.
- Верните прежний тег или digest в Quadlet.
- Выполните
systemctl --user daemon-reload. - Запустите unit и проверьте image ID.
- Проверьте healthcheck, логи и функциональный запрос.
- Восстановите данные только по заранее проверенной процедуре.
systemctl --user stop web.service
# вернуть прежний digest в web.container
systemctl --user daemon-reload
systemctl --user start web.service
journalctl --user -u web.service -b -n 100 --no-pager
podman inspect web --format '{{json .State.Health}}'
После необратимой миграции базы откат образа может не вернуть совместимость. В таком случае потребуется восстановление backup и повторная проверка приложения на прежней версии.
Риски и границы применения rootless Podman
Безопасность: что rootless решает, а что не решает
Rootless mode снижает привилегии процесса контейнера и отделяет его от root-пользователя хоста. Это уменьшает последствия части ошибок конфигурации. Изоляция не отменяет Unix-права, SELinux или AppArmor, firewall, обновление пакетов, ограничения systemd и контроль секретов.
- Проверьте
subuidиsubgid. - Ограничьте права каталогов данных и EnvironmentFile.
- Не подключайте Podman socket к контейнеру без четкой причины.
- Не выдавайте capabilities и доступ к устройствам без проверки.
- Настройте systemd hardening после успешного функционального теста.
- Проверьте SELinux или AppArmor в среде конкретного сервера.
Безопасное ужесточение делайте поэтапно: сначала соберите фактическое поведение в тестовой среде, затем включайте ограничения и проверяйте штатные сценарии, обновление и восстановление.
Когда Quadlet подходит, а когда нужен другой инструмент
Quadlet подходит для одного хоста, нескольких сервисов, контролируемого systemd-жизненного цикла и rootless-запуска от отдельного пользователя. Он удобен там, где уже есть процессы эксплуатации через units и journalctl.
Для кластеризации, autoscaling, распределения по узлам, сложных rollout-процессов и автоматического размещения используйте Kubernetes или специализированный оркестратор. Практика настройки probes и securityContext в Kubernetes приведена в руководстве по конфигурации Pod.
Если нужен сервер для тестирования rootless-стека, контейнеров и резервных копий, подойдет облачная инфраструктура с управляемыми VDS, хранилищем и Kubernetes, например Timeweb Cloud. Ресурсы выбирайте по измеренному потреблению CPU, RAM, диска и сети.
Итоговый рабочий чек-лист Podman Quadlet
Проверка перед вводом в эксплуатацию
- Определен отдельный непривилегированный пользователь и проверены версии Podman и systemd.
- Настроены
subuid,subgid, права каталогов и rootless-сетевой backend. - Созданы backup данных, Quadlet-файлов, EnvironmentFile, image digest и вывода inspect.
- Сеть и volume описаны отдельными файлами и проверены через
podman network inspectиpodman volume inspect. - Контейнерный unit прошел
daemon-reload, запуск и проверку generated unit. - Включен
loginctl enable-linger USER, а unit добавлен в автозапуск. - Healthcheck проверяет реальную готовность сервиса, а не только наличие процесса.
- Проверены журналы, восстановление после остановки и доступность данных после пересоздания.
- Документированы pull, обновление, smoke-тест и rollback.
- После тестов проверены SELinux, AppArmor, systemd hardening, firewall и доступ к секретам.
Рабочий порядок прост: подготовьте rootless-среду, сохраните данные, создайте сеть и volume, опишите контейнер, выполните daemon-reload, включите linger, запустите unit и проверьте healthcheck. После этого зафиксируйте процедуру обновления и отката в базе знаний команды.
Quadlet дает предсказуемый способ управлять Podman через systemd, если размер задачи соответствует возможностям одного хоста. Для переноса существующих Compose-проектов полезно сопоставить сервисы, сети, тома и зависимости, а затем проверить каждый ресурс отдельно. Практические различия Compose-конфигураций разобраны в руководстве по Docker Compose.