用 systemd 把 Python 小服务做成开机自启的守护进程:一份可复用的实践笔记

举报
yd_255634470 发表于 2026/11/10 08:39:57 2026/11/10
【摘要】 背景很多同学在云服务器上跑 Python 小服务时,习惯用 nohup python app.py & 或 screen 挂着。这种方式在临时调试时够用,但一旦服务器重启、进程崩溃或 SSH 断线,服务就会悄悄消失,而且出了问题很难查。本文记录一次把 Python 服务改造成 systemd 托管守护进程的完整过程,覆盖单元文件编写、日志查看、平滑重启和常见坑,适合刚接触 Linux 运维...

背景

很多同学在云服务器上跑 Python 小服务时,习惯用 nohup python app.py & 或 screen 挂着。这种方式在临时调试时够用,但一旦服务器重启、进程崩溃或 SSH 断线,服务就会悄悄消失,而且出了问题很难查。本文记录一次把 Python 服务改造成 systemd 托管守护进程的完整过程,覆盖单元文件编写、日志查看、平滑重启和常见坑,适合刚接触 Linux 运维的开发者参考。

为什么选 systemd

systemd 是主流 Linux 发行版默认的初始化系统,它自带进程守护、开机自启、失败重启、日志收集(journald)和资源限制能力。相比自己写 shell 守护脚本,systemd 的好处是配置声明式、状态可查询、日志有统一入口,而且不需要额外安装任何软件。

第一步:准备一个可运行的服务

假设我们有一个最小的 Flask 服务 /opt/demo/app.py:

from flask import Flask

app = Flask(__name__)

@app.get("/healthz")
def healthz():
    return {"status": "ok"}

if __name__ == "__main__":
    app.run(host="127.0.0.1", port=8000)

建议同时准备一个独立的虚拟环境,避免依赖污染系统 Python:

cd /opt/demo
python3 -m venv .venv
.venv/bin/pip install flask

关键点:服务只监听 127.0.0.1,对外暴露交给 Nginx 或安全组控制,这样即使程序有漏洞也不会直接暴露在公网。

第二步:编写 service 单元文件

新建 /etc/systemd/system/demo.service:

[Unit]
Description=Demo Flask Service
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=demo
Group=demo
WorkingDirectory=/opt/demo
ExecStart=/opt/demo/.venv/bin/python /opt/demo/app.py
Restart=on-failure
RestartSec=3
Environment=PYTHONUNBUFFERED=1
NoNewPrivileges=true
PrivateTmp=true

[Install]
WantedBy=multi-user.target

几个容易忽略的细节:

  • Type=simple 适合前台运行的进程;如果是自己 fork 到后台的老式程序,才需要用 forking。
  • Restart=on-failure 配合 RestartSec 能实现崩溃自动拉起,但不要设得太激进,否则会掩盖真实故障。
  • Environment=PYTHONUNBUFFERED=1 让 Python 的输出立即刷到 journal,否则日志会延迟出现,排查时很误导。
  • NoNewPrivileges 和 PrivateTmp 是低成本的安全加固,几乎不影响业务。

第三步:启用并验证

sudo systemctl daemon-reload
sudo systemctl enable --now demo.service
sudo systemctl status demo.service
curl -s http://127.0.0.1:8000/healthz

enable 负责写入开机自启的软链接,--now 表示同时立即启动。status 里重点看三件事:Active: active (running)、Main PID 是否存在、最近几行日志有没有异常。

第四步:日志与排错

systemd 服务不要自己写 app.log,直接用 journald 查询:

journalctl -u demo.service -n 100 --no-pager
journalctl -u demo.service -f          # 实时跟踪
journalctl -u demo.service --since "10 min ago"

如果想限制日志体积,可以在单元文件里加 SystemMaxUse 相关的 journald 配置,或者给服务加 StandardOutput=journal 明确输出方向。排查"服务起不来"时,先看 systemctl status 的退出码,再看 journalctl 的最后几十行,通常就能定位到是路径写错、权限不足还是端口被占用。

第五步:平滑重启与配置变更

改完代码后:

sudo systemctl restart demo.service

如果只是改了单元文件,记得先 daemon-reload 再 restart,否则新配置不会生效。这是新手最常踩的坑之一。

常见问题清单

  1. Permission denied:检查 User 是否有权限读取 /opt/demo,以及虚拟环境路径是否写对。
  2. 服务启动后马上退出:多半是前台进程被写成了后台运行,或 ExecStart 的路径不存在。
  3. 端口冲突:用 ss -tlnp | grep 8000 确认端口占用情况。
  4. 环境变量丢失:systemd 不会加载 .bashrc,所有变量都要在 Environment 或 EnvironmentFile 里显式声明。
  5. 开机没自启:确认执行过 enable,并且 WantedBy=multi-user.target 没有被误改。

小结

把 Python 服务交给 systemd 托管,本质上是用平台能力替代手工运维:开机自启、崩溃重启、统一日志、权限收敛,全部通过一个声明式单元文件完成。配置一次之后,后续的部署和排错都会简单很多。建议把单元文件纳入版本管理,和代码一起评审,这样服务配置的变更也能被追溯。

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

评论(0)

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

全部回复

上滑加载中

设置昵称

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

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

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