用 systemd 托管 Python Web 应用:Gunicorn + Flask 开机自启与崩溃自动重启实战

举报
yd_276596855 发表于 2026/11/08 11:49:54 2026/11/08
【摘要】 很多人在云服务器上用 nohup 或 screen 跑 Python 服务,一重启就掉线,进程崩了也没人管。本文以 Flask + Gunicorn 为例,手把手演示如何用 systemd 把应用变成系统服务,实现开机自启、崩溃自动拉起、日志集中查看、平滑重载和资源限制,并附上完整配置文件与常见排错方法。

一、为什么不要再用 nohup 跑服务


刚买云服务器时,最常见的部署方式是:nohup python app.py > app.log 2>&1 &


它能跑,但问题很多:服务器重启后服务不会自动启动;进程异常退出后没人重新拉起;日志越写越大没有轮转;重启服务还得先 ps 找进程号再 kill。现在主流 Linux 发行版(Ubuntu、Debian、CentOS、EulerOS、openEuler 等)都使用 systemd,写一个几十行的 unit 文件,就能让应用享受和 nginx、sshd 一样的待遇:开机自启、失败重启、统一日志、权限隔离。


二、准备示例应用


项目放在 /opt/myapp,先创建独立运行用户:


sudo useradd --system --no-create-home --shell /usr/sbin/nologin myapp

sudo mkdir -p /opt/myapp

sudo chown -R myapp:myapp /opt/myapp

cd /opt/myapp

sudo -u myapp python3 -m venv venv

sudo -u myapp ./venv/bin/pip install flask gunicorn


/opt/myapp/app.py:


from flask import Flask, jsonify

import os, socket

app = Flask(__name__)

@app.route("/")

def index():

    return jsonify(status="ok", host=socket.gethostname(), pid=os.getpid())

@app.route("/health")

def health():

    return "healthy", 200


手动验证:sudo -u myapp ./venv/bin/gunicorn -w 2 -b 127.0.0.1:8000 app:app ,另开终端 curl http://127.0.0.1:8000/ 返回 JSON 即正常。


三、Gunicorn 配置文件 /opt/myapp/gunicorn.conf.py


import multiprocessing

bind = "127.0.0.1:8000"

workers = multiprocessing.cpu_count() * 2 + 1

worker_class = "sync"

timeout = 30

graceful_timeout = 30

keepalive = 5

accesslog = "-"

errorlog = "-"

loglevel = "info"


accesslog 和 errorlog 设为 "-" 会写到标准输出,由 systemd 收进 journald,用 journalctl 统一查看。2核2G 的小服务器可把 workers 手动设为 3 左右。


四、systemd unit 文件 /etc/systemd/system/myapp.service


[Unit]

Description=MyApp Flask service (Gunicorn)

After=network-online.target

Wants=network-online.target

[Service]

Type=notify

User=myapp

Group=myapp

WorkingDirectory=/opt/myapp

Environment="PYTHONUNBUFFERED=1"

EnvironmentFile=-/opt/myapp/.env

ExecStart=/opt/myapp/venv/bin/gunicorn -c /opt/myapp/gunicorn.conf.py app:app

ExecReload=/bin/kill -s HUP $MAINPID

KillMode=mixed

TimeoutStopSec=30

Restart=on-failure

RestartSec=5

StartLimitIntervalSec=60

StartLimitBurst=5

MemoryMax=512M

NoNewPrivileges=true

ProtectSystem=full

PrivateTmp=true

[Install]

WantedBy=multi-user.target


关键配置说明:

1. Type=notify:Gunicorn 20 以上支持 systemd 通知;版本过旧改成 Type=simple。

2. EnvironmentFile:数据库密码等敏感配置放 .env,前面的减号表示文件不存在也不报错,记得 chmod 600。

3. ExecReload:给主进程发 HUP,平滑重启 worker,不中断正在处理的请求。

4. Restart=on-failure 与 RestartSec=5:异常退出 5 秒后自动拉起;StartLimit 表示 60 秒内最多重启 5 次,防止致命 bug 无限重启。

5. MemoryMax:限制最多使用 512M,防止内存泄漏拖垮整机。

6. NoNewPrivileges、ProtectSystem、PrivateTmp:简单安全加固。五、启动并设置开机自启


sudo systemctl daemon-reload

sudo systemctl enable --now myapp

sudo systemctl status myapp


常用:systemctl restart / reload / stop myapp; systemctl is-enabled myapp 查看是否开机自启。


六、用 journalctl 查看日志


journalctl -u myapp -f

journalctl -u myapp --since "1 hour ago"

journalctl -u myapp -p err

journalctl -u myapp -n 100 --no-pager


如担心日志占满磁盘,编辑 /etc/systemd/journald.conf 设置 SystemMaxUse=500M,再 sudo systemctl restart systemd-journald。


七、验证崩溃自动重启


sudo systemctl show -p MainPID myapp

sudo kill -9 <上一步得到的 PID>

sleep 6

systemctl status myapp


服务会被重新拉起,PID 变化,日志有 "Scheduled restart job"。sudo reboot 后服务同样自动运行。


八、配合 Nginx 对外服务


Gunicorn 只监听 127.0.0.1,Nginx 站点配置加入:


location / {

    proxy_pass http://127.0.0.1:8000;

    proxy_set_header Host $host;

    proxy_set_header X-Real-IP $remote_addr;

    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    proxy_set_header X-Forwarded-Proto $scheme;

}


安全组只需放行 80 和 443。


九、一键更新部署脚本 /opt/myapp/deploy.sh


#!/usr/bin/env bash

set -euo pipefail

cd /opt/myapp

sudo -u myapp git pull --ff-only

sudo -u myapp ./venv/bin/pip install -r requirements.txt

sudo systemctl reload myapp

sleep 2

curl -fsS http://127.0.0.1:8000/health && echo " 部署成功"


十、常见问题排查


1. status=203/EXEC:ExecStart 路径错或无执行权限,检查虚拟环境路径。

2. status=217/USER:User 指定的用户不存在,先 useradd。

3. Permission denied:chown -R myapp:myapp /opt/myapp。

4. 改了 service 文件没生效:先 sudo systemctl daemon-reload。

5. 启动超时:Type=notify 但 Gunicorn 版本过旧,升级或改 Type=simple。

6. 反复重启后停止:触发了 StartLimitBurst,journalctl 找到根因,修复后 sudo systemctl reset-failed myapp 再启动。


总结


用 systemd 托管 Python 应用几乎没有额外成本:一个 unit 文件、几条命令,就能获得开机自启、崩溃自愈、统一日志、平滑重载和资源限制。同样适用于 FastAPI(worker_class 换成 uvicorn.workers.UvicornWorker)、Django、Celery worker 等后台进程。

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

评论(0)

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

全部回复

上滑加载中

设置昵称

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

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

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