解决 systemd 系统中用户退出后 podman 容器自动退出的问题


在使用 podman 运行容器时,很多开发者会遇到一个常见的问题:当用户从 SSH 会话退出后,已经启动的容器会自动停止。
这个问题在使用 systemd 作为初始化系统的 Linux 发行版(如 Fedora、RHEL、CentOS、Ubuntu 等)上尤为常见。

本文将详细分析这个问题的原因,并提供几种可靠的解决方案。

问题现象

当你通过 SSH 连接到远程服务器,使用 podman 启动容器后:

# 启动一个 nginx 容器
podman run -d --name my-nginx -p 8080:80 nginx

# 确认容器正在运行
podman ps

# 退出 SSH 会话
exit

重新登录之后,你会发现容器已经停止了:

# 查看所有容器(包括已停止的)
podman ps -a

# 发现 my-nginx 容器的状态为 Exited

问题原因

这个问题的根本原因在于 systemd 的用户会话管理

1. 用户会话与进程清理

在 systemd 系统中,当用户通过 SSH 登录后,会创建一个用户会话(user session)。systemd 默认会在用户退出时清理该会话中的所有进程,以防止进程变成“孤儿进程”。

这个行为由 systemd-logind 服务控制,其配置项为 KillUserProcesses

2. 查看当前配置

可以通过以下命令查看 systemd-logind 的配置:

# 查看 KillUserProcesses 配置(推荐)
grep -i "^KillUserProcesses" /etc/systemd/logind.conf

# 或者查看所有配置项(包括注释的)
grep -i "KillUserProcesses" /etc/systemd/logind.conf

如果输出显示 KillUserProcesses=yes 或被注释掉(默认值),则系统会在用户退出时终止所有用户进程。

3. Linger 机制

systemd 提供了一个叫做 linger 的机制,允许特定用户的进程在用户退出登录后继续运行。

检查当前用户是否启用了 linger:

# 查看当前用户的 linger 状态
ls -la /var/lib/systemd/linger/$(whoami) 2>/dev/null && echo "Linger enabled" || echo "Linger disabled"

# 或者使用 loginctl 查看
loginctl show-user $(whoami) | grep Linger

如果输出 Linger=no 或文件不存在,说明未启用 linger。

4. Podman 容器的运行机制

Podman 以无守护进程的方式运行,容器进程直接作为用户进程的子进程运行。因此,当用户会话结束时,容器进程也会被 systemd 一并清理。

解决方案

方案一:启用 Linger(最简单,推荐)

这是最简单且推荐的解决方案,特别适用于个人开发环境和单用户场景。

步骤 1:启用当前用户的 linger

# 为当前用户启用 linger
sudo loginctl enable-linger $(whoami)

步骤 2:验证 linger 状态

# 查看是否启用成功
loginctl show-user $(whoami) | grep Linger
# 应该输出:Linger=yes

# 或者查看 linger 文件
ls -la /var/lib/systemd/linger/$(whoami)

步骤 3:测试容器运行

# 启动一个测试容器
podman run -d --name test-nginx -p 8080:80 nginx

# 确认容器运行中
podman ps

# 退出 SSH 会话
exit

重新登录后,检查容器是否仍在运行:

podman ps

禁用 Linger(如需要)

sudo loginctl disable-linger $(whoami)

优点

  • ✅ 配置简单,一条命令即可
  • ✅ 不需要修改系统配置文件
  • ✅ 不需要重启系统或服务
  • ✅ 立即生效
  • ✅ 只影响指定用户,不影响其他用户

缺点

  • ⚠️ 用户的所有进程都会在退出后继续运行(可能占用资源)
  • ⚠️ 需要定期清理不再需要的进程

适用场景:个人开发环境、测试环境、单用户服务器

方案二:修改 systemd-logind 配置(全局生效)

如果你需要为所有用户解决这个问题,可以修改系统级配置。

步骤 1:编辑 logind 配置文件

sudo vim /etc/systemd/logind.conf

步骤 2:修改或添加配置项

找到 KillUserProcesses 配置项,将其设置为 no

[Login]
KillUserProcesses=no

