强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

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 参数

参数行为
--newpodman 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 systemdQuadlet
配置方式先创建容器,再生成 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
Notifysd_notify 支持healthy
UserNS用户命名空间keep-id:uid=1000,gid=1000
ReadOnly只读根文件系统true
Tmpfstmpfs 挂载/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)
lingerRootless 用户必须启用以保持服务持久运行
AutoUpdateAutoUpdate=registry + podman auto-update

下一步


扩展阅读