Linux服务器后台运行Python脚本并保存日志

木白
木白
发布时间:2026-11-06 | 5人浏览过
原创

详解在Linux服务器后台持续运行Python脚本并可靠保存日志的完整操作方法,涵盖脚本与目录准备、nohup重定向与缓冲控制、进程核验与停止、systemd服务托管及日志排查。

Linux 运维与 Python 工程实操指南

在Linux服务器上让Python脚本脱离SSH终端持续运行并完整记录日志,最直接的做法是使用nohup python3 -u 脚本路径 >> 日志路径 2>&1 &;若任务需要开机自启和异常重启,则应交由systemd托管,并在Python脚本内部使用RotatingFileHandler控制日志体积。本文将按照准备环境、启动后台任务、实时查看与停止进程、配置长期服务以及排查日志异常的顺序,完整说明每一步的操作方法与背后的运行机制。

一、为什么直接运行脚本会在断开SSH后停止且看不到日志

通过SSH登录Linux服务器并直接执行python3 worker.py时,Python进程是当前Shell会话的子进程,且占用前台终端。一旦网络波动、关闭终端窗口或退出SSH连接,操作系统内核会向该会话下的进程组发送SIGHUP(挂起信号),导致Python脚本立即终止。

另一个常见现象是:即使在命令末尾加上&将脚本放入后台,再把输出重定向到文件,打开日志文件时却发现里面是空的。这是因为Python在检测到标准输出(stdout)的目标不是交互式终端而是普通文件时,会自动启用块缓冲(通常积累到4KB或8KB才写入磁盘一次)。如果脚本打印频率较低,或者进程在缓冲区写满前被强制终止,内存中的日志就会丢失。

Linux服务器后台运行Python脚本并保存日志
图 1:SSH 终端断开时的信号传递机制,以及使用 nohup、-u 参数与 2>&1 重定向保障日志实时落盘的原理。

二、准备工作:规范目录结构与示例脚本

在启动后台任务前,建议将脚本代码与日志文件分目录存放,并在脚本中使用绝对路径,避免因后台启动时的工作目录(Working Directory)不同而引发找不到文件的错误。本节统一使用以下目录和文件路径作为完整示例:

  • 项目目录与脚本路径:/opt/app/worker.py
  • Python虚拟环境解释器路径:/opt/app/.venv/bin/python3
  • 日志存放路径:/var/log/app/worker.log

通过SSH登录Linux服务器后,先创建代码目录与日志目录,并将日志目录归属权赋给当前运行脚本的普通用户(此处以用户名ubuntu为例):

Bash · 创建项目与日志目录(执行位置:任意终端目录) Linux Shell
sudo mkdir -p /opt/app /var/log/app
sudo chown -R ubuntu:ubuntu /opt/app /var/log/app
python3 -m venv /opt/app/.venv

接着编写用于演示后台运行的脚本/opt/app/worker.py。为了在后续日志中清晰区分正常输出(stdout)和错误输出(stderr),脚本每隔5秒通过print()打印一条带时间戳的进度记录,并在第3次循环时向标准错误流输出一条告警信息:

Python · /opt/app/worker.py UTF-8
import os
import sys
import time
from datetime import datetime

def main():
    pid = os.getpid()
    print(f"[{datetime.now():%Y-%m-%d %H:%M:%S}] 后台任务启动,当前进程 PID={pid}")
    counter = 0
    while True:
        counter += 1
        now_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        print(f"[{now_str}] 正在处理第 {counter} 批数据...")
        if counter == 3:
            sys.stderr.write(f"[{now_str}] [WARN] 第 {counter} 批数据出现超时重试记录\n")
        time.sleep(5)

if __name__ == "__main__":
    main()

三、使用 nohup 在后台运行 Python 脚本并保存日志

对于单次执行的数据处理、模型训练或临时巡检脚本,使用nohup配合Shell重定向是最轻量的方案,无需管理员权限即可直接运行。

