用 systemd 把 Python 脚本变成稳定的后台服务
很多人写完一个 Python 小服务(比如定时抓数据的脚本、一个 Flask 接口),第一反应是用 nohup python app.py & 丢到后台。这样能跑,但问题不少:进程挂了没人拉起,服务器重启后要手动再启动,日志散落在 nohup.out 里难以查找。其实主流 Linux 发行版都自带 systemd,用它来托管 Python 程序,只需要写一个十几行的配置文件。
一、准备工作
假设项目放在 /opt/demo 目录,已经建好虚拟环境:
cd /opt/demo
python3 -m venv .venv
.venv/bin/pip install flask
建议专门建一个没有登录权限的用户来运行服务,避免用 root 跑业务代码:
sudo useradd -r -s /usr/sbin/nologin demo
sudo chown -R demo:demo /opt/demo
二、编写 service 文件
在 /etc/systemd/system/ 下新建 demo.service:
[Unit]
Description=Demo Python Service
After=network-online.target
Wants=network-online.target
[Service]
User=demo
WorkingDirectory=/opt/demo
Environment=PYTHONUNBUFFERED=1
ExecStart=/opt/demo/.venv/bin/python app.py
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
几个关键点说明:
- ExecStart 要写虚拟环境里 python 的绝对路径,systemd 不会读取你的 shell 环境,写成 python 很可能找不到依赖。
- PYTHONUNBUFFERED=1 让 print 输出不经缓冲直接进日志,否则排查问题时会发现日志总是"慢半拍"。
- Restart=on-failure 配合 RestartSec=5,程序异常退出后 5 秒自动拉起;正常退出(退出码为 0)则不会重启。
- After=network-online.target 保证网络就绪后再启动,适合需要连数据库或外部接口的服务。
三、启动与开机自启
sudo systemctl daemon-reload
sudo systemctl enable --now demo
systemctl status demo
enable --now 一条命令同时完成"设置开机自启"和"立即启动"。以后每次修改 service 文件,都要先执行 daemon-reload 再 restart,这是新手最容易漏掉的一步。
四、查看日志
systemd 会把标准输出和标准错误统一交给 journald 管理,不再需要自己处理日志文件:
journalctl -u demo -f
journalctl -u demo --since "1 hour ago"
-f 类似 tail -f 实时跟踪,–since 可以按时间筛选。如果担心日志占满磁盘,可以用 journalctl --vacuum-size=200M 清理旧日志。
五、常见坑
- 状态显示 code=exited, status=203/EXEC:通常是 ExecStart 路径写错或文件没有执行权限。
- 状态显示 status=217/USER:配置里的用户不存在。
- 程序读不到配置文件:检查 WorkingDirectory,代码中的相对路径是相对它计算的。
- 需要环境变量较多时,可以用 EnvironmentFile=/opt/demo/.env 统一加载,注意该文件的权限要收紧。
小结
相比 nohup,systemd 提供了自动重启、开机自启、统一日志和权限隔离,配置成本却很低。把这个模板保存下来,以后部署任何 Python 脚本,只需改目录和启动命令即可。在云服务器上部署小型服务时,这也是最轻量可靠的方案之一。
- 点赞
- 收藏
- 关注作者
评论(0)