如果该行被注释(前面有 #),请取消注释。

步骤 3:重启 systemd-logind 服务

sudo systemctl restart systemd-logind

注意:在某些系统上,可能需要重启整个系统才能使配置生效。

验证配置

# 确认配置文件已修改
grep -i "^KillUserProcesses" /etc/systemd/logind.conf
# 应该输出:KillUserProcesses=no

优点

  • ✅ 全局生效,所有用户都受益
  • ✅ 配置一次,永久有效

缺点

  • ⚠️ 需要 root 权限
  • ⚠️ 可能需要重启才能生效
  • ⚠️ 影响所有用户的行为

适用场景:多用户服务器、需要统一管理的生产环境

这种方式更加规范,适合生产环境,可以让容器随系统启动而自动运行。

前提条件:建议先启用 linger(参照方案一),以确保用户服务在后台持续运行。

步骤 1:生成 Kubernetes YAML 配置

# 先运行容器
podman run -d --name my-nginx -p 8080:80 nginx

# 生成 systemd 服务文件
mkdir -p ~/.config/containers/systemd
podman generate systemd --new --name my-nginx > ~/.config/containers/systemd/container-my-nginx.service

步骤 2:启用并启动服务

# 重新加载 systemd 配置
systemctl --user daemon-reload

# 启用服务(开机自启)
systemctl --user enable container-my-nginx.service

# 启动服务
systemctl --user start container-my-nginx.service

# 查看服务状态
systemctl --user status container-my-nginx.service

步骤 3:启用 linger(关键步骤)

为了让用户服务在用户退出后继续运行,需要启用 linger:

# 为当前用户启用 linger
sudo loginctl enable-linger $(whoami)

启用 linger 后,即使用户退出登录,systemd 用户服务也会继续运行。

管理服务

# 停止容器
systemctl --user stop container-my-nginx.service

# 启动容器
systemctl --user start container-my-nginx.service

# 重启容器
systemctl --user restart container-my-nginx.service

# 禁用开机自启
systemctl --user disable container-my-nginx.service

方案三:使用 systemd 用户服务管理容器(生产环境推荐)

这种方式更加规范,适合生产环境,可以让容器随系统启动而自动运行。

前提条件:建议先启用 linger(参照方案一),以确保用户服务在后台持续运行。

使用 nohup

nohup podman run -d --name my-nginx -p 8080:80 nginx &

使用 disown

# 启动容器
podman run -d --name my-nginx -p 8080:80 nginx

# 将作业从 shell 作业列表中移除
disown -a

注意:这种方法虽然可以让容器在后台运行,但不够规范,也不便于管理多个容器。

方案四:使用 nohup 或 disown(临时方案,不推荐)

这是一种简单的临时解决方案,适合测试环境。

使用 nohup

nohup podman run -d --name my-nginx -p 8080:80 nginx &

使用 disown

# 启动容器
podman run -d --name my-nginx -p 8080:80 nginx

# 将作业从 shell 作业列表中移除
disown -a

使用后台运行符 &

podman run -d --name my-nginx -p 8080:80 nginx &

⚠️ 重要警告

如果不启用 linger,以上方法都无法阻止容器在退出 SSH 时被终止!

原因:

  1. nohupdisown& 只是让进程在当前 shell 中后台运行
  2. 它们无法改变进程的父子关系和会话归属
  3. 当用户退出时,systemd-logind 会清理该用户的所有进程,无论是否使用了这些工具
  4. 这些工具防的是 SIGHUP 信号(shell 退出时发送),但防不了 systemd 的进程清理

实际测试

# 场景 1:未启用 linger,使用 nohup
nohup podman run -d --name test1 nginx &
exit
# 重新登录后:容器已停止 ❌

# 场景 2:未启用 linger,使用 disown
podman run -d --name test2 nginx &
disown
exit
# 重新登录后:容器已停止 ❌

# 场景 3:启用了 linger
sudo loginctl enable-linger $(whoami)
podman run -d --name test3 nginx &
exit
# 重新登录后:容器仍在运行 ✅

结论

  • 这些方法只有在 已经启用 linger 的前提下才有效
  • 如果没有启用 linger,任何 “技巧” 都无法阻止 systemd 清理进程
  • 必须先启用 linger,然后这些后台运行方式才有意义

方案五:使用 root 模式运行容器

缺点

  • ❌ 容器内的进程以 root 权限运行,安全性降低
  • ❌ 违背了 Podman 的无 root 设计理念
  • ❌ 不推荐使用

适用场景:仅在特殊需求下使用,一般不推荐

方案对比

方案 优点 缺点 适用场景
启用 Linger 简单快速,无需修改系统配置,立即生效 只影响单个用户,需手动启用 个人开发/测试环境(首选)
修改 KillUserProcesses 全局生效,配置统一 需 root 权限,可能需重启,影响所有用户 多用户服务器、统一管理环境
systemd 用户服务 规范管理,支持开机自启,可精细控制 配置较复杂,建议配合 linger 使用 生产环境(推荐)
nohup/disown 快速临时解决 不便管理,不规范 临时测试
root 模式 简单直接 安全性低,不推荐 特殊需求

最佳实践建议

对于个人开发/测试环境

强烈推荐使用方案一,直接启用 linger:

# 一条命令搞定
sudo loginctl enable-linger $(whoami)

这样配置后:

  • ✅ 你的所有进程(包括容器)都不会因为退出登录而被终止
  • ✅ 不需要修改任何系统配置文件
  • ✅ 立即生效,无需重启
  • ✅ 只影响你自己的用户,不影响其他用户

对于生产环境

推荐组合使用方案一 + 方案三

  1. 首先为服务用户启用 linger
  2. 然后使用 systemd 用户服务来管理容器

这样做的好处包括:

  • 规范化管理:使用标准的 systemd 命令管理容器生命周期
  • 开机自启:可以配置容器随系统启动自动运行
  • 日志集成:容器日志自动集成到 journal 中,便于查看
  • 依赖管理:可以配置服务依赖,确保正确的启动顺序
  • 资源控制:可以利用 systemd 的资源限制功能
  1. 规范化管理:使用标准的 systemd 命令管理容器生命周期
  2. 开机自启:可以配置容器随系统启动自动运行
  3. 日志集成:容器日志自动集成到 journal 中,便于查看
  4. 依赖管理:可以配置服务依赖,确保正确的启动顺序
  5. 资源控制:可以利用 systemd 的资源限制功能

配置示例:生产环境的完整流程

# 1. 为当前用户启用 linger(推荐)
sudo loginctl enable-linger $(whoami)

# 2. 运行容器
podman run -d \
  --name web-app \
  -p 8080:80 \
  -v /data/web:/usr/share/nginx/html:Z \
  --restart=always \
  nginx

# 3. 生成 systemd 服务文件
mkdir -p ~/.config/containers/systemd
podman generate systemd --new --name web-app \
  > ~/.config/containers/systemd/container-web-app.service

# 4. 启用并启动服务
systemctl --user daemon-reload
systemctl --user enable container-web-app.service
systemctl --user start container-web-app.service

# 5. 查看状态
systemctl --user status container-web-app.service

# 6. 查看日志
journalctl --user -u container-web-app.service -f

常见问题

Q1: enable-linger 和修改 KillUserProcesses 有什么区别?

A:

  • enable-linger:只为特定用户启用,让该用户的进程在退出后继续运行,不影响其他用户
  • KillUserProcesses=no:全局配置,影响所有用户,所有用户退出后进程都不会被清理

推荐优先使用 enable-linger,更精准、更安全。

Q2: 如何查看哪些用户启用了 linger?

A: 执行以下命令:

ls -la /var/lib/systemd/linger/

Q3: 启用 linger 后,如何停止用户的后台进程?

A: 可以使用以下命令:

# 查看用户的所有会话和进程
loginctl list-sessions

# 终止特定用户的 linger 进程
loginctl terminate-user $(whoami)

Q4: 可以临时禁用 KillUserProcesses 吗?

A: 可以在 SSH 会话中使用 systemd-inhibit 命令:

systemd-inhibit --what=sleep podman run -d --name my-nginx nginx

Q5: 容器自动退出会影响数据吗?

A: Podman 容器退出不会影响持久化卷中的数据。只要使用了 -v 参数挂载了数据卷,数据是安全的。

总结

systemd 系统中用户退出后 podman 容器自动退出的问题,本质上是由于 systemd 的会话管理机制导致的。

最简单的解决方案(强烈推荐):

sudo loginctl enable-linger $(whoami)

一条命令即可解决问题,无需修改系统配置,立即生效!

其他可选方案

  • 修改 /etc/systemd/logind.conf 中的 KillUserProcesses 配置(全局生效)
  • 使用 systemd 用户服务配合 linger 功能(生产环境规范管理)
  • 使用 nohup 或 disown(临时方案)

根据你的具体场景选择合适的方案:

  • 个人开发/测试:直接用 enable-linger 🎯
  • 生产环境enable-linger + systemd 用户服务
  • 多用户服务器:修改全局 KillUserProcesses 配置

文章作者: 2356
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 2356 !