Linux服务器后台运行Python脚本并保存日志
图 2:使用 nohup 启动后台 Python 脚本、记录 PID、验证日志及安全终止的标准四步操作流程。

1. 启动脚本并记录进程 PID

进入/opt/app目录,使用虚拟环境中的Python解释器启动worker.py,同时用echo $!把刚启动的后台进程PID保存到/opt/app/worker.pid文件中:

Bash · 启动后台任务并记录 PID(执行位置:/opt/app) Linux Shell
cd /opt/app
nohup /opt/app/.venv/bin/python3 -u /opt/app/worker.py >> /var/log/app/worker.log 2>&1 &
echo $! > /opt/app/worker.pid
示例输出(终端返回作业编号与进程 PID)
[1] 28451

这条命令中各个部分的作用如下:

  • nohup:拦截SIGHUP信号,使SSH会话关闭后进程继续运行。
  • -u:强制Python的stdout和stderr完全不使用缓冲,确保print()和报错信息实时写入日志文件(等同于设置环境变量PYTHONUNBUFFERED=1)。
  • >> /var/log/app/worker.log:将标准输出(文件描述符1)以追加模式写入日志文件。如果改用单个>,每次重启脚本都会清空原有日志。
  • 2>&1:将标准错误(文件描述符2)重定向到标准输出当前指向的位置,使异常堆栈(Traceback)和普通日志保存在同一个文件中。注意2>&1必须写在>> 日志路径之后。
  • &:将命令放入当前Shell后台执行,立即释放终端输入提示符。
  • echo $! > /opt/app/worker.pid:$!是Shell内置变量,代表最近一个放入后台的进程PID。将其写入文件后,后续停止或检查进程时无需反复用ps筛选。
易错提醒:执行完nohup ... &后,建议按一次回车键,并输入exit正常退出SSH会话,不要直接强行关闭终端软件窗口。部分SSH客户端在窗口被直接关闭时,若后台任务仍持有未重定向的标准输入(stdin),可能引发会话挂起。

2. 验证进程存活与实时查看日志

命令返回PID只代表进程成功创建,如果脚本存在语法错误或缺少依赖包,进程会在启动后几十毫秒内退出。因此启动后必须检查进程是否仍在运行,并查看日志头部输出:

Bash · 检查进程状态并追踪实时日志(执行位置:/opt/app) Linux Shell
ps -fp $(cat /opt/app/worker.pid)
tail -n 20 -f /var/log/app/worker.log
示例输出(进程存活且日志按每5秒一条持续刷新)
UID        PID  PPID  C STIME TTY          TIME CMD
ubuntu   28451     1  0 16:10 ?        00:00:00 /opt/app/.venv/bin/python3 -u /opt/app/worker.py
[2026-11-04 16:10:02] 后台任务启动,当前进程 PID=28451
[2026-11-04 16:10:02] 正在处理第 1 批数据...
[2026-11-04 16:10:07] 正在处理第 2 批数据...
[2026-11-04 16:10:12] 正在处理第 3 批数据...
[2026-11-04 16:10:12] [WARN] 第 3 批数据出现超时重试记录

在tail -f持续刷新界面下,按Ctrl + C只会退出tail命令本身,后台的worker.py仍然继续运行。如果当时未保存worker.pid,也可以执行pgrep -af "/opt/app/worker.py"找回对应的PID。

3. 安全停止后台脚本

需要停止脚本时,根据保存的PID文件或pgrep查到的PID,发送SIGTERM(15号信号):

Bash · 终止后台 Python 进程(执行位置:/opt/app) Linux Shell
kill -15 $(cat /opt/app/worker.pid)
ps -fp $(cat /opt/app/worker.pid)
示例输出(再次查询时提示无此进程,说明已停止)
UID        PID  PPID  C STIME TTY          TIME CMD

只有当脚本卡死在底层C扩展或网络阻塞中、等待10秒以上仍未退出时,才使用kill -9 PID强制杀死进程。

四、在 Python 脚本内使用 logging 模块实现日志自动轮转

依靠Shell的>> /var/log/app/worker.log重定向有一个明显限制:只要后台进程不停止,日志文件就会无限增大;如果直接删除或移动正在被写入的worker.log,Linux内核并不会立即释放磁盘空间(因为进程仍持有该文件的打开句柄),直到重启Python进程才会释放。

如果脚本需要连续运行数周甚至数月,更稳妥的做法是在Python代码内使用标准库的logging.handlers.RotatingFileHandler(按大小切分)或TimedRotatingFileHandler(按天切分),由Python自身管理日志文件的创建与轮转。

Linux服务器后台运行Python脚本并保存日志
图 3:Shell 单文件追加与 Python 内置 RotatingFileHandler 自动轮转在磁盘空间管理上的对比。

修改/opt/app/worker.py,配置单文件上限为10MB、最多保留5个历史备份文件(即日志总占用不超过60MB):

Python · 带自动轮转日志的 /opt/app/worker.py UTF-8
import logging
from logging.handlers import RotatingFileHandler
import os
import time

LOG_FILE = "/var/log/app/worker.log"

def setup_logger():
    logger = logging.getLogger("worker")
    logger.setLevel(logging.INFO)

    handler = RotatingFileHandler(
        LOG_FILE,
        maxBytes=10 * 1024 * 1024,  # 单个文件最大 10MB
        backupCount=5,              # 最多保留 worker.log.1 至 worker.log.5
        encoding="utf-8"
    )
    formatter = logging.Formatter(
        fmt="%(asctime)s [%(levelname)s] [PID:%(process)d] %(message)s",
        datefmt="%Y-%m-%d %H:%M:%S"
    )
    handler.setFormatter(formatter)
    logger.addHandler(handler)
    return logger

def main():
    logger = setup_logger()
    logger.info("后台服务已启动")
    counter = 0
    while True:
        counter += 1
        logger.info("正在处理第 %d 批数据...", counter)
        if counter == 3:
            logger.warning("第 %d 批数据响应超时,已触发重试", counter)
        time.sleep(5)

if __name__ == "__main__":
    main()

当脚本自身已经通过RotatingFileHandler将日志写入/var/log/app/worker.log时,如果再用nohup ... >> /var/log/app/worker.log 2>&1 &指向同一个文件,会导致两种写入机制冲突。此时应将未捕获的底层崩溃信息单独指向另一个错误文件(如/var/log/app/worker-crash.log),或者交给下一节的systemd统一管理。

五、长期运行方案:用 systemd 托管 Python 脚本与日志

nohup适合临时任务,但如果Python脚本因未捕获异常崩溃退出,或者Linux服务器重启,nohup启动的进程不会自动恢复。对于需要7×24小时运行的生产脚本,应使用Linux发行版内置的systemd将其注册为系统服务。

1. 创建 systemd 服务单元文件

使用管理员权限创建/etc/systemd/system/python-worker.service文件,将工作目录、运行用户、环境变量PYTHONUNBUFFERED=1以及日志路径写入配置:

INI · /etc/systemd/system/python-worker.service systemd Unit
[Unit]
Description=Python Background Worker Service
After=network.target

[Service]
Type=simple
User=ubuntu
Group=ubuntu
WorkingDirectory=/opt/app
Environment="PYTHONUNBUFFERED=1"
ExecStart=/opt/app/.venv/bin/python3 /opt/app/worker.py
StandardOutput=append:/var/log/app/worker.log
StandardError=append:/var/log/app/worker.log
Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target

如果worker.py内部已经使用了上一节的RotatingFileHandler写/var/log/app/worker.log,则只需把配置中的StandardOutput和StandardError两行删掉(默认由journald接收底层标准输出),或改为StandardError=append:/var/log/app/worker-crash.log即可。

