Введение#
Если вам понравилась настоящая статья, то можете поддержать автора став спонсором на бусти (ссылка в разделе контакты).
Когда речь заходит про контейнеры, девять человек из десяти по умолчанию имеют в виду Docker. Он действительно долгие годы был синонимом контейнеризации: docker run, docker-compose up и Dockerfile - это тот минимальный набор, который знает практически любой, кто хоть раз разворачивал сервис не на голом железе. Но у Docker есть архитектурная особенность, из-за которой у части сообщества, включая меня, к нему постепенно накопились вопросы - это постоянно работающий демон с root-правами.
Podman - это попытка решить именно эту проблему, причём не косметически, а на уровне архитектуры. Это контейнерный движок без демона, с полноценным rootless-режимом из коробки и нативной интеграцией с systemd. В моем homelab он давно заменил Docker, и в этой статье я хочу разобрать, откуда Podman взялся, как он устроен внутри, чем принципиально отличается от Docker, и что такое поды, Quadlet и Podlet - термины, которые обычно и вызывают больше всего путаницы.
Откуда взялся Podman#
История у Podman не самая очевидная, и знать её полезно, чтобы понимать, почему проект устроен именно так.
Всё началось в 2017 году внутри проекта CRI-O (container runtime для Kubernetes) с небольшой утилиты под рабочим названием kpod - её делали инженеры Red Hat, которым нужен был способ отлаживать и инспектировать контейнеры, созданные CRI-O, без необходимости поднимать полноценный Docker. Имя kpod никому в команде не нравилось, и через несколько месяцев разработки инструмент выделили в отдельный проект - библиотеку libpod, которая отвечала за управление подами и контейнерами без демона. А ещё через несколько месяцев, в начале 2018 года, вышел первый публичный релиз уже под новым именем - Podman, что расшифровывается как “POD MANager”.
Важный нюанс: Podman - это не форк Docker. Это независимая реализация, написанная с нуля, которая при этом использует те же низкоуровневые стандарты и во многом те же компоненты экосистемы, что и Docker:
runcилиcrun- низкоуровневый OCI-runtime, который непосредственно запускает контейнер;conmon- лёгкий супервизор, следящий за процессом контейнера;containers/storage- библиотека для хранения образов и слоёв;containers/image- работа с реестрами и форматами образов.
Сегодня Podman разрабатывается под эгидой Red Hat, входит в состав проекта containers на GitHub и является контейнерным движком по умолчанию в RHEL, Fedora, CentOS Stream и ряде других дистрибутивов, где Docker в принципе не поставляется из коробки уже несколько лет.
Для каких задач нужен Podman#
По сути Podman закрывает те же задачи, что и Docker - сборка образов, запуск контейнеров, работа с реестрами - но с прицелом на другие сценарии использования:
- локальная разработка и тестирование контейнеризованных приложений;
- запуск контейнеров и подов на серверах и в homelab, особенно там, где хочется обойтись без демона;
- эксплуатация контейнеров без root-доступа, что критично для multi-tenant окружений;
- генерация Kubernetes-манифестов прямо из работающих контейнеров - удобно, если Kubernetes только планируется;
- замена Docker в CI/CD, где Podman может запускаться внутри самого CI-раннера без привилегированного демона.
На практике чаще всего Podman выбирают для домашних лабораторий и небольших серверов с повышенными требованиями к безопасности, для edge-устройств с ограниченными ресурсами, и там, где Kubernetes уже используется или явно стоит в планах - генерация манифестов сильно упрощает миграцию.
Архитектура: почему нет демона#
Ключевая архитектурная особенность Podman - daemonless-модель. У Docker есть dockerd, постоянно работающий в фоне процесс с root-правами, который принимает команды от клиента и управляет жизненным циклом всех контейнеров. У Podman такого процесса нет вообще: команда podman run напрямую, без посредников, вызывает conmon и runc/crun, которые и запускают контейнер как обычный дочерний процесс системы.
Из этого вытекает несколько практических следствий:
- контейнер Podman в
psвиден как самостоятельный процесс, а не как что-то, спрятанное внутри демона - это заметно упрощает отладку; - если что-то упало в одном контейнере, это не затрагивает остальные и уж тем более не требует перезапуска общего демона;
- пропадает единая точка отказа: не станет демона - не станет и всех контейнеров разом, как иногда бывает с Docker после сбойного обновления
dockerd. - systemd воспринимает такие процессы естественно, без обходных путей вроде
docker runвнутриExecStart- об этом подробнее поговорим в разделе про Quadlet.
Rootless-контейнеры и сеть#
Rootless-режим в Podman - не костыль поверх существующей архитектуры, а то, ради чего проект во многом и затевался. Контейнер можно запустить от обычного, непривилегированного пользователя, и при этом внутри контейнера у процесса всё равно будет “свой” root - просто это не настоящий root хостовой системы.
Технически это реализовано через:
- user namespaces - подмену UID/GID внутри контейнера на непривилегированные диапазоны на хосте;
- subuid/subgid - диапазоны идентификаторов, которые администратор выделяет пользователю для этой подмены (
/etc/subuid,/etc/subgid); - сетевой стек в user space, который не требует привилегий на хосте.
Тут стоит сделать отдельную ремарку про сеть, потому что она за последние пару лет заметно изменилась. Раньше rootless-сеть в Podman почти всегда означала slirp4netns - рабочее, но довольно медленное решение. Начиная с Podman 5.x в качестве современной замены продвигается pasta - она заметно быстрее, полноценно поддерживает IPv6 и, что особенно приятно, “отражает” сетевую конфигурацию хоста прямо в контейнер вместо классического NAT. В актуальных версиях Podman pasta уже используется как бэкенд для rootless-сети по умолчанию, а поддержка slirp4netns в новых релизах постепенно сворачивается. Если у вас старый конфиг с явным указанием slirp4netns - самое время свериться с man podman-network для своей версии и по возможности перейти на pasta.
Итог такого подхода: даже если контейнер скомпрометируют, атакующий окажется не в root-окружении хоста, а в изолированном namespace обычного пользователя - потенциальный ущерб принципиально меньше.
Чем Podman отличается от Docker#
Сравнение по ключевым параметрам#
| Критерий | Docker | Podman |
|---|---|---|
| Архитектура | Клиент-демон (dockerd) | Без демона |
| Права демона | root | демона нет |
| Rootless | Есть, но требует отдельной настройки и имеет ограничения | Полноценный режим “из коробки” |
| Интеграция с systemd | Через обходные пути | Нативная (Quadlet) |
| Kubernetes-манифесты | Нужны сторонние инструменты | Встроенная генерация YAML |
| Поды | Нет как первичной сущности | Есть, как в Kubernetes |
| CLI | Docker CLI | Совместим с Docker CLI |
Совместимость с Docker#
Пожалуй, лучшая новость для тех, кто переходит с Docker - переучиваться почти не придётся. CLI Podman намеренно сделан максимально похожим:
alias docker=podmanПосле такого алиаса подавляющее большинство привычных команд просто продолжает работать:
podman run -d -p 8080:80 nginx
podman build -t myapp .
podman pull docker.io/library/redis
podman push myapp registry.example.com/myappФормат Dockerfile тоже поддерживается без каких-либо изменений - Podman умеет собирать образы из обычных Dockerfile, дополнительно предлагая свой более гибкий формат Containerfile (по сути то же самое, просто другое имя файла).
Поды (Pods) в Podman#
Что такое под#
Под (pod) - это группа из одного или нескольких контейнеров, которые:
- разделяют один сетевой namespace, то есть общий IP-адрес и общее пространство портов;
- при желании могут совместно использовать IPC-namespace;
- логически представляют собой одно приложение, которое удобно запускать и останавливать как единое целое.
Концепция полностью заимствована из Kubernetes, и это не случайность, а осознанное архитектурное решение - Podman изначально проектировался так, чтобы модель локальной разработки была максимально близка к тому, как приложение потом будет вести себя в кластере. Типичный под - это, например, контейнер с приложением, контейнер с reverse-proxy перед ним и sidecar-контейнер для логирования или метрик, которые вместе образуют один логический сервис.
Пример: создаём под руками#
podman pod create --name web-pod -p 8080:80
podman run -d --pod web-pod nginx
podman run -d --pod web-pod busybox sleep infinityВсе контейнеры внутри web-pod получают один общий IP-адрес и видят друг друга через localhost, как процессы на одной машине - можно смело обращаться друг к другу по 127.0.0.1:<порт> без настройки отдельной docker-сети, как пришлось бы делать в Docker.
Из практических плюсов такого подхода: сетевое взаимодействие внутри пода становится тривиальным, конфигурация ближе к модели Kubernetes, а сами контейнеры логически группируются, что удобно и для мониторинга, и для последующей миграции.
Генерация Kubernetes-манифестов#
Отдельная сильная сторона Podman - умение прямо из работающего пода сгенерировать готовый Kubernetes YAML:
podman generate kube web-pod > pod.yamlЭто позволяет собрать и обкатать архитектуру приложения локально на одной машине, а потом перенести конфигурацию в кластер без переписывания с нуля - Podman в этом смысле неплохо работает как “локальный Kubernetes” для разработки и тестирования.
Podman и systemd#
Podman нативно умеет интегрироваться с systemd - и делать это можно двумя способами.
Первый, более старый - генерация unit-файла из уже созданного контейнера или пода:
podman generate systemd --name nginx --files --newКоманда создаёт .service-файл, который можно положить в systemd-юниты и управлять контейнером стандартными systemctl start/stop/enable. Этот способ рабочий, но требует, чтобы контейнер уже существовал, и плохо подходит для декларативного описания инфраструктуры “с нуля”.
Второй, современный и рекомендуемый способ - Quadlet.
Что такое Quadlet#
Quadlet - это встроенный в Podman механизм, который позволяет описывать контейнеры, поды, сети, volume’ы и образы декларативно, обычными systemd unit-файлами специального формата. systemd на старте сам транслирует такие файлы в полноценные .service-юниты и запускает их через Podman - вручную ничего генерировать не нужно.
Всего Quadlet поддерживает несколько типов файлов:
| Расширение | Что описывает |
|---|---|
.container | отдельный контейнер |
.pod | под (группу контейнеров) |
.volume | именованный volume |
.network | сеть |
.image | образ, который нужно заранее подтянуть |
.build | сборку образа из Containerfile |
.kube | развёртывание из Kubernetes YAML (podman kube play) |
.artifact | OCI-артефакт |
Файлы размещаются в одном из стандартных путей, в зависимости от того, нужен ли системный (root) или пользовательский (rootless) сервис:
/etc/containers/systemd/ # системные квадлеты, root
~/.config/containers/systemd/ # пользовательские, rootlessПример .container-файла#
[Container]
Image=docker.io/library/nginx:latest
PublishPort=8080:80
Volume=nginx-data:/usr/share/nginx/html:Z
[Service]
Restart=always
[Install]
WantedBy=multi-user.targetПосле создания файла достаточно перечитать конфигурацию systemd и запустить сервис:
systemctl daemon-reload
systemctl start nginx.containerДальше это уже полноценный systemd-сервис: автозапуск при загрузке системы через [Install], единый журнал через journalctl -u nginx.container, управление зависимостями через стандартные After=/Requires=. Для homelab, где нет и не планируется Kubernetes, но есть systemd - это, на мой взгляд, оптимальный способ держать инфраструктуру контейнеров.
Podlet: помощник в написании Quadlet-файлов#
Писать .container-файлы руками не всегда удобно, особенно когда под рукой уже есть рабочая команда docker run или docker-compose.yml, которые не хочется переписывать с нуля. Для этого есть отдельный инструмент - Podlet (не путать с Quadlet, это разные вещи, хотя названия и похожи).
Podlet - самостоятельная утилита на Rust, которая не входит в состав самого Podman, но плотно с ним используется. Она умеет генерировать Quadlet-файлы тремя способами: из команды podman run (или docker run - синтаксис почти идентичен), из существующего compose-файла, и даже из уже запущенного контейнера, пода, сети или volume’а через podlet generate.
podlet podman run -d -p 8080:80 nginxКоманда выше выведет в консоль готовый .container-файл со всеми нужными секциями - остаётся сохранить его в правильную директорию. Для compose-файлов Podlet умеет либо разбивать сервисы на отдельные .container-файлы, либо собирать их в один .pod вместе с сопутствующими контейнерами - в зависимости от того, что ближе к исходной архитектуре приложения.
На практике Podlet особенно полезен именно на этапе миграции с Docker Compose: не нужно вручную разбираться в синтаксисе Quadlet-секций - достаточно скормить существующий docker-compose.yml, а дальше уже донастроить сгенерированный файл под себя.
Когда стоит выбирать Podman#
Из моего собственного опыта, Podman особенно оправдан, если:
- для вас важны безопасность и rootless-режим по умолчанию, а не как опция, которую нужно отдельно допиливать;
- на сервере и так используется systemd, и хочется управлять контейнерами теми же привычными командами
systemctl, а не отдельным демоном; - Kubernetes уже используется или явно в планах - переносить конфигурацию через
podman generate kubeудобнее, чем писать манифесты с нуля; - нужна daemonless-архитектура без единой точки отказа;
- вы строите homelab или self-hosted инфраструктуру и не хотите, чтобы всё держалось на одном привилегированном процессе.
Заключение#
Podman - это не “Docker, но бесплатно” и не клон ради клона. Это отдельная, довольно зрелая на сегодняшний день реализация контейнерного движка, которая решает конкретные архитектурные проблемы Docker: убирает демон с root-правами, делает rootless-режим полноценным, а не опциональным, и нативно встраивается в systemd через Quadlet.
Для homelab и небольших серверов переход с Docker на Podman у меня лично не потребовал почти никаких компромиссов - CLI совместим, Dockerfile работает как есть, а взамен я получил меньше точек отказа и намного более прозрачную интеграцию с systemd. Если вы давно присматриваетесь к Podman, но не решались - лучший способ узнать, подходит ли он вам, это просто попробовать на одном не самом критичном сервисе.
Полезные ссылки:





