用 systemd 托管 Python 服务:从手动运行到开机自启
用 systemd 托管 Python 服务:从手动运行到开机自启
很多 Python 服务一开始都是这样启动的:登录服务器,进入虚拟环境,执行 python app.py,然后用 nohup 挂到后台。短期能用,但一旦服务器重启、进程崩溃或需要查看日志,就会变得很麻烦。本文用一个最小示例,说明如何用 systemd 把 Python 服务变成可管理、可自启、可查看日志的系统服务。
一、准备服务代码
假设有一个简单的 HTTP 服务,监听 8000 端口:
# /opt/demo/app.py
from http.server import BaseHTTPRequestHandler, HTTPServer
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header("Content-Type", "text/plain; charset=utf-8")
self.end_headers()
self.wfile.write("hello from systemd\n".encode())
if __name__ == "__main__":
HTTPServer(("0.0.0.0", 8000), Handler).serve_forever()
建议把代码放在 /opt/demo,并创建独立虚拟环境:
python3 -m venv /opt/demo/venv
/opt/demo/venv/bin/pip install -r /opt/demo/requirements.txt
不要用 root 运行应用。创建一个专用用户:
useradd --system --home /opt/demo --shell /usr/sbin/nologin demo
chown -R demo:demo /opt/demo
二、编写 unit 文件
在 /etc/systemd/system/demo.service 写入:
[Unit]
Description=Demo Python HTTP Service
After=network.target
[Service]
Type=simple
User=demo
Group=demo
WorkingDirectory=/opt/demo
Environment=PYTHONUNBUFFERED=1
ExecStart=/opt/demo/venv/bin/python /opt/demo/app.py
Restart=on-failure
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true
[Install]
WantedBy=multi-user.target
几个关键点:Type=simple 适合前台运行的进程;Restart=on-failure 让异常退出后自动拉起;Environment=PYTHONUNBUFFERED=1 让日志立即写入 journal;NoNewPrivileges 和 PrivateTmp 是低成本的权限收紧措施。
三、启动与排障
systemctl daemon-reload
systemctl enable --now demo
systemctl status demo
journalctl -u demo -f
如果启动失败,先看 systemctl status 的退出码,再看 journalctl -u demo -n 100。常见问题包括虚拟环境路径写错、工作目录权限不足、端口被占用,以及服务代码没有监听 0.0.0.0。
四、更新服务
修改代码后不需要改 unit 文件时,直接重启:
systemctl restart demo
如果修改了 unit 文件,则需要先执行 systemctl daemon-reload,再重启服务。发布流程可以写成脚本,但不要把数据库迁移、代码拉取和进程重启混在一个不可回滚的操作里;至少保留上一版本目录,出问题时可以快速切回。
五、小结
systemd 解决的不只是“开机自启”,它还统一了进程守护、日志、权限和依赖顺序。对于单机部署的 Python 服务,一个几十行的 unit 文件通常比 nohup 加手写监控脚本更可靠。先把服务以前台方式跑通,再交给 systemd 托管,排障会简单很多。
- 点赞
- 收藏
- 关注作者
评论(0)