版本兼容说明:StandardOutput=append:/path/to/file语法要求systemd版本不低于 240(Ubuntu 20.04+、Debian 10+、AlmaLinux/Rocky Linux 8+ 均默认满足;可通过systemctl --version确认)。在更早的系统(如CentOS 7,systemd 219)中,建议删去这两行并通过journalctl -u python-worker -f查看日志,或在Python脚本内使用RotatingFileHandler。

2. 启动服务、设置开机自启与核验状态

保存配置文件后,依次执行重载配置、设置开机自启并启动服务:

Bash · 启用并检查 python-worker 服务(执行位置:任意终端目录) Linux Shell
sudo systemctl daemon-reload
sudo systemctl enable --now python-worker.service
sudo systemctl status python-worker.service
示例输出(Active 显示 active (running) 代表后台托管成功)
● python-worker.service - Python Background Worker Service
     Loaded: loaded (/etc/systemd/system/python-worker.service; enabled; vendor preset: enabled)
     Active: active (running) since Wed 2026-11-04 16:25:10 CST; 6s ago
   Main PID: 30112 (python3)
      Tasks: 1 (limit: 4656)
     Memory: 9.4M
        CPU: 42ms
     CGroup: /system.slice/python-worker.service
             └─30112 /opt/app/.venv/bin/python3 /opt/app/worker.py

后续需要重启或停止脚本时,无需手动查找PID,直接执行sudo systemctl restart python-worker或sudo systemctl stop python-worker即可。

六、常见问题与故障排查

1. 日志文件已生成,但大小一直是 0 字节

原因:启动命令未加-u参数,且脚本中使用print()输出的内容尚未填满4KB~8KB的文件流缓冲区。

解决方法:启动时加上python3 -u,或在环境设置中加入export PYTHONUNBUFFERED=1;如果只想针对单条关键print立即写入,可在代码中写为print("关键状态", flush=True)。

2. 前台手动运行正常,放到后台或 systemd 中却立刻报错退出

原因:最常见的原因有两个:一是使用了默认的系统python3而非虚拟环境解释器,导致报ModuleNotFoundError;二是脚本内使用了相对路径(如open("config.json")),而后台启动时的工作目录不在/opt/app。

解决方法:始终使用/opt/app/.venv/bin/python3完整路径调用解释器;在Python脚本中通过os.path.dirname(os.path.abspath(__file__))定位脚本所在目录并拼接配置文件路径。

3. 日志中的中文字符变成乱码或报 UnicodeEncodeError

原因:非交互式后台环境缺少UTF-8区域设置变量(LANG或LC_ALL为POSIX或C)。

解决方法:在启动前设置export PYTHONIOENCODING=utf-8,或在systemd的[Service]段中添加一行Environment="PYTHONIOENCODING=utf-8"。

七、方案对比与选型总结

在实际运维中,可以根据脚本运行周期、是否需要交互式调试以及是否具备sudo权限,按以下标准选择对应方案:

对比维度 nohup + Shell 重定向 tmux / screen 会话 systemd 服务托管
适用场景 几十分钟到数天的临时数据处理、导出、训练脚本 需要随时重连终端观察实时交互界面的调试任务 7×24 小时运行的生产采集器、消费队列、定时守护进程
是否需要 root 权限 不需要 不需要(需系统已安装对应工具) 需要(或使用 systemctl --user 配合 loginctl enable-linger)
异常崩溃自动重启 不支持 不支持 支持(Restart=on-failure)
日志保存推荐做法 python3 -u 配合 >> worker.log 2>&1 在会话内配合 tee -a worker.log 或脚本内写文件 Python RotatingFileHandler 或 StandardOutput=append:
Linux服务器后台运行Python脚本并保存日志
图 4:Linux 后台运行 Python 脚本与保存日志的选型决策树及四项关键配置核对清单。
相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表