华为云 ECS 上用 systemd 托管 Python Web 服务:开机自启、崩溃自拉起与日志体积控制实践

举报
yd_252351073 发表于 2026/11/02 12:00:18 2026/11/02
【摘要】 华为云 ECS 上用 systemd 托管 Python Web 服务:开机自启、崩溃自拉起与日志体积控制实践在华为云 ECS 上跑一个 Python 小服务(比如内部接口、Webhook 接收器、小型管理后台),很多人第一反应是 nohup python app.py &。这种方式能跑,但机器一重启服务就没了,进程崩溃也没人管,日志还会悄悄把系统盘写满。本文以一台 Huawei Clou...

华为云 ECS 上用 systemd 托管 Python Web 服务:开机自启、崩溃自拉起与日志体积控制实践

在华为云 ECS 上跑一个 Python 小服务(比如内部接口、Webhook 接收器、小型管理后台),很多人第一反应是 nohup python app.py &。这种方式能跑,但机器一重启服务就没了,进程崩溃也没人管,日志还会悄悄把系统盘写满。本文以一台 Huawei Cloud EulerOS / CentOS / Ubuntu 均适用的 Linux ECS 为例,把一个 Flask 服务交给 systemd 托管,实现开机自启、异常自动重启,并用 journalctl 查日志、用 journald 和 logrotate 限制日志体积。整个过程不依赖额外的进程管理工具,只用系统自带组件即可完成。文中地址统一写成 127.0.0.1:8000,域名用 your_domain 代替。

一、准备运行环境

建议不要用 root 跑业务进程,先建一个专用用户和目录:

sudo useradd -r -m -d /opt/demo -s /sbin/nologin demo
sudo mkdir -p /opt/demo/app
cd /opt/demo/app
sudo python3 -m venv venv
sudo ./venv/bin/pip install flask gunicorn

写一个最小的服务 app.py:

import os
from flask import Flask

app = Flask(__name__)

@app.route("/health")
def health():
    return {"status": "ok", "env": os.getenv("APP_ENV", "dev")}

先手动验证能跑起来:

sudo ./venv/bin/gunicorn -w 2 -b 127.0.0.1:8000 app:app
curl 127.0.0.1:8000/health

确认无误后 Ctrl+C 停掉,并把目录属主改为 demo:sudo chown -R demo:demo /opt/demo。

二、编写 unit 文件

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

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

[Service]
Type=simple
User=demo
Group=demo
WorkingDirectory=/opt/demo/app
EnvironmentFile=-/opt/demo/app/demo.env
ExecStart=/opt/demo/app/venv/bin/gunicorn -w 2 -b 0.0.0.0:8000 app:app
Restart=on-failure
RestartSec=5
StartLimitIntervalSec=60
StartLimitBurst=5
StandardOutput=journal
StandardError=journal
NoNewPrivileges=true

[Install]
WantedBy=multi-user.target

几个关键字段说明:

  • Restart=on-failure:进程非正常退出(返回码非 0、被信号杀死)时自动拉起;RestartSec=5 表示间隔 5 秒再启动。
  • StartLimitIntervalSec 与 StartLimitBurst:60 秒内最多重启 5 次,防止程序有致命错误时无限重启刷屏。
  • EnvironmentFile 前面的 - 表示文件不存在也不报错,环境变量统一放到 demo.env 里,例如 APP_ENV=prod。
  • ExecStart 必须写绝对路径,systemd 不会读取你登录 shell 的 PATH。

加载并启用:

sudo systemctl daemon-reload
sudo systemctl enable --now demo-web
systemctl status demo-web

验证崩溃自拉起:sudo kill -9 $(pgrep -f "gunicorn.*app:app" | head -1),几秒后再看 status,主进程 PID 已经变化,说明重启生效。再执行一次 sudo reboot,开机后服务应自动处于 active (running)。

日常运维中最常用的几条管理命令也顺手记一下:systemctl restart demo-web 重启服务,systemctl stop demo-web 停止服务,systemctl disable demo-web 取消开机自启,systemctl is-enabled demo-web 查看是否已设为自启。每次修改 unit 文件后,都必须先执行 daemon-reload,否则 systemd 仍会使用旧配置,这是新手最容易忽略的一步。

三、用 journalctl 看日志

服务的标准输出和错误输出都进入了 journald,常用命令:

journalctl -u demo-web -f              # 实时跟踪
journalctl -u demo-web --since "1 hour ago"
journalctl -u demo-web -p err -b       # 本次开机以来的错误
journalctl --disk-usage                # 查看日志总占用

排查线上问题时,推荐先用 -p err 过滤出错误级别的日志,再结合 --since 缩小时间范围,比直接翻几万行输出高效得多。如果需要把日志交给同事分析,可以加上 --no-pager > demo.log 导出成文本文件。

四、限制日志体积

journald 方面,编辑 /etc/systemd/journald.conf:

[Journal]
Storage=persistent
SystemMaxUse=500M
SystemMaxFileSize=50M
MaxRetentionSec=14day

然后 sudo systemctl restart systemd-journald 生效。临时清理可用 sudo journalctl --vacuum-size=200M。

logrotate 方面,如果应用自己还写了文件日志(比如 gunicorn 加了 --access-logfile /var/log/demo/access.log),新建 /etc/logrotate.d/demo-web:

/var/log/demo/*.log {
    daily
    rotate 7
    compress
    delaycompress
    missingok
    notifempty
    copytruncate
}

用 sudo logrotate -d /etc/logrotate.d/demo-web 先空跑检查语法。copytruncate 可以避免进程继续往已被改名的旧文件里写。注意 /var/log/demo 目录要提前创建并授权给 demo 用户。

五、用 drop-in 覆盖配置与资源限制

线上运行一段时间后,经常需要调整参数,比如加大工作进程数、限制内存。不建议直接改主 unit 文件,而是用 systemd 自带的覆盖机制:

sudo systemctl edit demo-web

在弹出的编辑器中写入:

[Service]
MemoryMax=512M
CPUQuota=80%
LimitNOFILE=65535
Environment=APP_ENV=prod

保存后会自动生成 /etc/systemd/system/demo-web.service.d/override.conf,再执行 sudo systemctl restart demo-web 即可生效。MemoryMax 可以防止程序内存泄漏拖垮整台机器,超出上限时进程会被杀掉,再由 Restart=on-failure 自动拉起;CPUQuota 适合在一台小规格 ECS 上同时跑多个服务的场景,避免某个服务把 CPU 吃满。用 systemctl show demo-web -p MemoryMax 可以确认参数是否已经生效,用 systemd-cgtop 可以实时观察各个服务的资源占用。

这种做法的好处是主文件保持干净,升级或回滚时只需要关注覆盖文件;如果想恢复默认,删除 override 文件再 daemon-reload 即可。团队协作时,也建议把 unit 文件和覆盖文件一起纳入版本管理,新机器部署时直接拷贝过去,保证多台 ECS 配置一致。

六、常见排错

  1. 端口被占用:日志里出现 Address already in use。用 sudo ss -lntp | grep 8000 找到占用进程,通常是之前手动启动、没停掉的 nohup 进程,结束它或改端口。
  2. 权限问题:出现 Permission denied,多半是目录属主还是 root,执行 chown -R demo:demo /opt/demo;另外普通用户不能监听 1024 以下端口,想用 80 端口建议前面加 Nginx 反向代理。
  3. 环境变量不生效:手动运行正常、systemd 里却报找不到配置。原因是 systemd 不加载 ~/.bashrc 和 /etc/profile,变量要写进 Environment= 或 EnvironmentFile,修改后记得 daemon-reload 再 restart。
  4. 本机能访问,外网不通:curl 127.0.0.1:8000/health 正常但公网访问超时,先确认绑定的是 0.0.0.0 而不是 127.0.0.1,再到华为云控制台检查安全组入方向是否放行了 TCP 8000,最后看系统防火墙 firewall-cmd --list-ports 或 ufw status。
  5. 反复重启后停止:status 显示 start request repeated too quickly,说明触发了启动频率限制。先用 journalctl -u demo-web -n 50 找到真正的报错,修复后执行 systemctl reset-failed demo-web 再启动。

小结

用 systemd 托管 Python 服务,只需要一个十几行的 unit 文件,就能同时得到开机自启、崩溃自动恢复和统一的日志入口。配合 journald 的容量上限和 logrotate 的按天轮转,可以避免日志把 ECS 系统盘撑满。上线前记得三件事:专用低权限用户运行、环境变量写进 unit 或 EnvironmentFile、安全组和系统防火墙同时放行端口。按这个流程搭好后,服务的稳定性和可维护性都会比 nohup 方式高出一大截。

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

评论(0)

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

全部回复

上滑加载中

设置昵称

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

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

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