Podman 完全指南 / 06 - Systemd 集成
第 06 章 — Systemd 集成
6.1 为什么需要 Systemd 集成?
在生产环境中,容器需要像传统服务一样:
- 开机自启
- 自动重启(崩溃恢复)
- 日志管理(journald)
- 资源控制(cgroup 限制)
- 依赖管理(启动顺序)
Podman 与 systemd 的深度集成是其最重要的生产特性之一,Docker 无法原生提供。
传统容器管理: Systemd 管理:
┌──────────────┐ ┌──────────────┐
│ 手动启动 │ │ systemctl │
│ 自写脚本 │ │ enable/start│
│ cron 健康检查│ │ 原生集成 │
│ 自己管日志 │ │ journald │
└──────────────┘ └──────────────┘
脆弱易错 稳定可靠
6.2 generate systemd(传统方式)
6.2.1 基本用法
# 先创建并运行一个容器
podman run -d --name webserver -p 8080:80 nginx:1.27-alpine
# 生成 systemd 服务文件
podman generate systemd --name webserver --files
# 输出:
# /home/user/container-webserver.service
# 查看生成的服务文件
cat container-webserver.service
6.2.2 服务文件分析
# container-webserver.service
# generated by Podman 5.x.x
[Unit]
Description=Podman container-webserver.service
Documentation=man:podman-generate-systemd(1)
Wants=network-online.target
After=network-online.target
RequiresMountsFor=/run/user/1000/containers
[Service]
Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=on-failure
TimeoutStartSec=70
TimeoutStopSec=70
ExecStart=/usr/bin/podman start webserver
ExecStop=/usr/bin/podman stop -t 10 webserver
ExecStopPost=/usr/bin/podman stop -t 10 webserver
PIDFile=/run/user/1000/containers/overlay-containers/<id>/userdata/conmon.pid
Type=forking
[Install]
WantedBy=default.target
6.2.3 安装与管理
# 移动到 systemd 用户目录
mkdir -p ~/.config/systemd/user/
mv container-webserver.service ~/.config/systemd/user/
# 重新加载 systemd
systemctl --user daemon-reload
# 启动服务
systemctl --user start container-webserver
# 查看状态
systemctl --user status container-webserver
# 开机自启
systemctl --user enable container-webserver
# 查看日志
journalctl --user -u container-webserver
# 停止
systemctl --user stop container-webserver
6.2.4 Pod 级别的 Systemd 服务
# 创建 Pod
podman pod create --name myapp -p 8080:80
podman run -d --pod myapp --name web nginx:1.27-alpine
podman run -d --pod myapp --name app myapp:latest
# 生成 Pod 级别的 systemd 文件
podman generate systemd --name myapp --files --new
# 这会为 Pod 和每个容器都生成服务文件
# 包含正确的依赖顺序(pod 先启动)
6.2.5 –new 参数
| 参数 | 行为 |
|---|---|
无 --new | podman start/stop 操作已有容器 |
--new | 每次启动创建新容器,停止后销毁(推荐) |
# --new 模式(推荐):每次启动都是全新容器
podman generate systemd --name webserver --new --files
# 优势:
# 1. 每次启动拉取最新镜像(配合 Restart=always)
# 2. 无状态,不会累积旧容器数据
# 3. 更符合容器"一次性"的设计理念
⚠️ 注意
podman generate systemd在较新版本中标记为 legacy。Red Hat 推荐使用 Quadlet(见下一节)作为新的容器 systemd 集成方案。
6.3 Quadlet(推荐方式)
Quadlet 是 Podman 4.4+ 引入的全新 systemd 集成方案,使用声明式 .container、.volume、.network 文件替代传统的 generate systemd。
6.3.1 Quadlet vs generate systemd
| 维度 | generate systemd | Quadlet |
|---|---|---|
| 配置方式 | 先创建容器,再生成 unit | 声明式 .container 文件 |
| 可读性 | 低(自动生成的 shell 命令) | 高(类似 Docker Compose) |
| 维护成本 | 修改容器需重新生成 | 直接编辑 .container 文件 |
| 多容器编排 | 每个容器一个文件 | 支持 .kube 文件 |
| 卷/网络管理 | 分别创建 | .volume / .network 声明 |
| 推荐程度 | Legacy(维护模式) | ✅ 推荐 |
6.3.2 Quadlet 文件位置
用户级:~/.config/containers/systemd/
系统级:/etc/containers/systemd/
6.3.3 容器定义(.container)
创建 ~/.config/containers/systemd/webserver.container:
# webserver.container
[Unit]
Description=My Web Server
After=network-online.target
Wants=network-online.target
[Container]
Image=docker.io/library/nginx:1.27-alpine
PublishPort=8080:80
Volume=web-content.volume:/usr/share/nginx/html:ro
Environment=NGINX_HOST=example.com
Label=app=webserver
AutoUpdate=registry
[Service]
Restart=always
RestartSec=5
[Install]
WantedBy=default.target
6.3.4 卷定义(.volume)
创建 web-content.volume:
# web-content.volume
[Volume]
Label=app=webserver,type=content
6.3.5 网络定义(.network)
创建 mynet.network:
# mynet.network
[Network]
Subnet=10.89.0.0/24
Gateway=10.89.0.1
Label=app=mynetwork
6.3.6 在 .container 中使用自定义网络
# app.container
[Unit]
Description=My Application
After=network-online.target webserver.service
[Container]
Image=docker.io/library/myapp:v2.0
PublishPort=8080:8080
Network=mynet.network
Volume=app-data.volume:/data:Z
Environment=DATABASE_URL=postgres://localhost:5432/mydb
Secret=database-credentials.secret
HealthCmd=/app/healthcheck
HealthInterval=30s
HealthRetries=3
AutoUpdate=registry
[Service]
Restart=always
RestartSec=10
TimeoutStartSec=120
[Install]
WantedBy=default.target
6.3.7 Quadlet 关键指令
[Container] 段
| 指令 | 说明 | 示例 |
|---|---|---|
Image | 镜像 | nginx:1.27-alpine |
PublishPort | 端口映射 | 8080:80 |
Volume | 卷挂载 | data.volume:/app/data:Z |
Network | 网络 | mynet.network |
Environment | 环境变量 | KEY=value |
Secret | 密钥 | db-password.secret |
Label | 标签 | app=web |
AutoUpdate | 自动更新策略 | registry / local |
Exec | 覆盖入口点 | /app/start.sh |
PodmanArgs | 额外 podman 参数 | --memory 512m --cpus 1 |
HealthCmd | 健康检查命令 | curl -f http://localhost/ |
HealthInterval | 健康检查间隔 | 30s |
HealthRetries | 健康检查重试次数 | 3 |
Notify | sd_notify 支持 | healthy |
UserNS | 用户命名空间 | keep-id:uid=1000,gid=1000 |
ReadOnly | 只读根文件系统 | true |
Tmpfs | tmpfs 挂载 | /tmp:size=100m |
HostName | 主机名 | web01 |
AddHost | 添加 host 映射 | db:10.0.0.5 |
Pull | 拉取策略 | always / never / newer |
Timezone | 时区 | Asia/Shanghai |
6.3.8 启用与管理
# 重新加载 systemd 以识别新的 Quadlet 文件
systemctl --user daemon-reload
# Quadlet 会自动生成对应的服务:
systemctl --user start webserver.service
# 查看状态
systemctl --user status webserver.service
# 开机自启
systemctl --user enable webserver.service
# 查看日志
journalctl --user -u webserver.service -f
# 检查生成的 podman 命令(调试用)
/usr/libexec/podman/quadlet --dry-run
6.3.9 Kubernetes YAML 集成
Quadlet 还支持 .kube 文件,直接管理 K8s YAML:
# myapp.kube
[Unit]
Description=My App Stack (Kubernetes YAML)
After=network-online.target
[Kube]
YAML=/path/to/deployment.yaml
PublishPort=8080:8080
AutoUpdate=registry
[Install]
WantedBy=default.target
6.4 生产场景
场景一:Nginx 反向代理 + 应用
# ~/.config/systemd/system/nginx-proxy.container
[Unit]
Description=Nginx Reverse Proxy
After=network-online.target
[Container]
Image=docker.io/library/nginx:1.27-alpine
PublishPort=80:80
PublishPort=443:443
Volume=nginx-conf.volume:/etc/nginx/conf.d:ro
Volume=certs.volume:/etc/nginx/certs:ro
Network=frontend.network
AutoUpdate=registry
[Service]
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.target
# ~/.config/systemd/system/backend-app.container
[Unit]
Description=Backend Application
After=network-online.target postgres.service
[Container]
Image=registry.example.com/app:v2.1.0
Network=frontend.network
Network=backend.network
Environment=DB_HOST=postgres
Environment=REDIS_HOST=redis
Volume=app-logs.volume:/app/logs:Z
HealthCmd=/app/healthcheck.sh
HealthInterval=30s
AutoUpdate=registry
PodmanArgs=--memory 1g --cpus 2
[Service]
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
场景二:数据库持久化服务
# postgres-prod.container
[Unit]
Description=PostgreSQL Database
After=network-online.target
[Container]
Image=docker.io/library/postgres:16-alpine
PublishPort=5432:5432
Volume=pgdata.volume:/var/lib/postgresql/data:Z
Environment=POSTGRES_DB=appdb
Environment=POSTGRES_USER=appuser
Secret=db-password.secret,type=env,target=POSTGRES_PASSWORD
Network=backend.network
HealthCmd=pg_isready -U appuser -d appdb
HealthInterval=10s
HealthRetries=5
HealthStartPeriod=30s
PodmanArgs=--shm-size=256m
[Service]
Restart=always
RestartSec=30
TimeoutStartSec=120
[Install]
WantedBy=multi-user.target
场景三:定时更新镜像
# 创建 systemd timer 定期更新镜像
cat > ~/.config/systemd/user/update-images.timer << 'EOF'
[Unit]
Description=Update container images weekly
[Timer]
OnCalendar=Sun *-*-* 03:00:00
Persistent=true
[Install]
WantedBy=timers.target
EOF
cat > ~/.config/systemd/user/update-images.service << 'EOF'
[Unit]
Description=Update container images
[Service]
Type=oneshot
ExecStart=/usr/bin/podman pull nginx:1.27-alpine
ExecStart=/usr/bin/podman pull postgres:16-alpine
ExecStart=/usr/bin/podman image prune -f
EOF
systemctl --user daemon-reload
systemctl --user enable --now update-images.timer
6.5 持久登录(Rootless)
# Rootless 用户需要启用 linger 才能在未登录时保持服务运行
sudo loginctl enable-linger $(whoami)
# 验证
ls /var/lib/systemd/linger/
# 应该包含你的用户名
# 禁用
sudo loginctl disable-linger $(whoami)
⚠️ 重要
如果不启用 linger,用户注销后 systemd 会停止所有用户级服务。这在服务器环境中是常见问题。
6.6 AutoUpdate(自动更新)
Quadlet 的 AutoUpdate=registry 配合 podman auto-update 实现自动拉取新镜像:
# 检查有哪些容器可自动更新
podman auto-update --dry-run
# 执行更新
podman auto-update
# 设置 systemd timer 定期检查
# 通常已由发行版提供:podman-auto-update.timer
systemctl --user enable --now podman-auto-update.timer
# 查看更新日志
journalctl --user -u podman-auto-update.service
6.7 本章小结
| 知识点 | 要点 |
|---|---|
| generate systemd | 传统方式,从已有容器生成 unit 文件 |
| Quadlet | 推荐方式,声明式 .container/.volume/.network |
| Quadlet 位置 | ~/.config/containers/systemd/ |
| 关键文件 | .container(容器)、.volume(卷)、.network(网络)、.kube(K8s YAML) |
| linger | Rootless 用户必须启用以保持服务持久运行 |
| AutoUpdate | AutoUpdate=registry + podman auto-update |
下一步
- 👉 第 07 章:容器网络 — 掌握 Podman 网络管理