用 systemd 把 Python 脚本变成开机自启服务:入门笔记

举报
yd_226560001 发表于 2026/10/22 10:09:04 2026/10/22
【摘要】 本文介绍为什么 nohup 不足以做守护进程,如何用 systemd 把一个 Python 脚本做成开机自启、崩溃自动重启的系统服务。包含 /opt/demo/app.py 心跳示例、demo.service 单元文件的完整字段解释、daemon-reload / enable / status 启停命令与忘记 daemon-reload 的常见坑、journalctl 日志查看与 SystemM

为什么 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 更现代。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。