华为云 ECS 上用 systemd 托管 Python Web 服务:开机自启、崩溃自拉起与日志体积控制实践
华为云 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 配置一致。
六、常见排错
- 端口被占用:日志里出现
Address already in use。用sudo ss -lntp | grep 8000找到占用进程,通常是之前手动启动、没停掉的 nohup 进程,结束它或改端口。 - 权限问题:出现
Permission denied,多半是目录属主还是 root,执行chown -R demo:demo /opt/demo;另外普通用户不能监听 1024 以下端口,想用 80 端口建议前面加 Nginx 反向代理。 - 环境变量不生效:手动运行正常、systemd 里却报找不到配置。原因是 systemd 不加载
~/.bashrc和/etc/profile,变量要写进Environment=或EnvironmentFile,修改后记得daemon-reload再restart。 - 本机能访问,外网不通:
curl 127.0.0.1:8000/health正常但公网访问超时,先确认绑定的是 0.0.0.0 而不是 127.0.0.1,再到华为云控制台检查安全组入方向是否放行了 TCP 8000,最后看系统防火墙firewall-cmd --list-ports或ufw status。 - 反复重启后停止: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 方式高出一大截。
- 点赞
- 收藏
- 关注作者
评论(0)