怎么让Python脚本在服务器后台持续运行
通过 SSH 远程连接 Linux 服务器运行 Python 脚本时,默认情况下一旦关闭终端或网络波动中断,脚本就会随之退出。要让 Python 程序在后台脱离终端、稳定持续运行,核心路径有三种:使用 nohup 与输出重定向实现轻量快速运行、使用 tmux 等终端复用工具保持交互式会话,以及通过 systemd 配置系统级守护服务以支持开机自启和故障重启。本文将由浅入深讲解具体操作、底层机制与选型依据。
一、为什么终端断开会导致 Python 脚本退出
在 Linux 操作系统中,通过 SSH 登录服务器后,系统会为当前连接创建一个伪终端(PTY)以及一个关联的 Bash 会话。当我们在命令行直接执行 python3 app.py 时,该 Python 进程作为当前终端会话的前台子进程运行。
当网络中断或直接关闭 SSH 客户端窗口时,系统内核会检测到控制终端的断开,并向该会话的首进程发送一个挂断信号——SIGHUP(Signal Hang Up)。默认情况下,会话内的子进程接收到该信号后都会被强制终止。因此,要让脚本在关闭连接后仍能继续工作,关键在于阻断 SIGHUP 信号向进程传递,或者将脚本从当前终端会话中彻底剥离解绑。
二、方案一:使用 nohup 与重定向(最轻量的快速解法)
nohup(no hang up)是 Linux 系统自带的工具,专门用于忽略 SIGHUP 信号。配合末尾的 & 符号,可以让程序直接在后台启动并脱离控制终端。
1. 推荐的完整运行命令
nohup python3 -u app.py > app.log 2>&1 &
这个由多个参数组成的命令是业界经过大量实践得出的标准写法,各部分的具体作用如下:
nohup:告知操作系统忽略所有的挂断(SIGHUP)信号,防止会话关闭时进程被误杀。python3 -u:非常关键的参数。Python 在输出重定向到文件时默认会开启缓冲区,若不加-u,控制台输出的日志可能很久都不会写入磁盘文件;-u强制取消标准输出与标准错误的缓冲,实现实时写盘。> app.log:将脚本的标准输出(stdout,文件描述符 1)重定向写入到app.log文件中。2>&1:将标准错误(stderr,文件描述符 2)重定向合并到标准输出(&1)中,确保报错堆栈也能完整记录在日志内。&:放在命令最后,表示将该命令转入后台执行,立即释放当前终端提示符,不阻塞用户操作。
2. 日志查看与进程维护
执行后台启动后,终端通常会输出一个代表该进程的数字编号(PID)。后续可通过以下命令进行日常维护:
- 查看实时运行日志:
使用tail -f app.logtail -f可以像前台执行一样动态刷新输出,按Ctrl + C即可退出日志查看,该退出动作不会影响后台正在运行的 Python 进程。 - 确认进程是否在运行:
在输出结果中可以观察到该进程的 PID、父进程 PID(通常会变成 1 号 init/systemd 进程)、CPU/内存占用及运行时间。ps -ef | grep app.py - 正常停止后台进程:
向指定进程发送kill <PID>SIGTERM信号,通知程序平稳退出;若脚本无响应卡死,可使用强制终止命令kill -9 <PID>。
三、方案二:使用 tmux 或 screen 终端复用(交互与排错利器)
nohup 适合不需要任何手动输入的纯后台服务。但如果程序在运行过程中需要观察彩色终端控制台、需要临时通过键盘输入参数,或者你希望随时重新连接进当前执行界面,终端复用工具(Terminal Multiplexer)是最好的选择。最主流的工具是 tmux。
1. tmux 的标准工作流
- 安装 tmux(若服务器未预装):
sudo apt install tmux # Ubuntu / Debian sudo yum install tmux # CentOS / RHEL - 创建一个独立的命名会话:
此时会进入一个全新的终端窗口,底部会出现绿色的 tmux 状态栏。tmux new -s my_task - 在 tmux 会话内正常启动 Python 脚本:
脚本就像平时在本地电脑一样正常输出打印。python3 app.py - 脱离(Detach)会话返回母机终端:
依次按下键盘组合键:
Ctrl + B,松开后再按下字母D。此时终端会提示[detached (from session my_task)],返回到最初的 SSH 命令行中,而my_task内部的脚本依然在服务器后台全力运行。此时你可以放心地直接关闭 SSH 客户端。 - 重新接入(Attach)已有会话:
重新通过 SSH 登录服务器后,执行:
终端会立即恢复到脚本运行的现场,所有输出与上下文完全保留。tmux attach -t my_task - 结束会话:
在 tmux 窗口内当脚本执行完毕后,直接输入
exit即可销毁该会话;或者在外层终端执行tmux kill-session -t my_task。
四、方案三:配置 systemd 服务(生产环境标准解法)
无论是 nohup 还是 tmux,在面临服务器断电重启、系统补丁重启或 Python 程序出现未捕获异常崩溃时,都无法实现自动恢复。生产环境(如企业爬虫、Web 后台、自动化数据同步任务)通常统一采用现代 Linux 系统的服务管理器——systemd。
1. 编写服务单元配置文件
假设我们的 Python 脚本位于 /home/ubuntu/app/main.py,其运行环境使用的是同目录下的虚拟环境 /home/ubuntu/app/venv。我们使用 root 权限在 /etc/systemd/system/ 目录下创建一个名为 py_worker.service 的服务文件:
sudo nano /etc/systemd/system/py_worker.service
在文件中填入如下标准配置内容:
[Unit]
Description=My Python Persistent Worker Service
After=network.target
[Service]
Type=simple
User=ubuntu
WorkingDirectory=/home/ubuntu/app
ExecStart=/home/ubuntu/app/venv/bin/python3 -u /home/ubuntu/app/main.py
Restart=always
RestartSec=5
Environment=PYTHONUNBUFFERED=1
# 日志输出配置(可选输出到文件或交由 systemd journal 管理)
StandardOutput=append:/home/ubuntu/app/worker.log
StandardError=append:/home/ubuntu/app/worker_error.log
[Install]
WantedBy=multi-user.target
核心配置项含义说明:
User:指定运行该脚本的系统用户,避免直接使用 root 运行非特权脚本,提高安全性。WorkingDirectory:脚本的工作目录。设置该项后,代码中读写相对路径文件就不会发生FileNotFoundError。ExecStart:必须使用绝对路径指定 Python 解释器和脚本入口文件。如果有虚拟环境,直接填写虚拟环境内的bin/python3绝对路径。Restart=always:进程无论是因为异常抛出崩溃还是被系统误杀,systemd 都会自动重新拉起。RestartSec=5:崩溃后等待 5 秒再执行重启,防止代码逻辑错误导致无限密集重启打满 CPU。
2. 日常运维管理命令汇总
# 启动 / 停止 / 重启服务
sudo systemctl start py_worker
sudo systemctl stop py_worker
sudo systemctl restart py_worker
# 查看服务状态
sudo systemctl status py_worker
# 开启 / 关闭开机自启
sudo systemctl enable py_worker
sudo systemctl disable py_worker
五、三种后台运行方案的选型对比与决策
在面对不同任务类型时,盲目选择最复杂的方案反而会增加操作负担,而过于简单的方案又可能在生产中发生故障。我们可以通过下表与决策树来快速选择最适合的工具:
| 对比维度 | nohup 命令 | tmux / screen 会话 | systemd 服务 |
|---|---|---|---|
| 适用场景 | 临时脚本、一次性数据清洗、离线抓取 | 长时间训练模型、排查问题、需控制台交互 | 生产 API、长期常驻服务、消息队列消费 |
| 配置成本 | 极低(单行命令直接启动) | 低(学习几个快捷键即可熟练使用) | 中(需编写 service 文件并具备 sudo 权限) |
| 控制台恢复 | 不支持(只能通过 tail 查看重定向文件) | 完美支持(随时 attach 恢复实时交互界面) | 不支持交互(通过 journalctl 或日志文件查阅) |
| 开机自启动 | 不支持 | 不支持 | 原生支持(enable 即自动注册) |
| 崩溃自动拉起 | 不支持(进程异常退出后即终止) | 不支持 | 原生支持(Restart=always 参数) |
六、后台运行常见问题与排查指南
1. 日志文件为什么长时间为空(输出缓冲问题)
很多开发者在执行 nohup python3 script.py > run.log & 后,使用 tail -f run.log 发现日志空空如也,误以为脚本没有执行。这是因为 Python 侦测到标准输出不是交互式 TTY 终端而是重定向文件时,会自动切换为块缓冲模式(Block Buffering),通常要等到累积满 4KB 或 8KB 数据才会统一刷入磁盘。
解决办法:在执行命令时加入 -u 开关,例如 python3 -u script.py;或者在环境变量中设置 export PYTHONUNBUFFERED=1;如果是在 Python 代码内部,可以在 print() 函数中加入参数 print("...", flush=True)。
2. 相对路径导致的 FileNotFoundError
当使用 nohup 或 systemd 运行时,脚本的当前执行目录(CWD)取决于你执行命令时的终端位置或 service 中的 WorkingDirectory 配置。如果在代码中直接使用 open("data/config.json"),极易因相对路径基准变化而引发报错崩溃。
解决办法:在 Python 代码中采用基于当前脚本所在目录的绝对路径定位:
from pathlib import Path
# 获取当前脚本所在目录的绝对路径
BASE_DIR = Path(__file__).resolve().parent
config_path = BASE_DIR / "data" / "config.json"
3. 如何确认后台脚本的真实资源占用
长期运行在服务器后台的 Python 脚本如果存在内存泄漏或死循环,可能导致服务器崩溃。可通过以下 Linux 命令快速检查:
# 查看该 Python 进程的 CPU 与内存实时占用
top -p $(pgrep -f app.py)
# 或使用 ps 查看简报
ps -o pid,user,%cpu,%mem,stat,start,time,command -p $(pgrep -f app.py)
如果发现状态显示为 Z(Zombie 僵尸进程)或 D(不可中断的休眠,通常在等待死锁磁盘 I/O),需检查代码中的多线程、网络套接字及资源释放逻辑。
总结:临时运行和排查优先选 tmux 或 nohup -u;一旦程序需要长期对业务负责、要求无人值守与自愈能力,务必投入少量时间配置 systemd 服务,这是保障服务器应用健壮运行最规范的做法。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









