痛点
在非 Kubernetes 的服务器环境中(边缘节点、单体应用服务器、CI Runner),我们通常用 docker-compose 编排容器。但它带来几个尴尬问题:
- compose 是个额外守护进程层 — 服务启停依赖
docker compose up -d,与系统的 init 体系割裂,重启后需要额外 enable - 日志管理分裂 — 容器日志走 docker driver,系统日志走 journald,排查故障两头看
- 依赖管理弱 — compose 的
depends_on只做启动顺序,不做健康依赖,复杂场景照样翻车 - Rootless 场景受限 — docker-compose 在 rootless 模式下坑多,权限、网络各种不兼容
Podman 4.4+ 引入的 Quadlet 彻底解决了这些问题 — 把容器声明为 systemd unit 文件,让容器成为系统的一等公民。
方案
Quadlet 的核心思路:写一个 .container 文件,systemd generator 自动将其转换为标准的 .service unit。容器的生命周期完全由 systemd 管理 — 启停、重启、依赖、日志、资源限制全部复用 systemd 的成熟机制。
核心优势:
- 零额外守护进程,开机自启和 systemctl 原生管理
- 日志统一进 journald,journalctl -u 一条命令搞定
- 支持 .volume、.network、.kube(Pod YAML)多种单元类型
- Rootless 天然支持,用户级 systemd 即可运行
实操步骤
第 1 步:创建 Quadlet 配置目录
# 系统级(root)
sudo mkdir -p /etc/containers/systemd
# 用户级(rootless,推荐)
mkdir -p ~/.config/containers/systemd
第 2 步:编写 .container 文件
以部署一个 Redis 实例为例,创建 ~/.config/containers/systemd/redis.container:
[Unit]
Description=Redis Cache Server
After=network-online.target
[Container]
Image=docker.io/library/redis:7-alpine
ContainerName=redis-cache
PublishPort=6379:6379
Volume=redis-data.volume:/data
Environment=REDIS_ARGS=--maxmemory 256mb --maxmemory-policy allkeys-lru
# 健康检查
HealthCmd=redis-cli ping
HealthInterval=10s
HealthRetries=3
[Service]
Restart=always
TimeoutStartSec=60
[Install]
WantedBy=default.target
第 3 步:创建对应的 Volume 文件
创建 ~/.config/containers/systemd/redis-data.volume:
[Volume]
VolumeName=redis-data
[Install]
WantedBy=default.target
第 4 步:加载并启动服务
# 重新加载 systemd generator
systemctl --user daemon-reload
# 启动容器(注意:unit 名自动变为 redis.service)
systemctl --user start redis.service
# 设置开机自启
systemctl --user enable redis.service
# 检查状态
systemctl --user status redis.service
# 查看日志
journalctl --user -u redis.service -f
进阶:多容器编排(替代 docker-compose)
假设需要部署 Web 应用 + Redis + PostgreSQL,通过 systemd 依赖链实现:
~/.config/containers/systemd/webapp.container:
[Unit]
Description=Web Application
After=redis.service postgres.service
Requires=redis.service postgres.service
[Container]
Image=ghcr.io/myorg/webapp:latest
ContainerName=webapp
PublishPort=8080:8080
Network=app.network
Environment=REDIS_URL=redis://redis-cache:6379
Environment=DATABASE_URL=postgresql://app:secret@pg-db:5432/app
# 引用其他容器的网络
PodmanArgs=--requires redis-cache,pg-db
[Service]
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=default.target
共享网络文件 ~/.config/containers/systemd/app.network:
[Network]
NetworkName=app-net
Subnet=10.89.1.0/24
[Install]
WantedBy=default.target
避坑指南
坑 1:daemon-reload 后 unit 不出现
Quadlet generator 只在 daemon-reload 时运行。如果文件有语法错误,unit 不会生成且无报错。排查方法:
# 手动运行 generator 查看输出
/usr/lib/systemd/system-generators/podman-system-generator --dryrun --user
# 或者用专用调试工具
podman-quadlet --dryrun --user
坑 2:容器间 DNS 解析失败
Quadlet 默认每个容器独立网络。多容器互访必须显式声明 .network 文件并在所有相关 .container 中引用:
[Container]
Network=app.network
容器间通过 ContainerName 互相解析,不是 unit 名称。
坑 3:Rootless 模式端口低于 1024
Rootless 容器默认无法绑定 80/443 端口。解决:
# 方案一:调整内核参数
sudo sysctl net.ipv4.ip_unprivileged_port_start=80
# 方案二:使用 socket activation(推荐)
# 创建 webapp.socket 文件配合 systemd socket activation
总结
Podman Quadlet 把容器管理从"应用层编排"降维到"系统级服务管理",核心收益:
| 对比项 | docker-compose | Podman Quadlet |
|---|---|---|
| 守护进程 | 依赖 dockerd | 无(systemd 直管) |
| 开机自启 | 需额外配置 | systemctl enable |
| 日志 | docker logs | journalctl 统一 |
| 依赖管理 | depends_on(弱) | systemd Requires/After(强) |
| 资源限制 | compose 配置 | cgroup v2 原生 |
| Rootless | 受限 | 完整支持 |
适用场景:边缘节点、单机部署、CI/CD Runner、开发环境 — 任何不需要 K8s 但又想用容器的地方。如果你的服务器上还在跑 docker-compose up -d,是时候迁移到 Quadlet 了。