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

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

下一步


扩展阅读