用 systemd 把 Python 脚本变成开机自启服务:入门笔记
为什么 nohup 不够
用 nohup python3 app.py & 把进程丢到后台,窗口一关或 SSH 断开它就"无人继承"了:终端关闭会发 SIGHUP,nohup 只屏蔽了 SIGHUP,并不保证开机自动启动、崩溃重启、日志轮转、资源限制,也不受统一的管理命令控制。systemd 原生提供这些能力,是 Linux 服务化的标准做法。
示例脚本 /opt/demo/app.py
#!/usr/bin/env python3
import time, sys
while True:
print(f"heartbeat at {time.strftime('%H:%M:%S')}", flush=True)
time.sleep(5)
print(..., flush=True) 确保 stdout 实时写出;配合 systemd 的 STDOUT 日志通道,journal 才能逐行拿到心跳。
编写 demo.service
# /etc/systemd/system/demo.service
[Unit]
Description=Demo Python heartbeat service
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=demo
WorkingDirectory=/opt/demo
ExecStart=/usr/bin/python3 /opt/demo/app.py
Restart=on-failure
RestartSec=5
Environment=PYTHONUNBUFFERED=1
[Install]
WantedBy=multi-user.target
字段说明:
After/Wants=network-online.target:等网络就绪再启,但 Wants 是弱依赖(网络目标不存在也不会阻塞开机)。Type=simple:ExecStart 直接就是主进程,systemd 据此判断存活。User=demo:以普通用户运行,最小权限。WorkingDirectory:工作目录,脚本里的相对路径基于它解析。Restart=on-failure+RestartSec=5:异常退出时 5 秒后重启。Environment=PYTHONUNBUFFERED=1:让 Python 行缓冲,日志不卡。WantedBy=multi-user.target:多用户运行级别(正常开机)时自动拉起。
启停与常见坑
sudo systemctl daemon-reload
sudo systemctl enable --now demo
sudo systemctl status demo
sudo systemctl restart demo
sudo systemctl stop demo
常见坑:改完 .service 后直接 restart 不生效 —— systemd 缓存的是加载时的单元内容,修改文件后必须先 daemon-reload,否则 restart 跑的仍是旧配置。
用 journalctl 看日志
journalctl -u demo -f
journalctl -u demo --since "1 hour ago"
journalctl -u demo -n 100 --no-pager
日志默认会无限增长,建议限制大小:
# /etc/systemd/journald.conf
[Journal]
SystemMaxUse=500M
排错速查
- 状态
failed:看systemctl status demo和journalctl -u demo -n 50,通常是 ExecStart 路径/权限/语法错误。 - 反复 activating → failed 循环:脚本退出码非 0 且配了 Restart,日志里找真正报错行;临时改
Restart=no便于观察。 - 环境变量不生效(从 ~/.bashrc 读不到):systemd 不加载用户的 bashrc/profile。改用
Environment=KEY=VAL或EnvironmentFile=/etc/demo.env。 - 权限问题:检查脚本
chmod +x、工作目录属主与User=一致;ExecStart 必须写绝对路径。
进阶:Timer + 资源限制
用 systemd timer 替代 cron 做定时任务,支持错过补偿:
# /etc/systemd/system/demo.timer
[Unit]
Description=Run demo every 5 minutes
[Timer]
OnCalendar=*:0/5
Persistent=true
Unit=demo-once.service
[Install]
WantedBy=timers.target
Persistent=true 表示关机期间错过的触发会在下次开机后补执行。
资源限制可直接写在 service 里:
MemoryMax=200M
CPUQuota=50%
超限会被 OOM kill 或节流,配合 Restart=on-failure 可实现自动恢复。
小结
把 Python 脚本交给 systemd 只需三件事:写一个稳定输出日志的脚本、写一个描述清楚依赖/用户/重启策略的 .service、用 daemon-reload + enable --now 上架。用 journalctl 看日志,用 Environment/EnvironmentFile 传变量,用 MemoryMax/CPUQuota 限资源,用 timer + Persistent 做可靠调度。比 nohup 更可控,也比 crontab 更现代。
- 点赞
- 收藏
- 关注作者
评论(0)