feat: 进程守护 supervise.sh,崩溃自动重启;DEPLOY 补 docker healthcheck
- scripts/supervise.sh:web_server 崩溃自动重启,带 2s 退避和 300s 窗口内重启上限(10次)防死循环 - doc/DEPLOY.md 补充通用守护用法 + python-app 容器 healthcheck 配置
This commit is contained in:
@@ -149,6 +149,31 @@ RestartSec=10
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
**通用脚本(Linux/macOS,无需 systemd)**:
|
||||
|
||||
项目自带 `scripts/supervise.sh`:web_server 崩溃自动重启、带退避和重启上限(防崩溃死循环)。
|
||||
|
||||
```bash
|
||||
# 用 venv 的 python 守护
|
||||
PYTHON=./.venv/bin/python bash scripts/supervise.sh
|
||||
```
|
||||
|
||||
> 进程守护只保证服务重启,不恢复已运行的任务(worker 状态在内存)。崩溃后残留的设备占用需用"强制释放占用"清理(或启动时的提示)。
|
||||
|
||||
**Docker(生产 python-app 容器)**:
|
||||
|
||||
docker-compose 已配置 `restart: unless-stopped`,web_server 进程退出 → 容器退出 → Docker 自动重启。建议再加健康检查,让编排感知服务存活:
|
||||
|
||||
```yaml
|
||||
# docker-compose.yaml 的 python-app 服务下添加
|
||||
healthcheck:
|
||||
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:18050/api/health', timeout=3)"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 10s
|
||||
```
|
||||
|
||||
### 3.2 反向代理(可选)
|
||||
|
||||
如需 HTTPS 或 80 端口,用 Nginx 反向代理:
|
||||
|
||||
Executable
+54
@@ -0,0 +1,54 @@
|
||||
#!/bin/bash
|
||||
# web_server.py 进程守护脚本:崩溃自动重启,带退避和重启上限。
|
||||
#
|
||||
# 用法:
|
||||
# bash scripts/supervise.sh # 用 python3 启动
|
||||
# PYTHON=./.venv/bin/python bash scripts/supervise.sh # 指定 Python(venv)
|
||||
#
|
||||
# 行为:
|
||||
# - web_server 崩溃(非 0/130 退出码)→ 等待 BACKOFF_MIN 秒自动重启
|
||||
# - 重启窗口(RESTART_WINDOW 秒)内超过 MAX_RESTARTS 次 → 停止(防崩溃死循环)
|
||||
# - 正常退出(Ctrl+C=130 或 code=0)→ 守护结束
|
||||
# - 日志写到 logs/supervise.log
|
||||
#
|
||||
# 生产无人值守建议配合 Docker 的 restart: unless-stopped 或 systemd 一起用。
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
PYTHON="${PYTHON:-python3}"
|
||||
mkdir -p logs
|
||||
|
||||
MAX_RESTARTS="${MAX_RESTARTS:-10}" # 窗口内最大重启次数
|
||||
RESTART_WINDOW="${RESTART_WINDOW:-300}" # 重启窗口(秒)
|
||||
BACKOFF_MIN="${BACKOFF_MIN:-2}" # 重启前等待(秒)
|
||||
|
||||
restarts=0
|
||||
window_start=$(date +%s)
|
||||
|
||||
echo "[$(date '+%F %T')] 进程守护启动,python=$PYTHON"
|
||||
while true; do
|
||||
echo "[$(date '+%F %T')] 启动 web_server.py..."
|
||||
"$PYTHON" web_server.py >> logs/supervise.log 2>&1
|
||||
code=$?
|
||||
echo "[$(date '+%F %T')] web_server 退出,code=$code"
|
||||
|
||||
# 0 = 正常结束;130 = Ctrl+C(SIGINT)→ 手动停止,守护结束
|
||||
if [ "$code" -eq 0 ] || [ "$code" -eq 130 ]; then
|
||||
echo "[$(date '+%F %T')] 正常退出,守护进程结束"
|
||||
break
|
||||
fi
|
||||
|
||||
# 滑动窗口计数
|
||||
now=$(date +%s)
|
||||
if [ $((now - window_start)) -gt "$RESTART_WINDOW" ]; then
|
||||
window_start=$now
|
||||
restarts=0
|
||||
fi
|
||||
restarts=$((restarts + 1))
|
||||
if [ "$restarts" -gt "$MAX_RESTARTS" ]; then
|
||||
echo "[$(date '+%F %T')] ${RESTART_WINDOW}s 内崩溃超过 ${MAX_RESTARTS} 次,停止守护(可能配置/环境问题)"
|
||||
break
|
||||
fi
|
||||
|
||||
echo "[$(date '+%F %T')] ${BACKOFF_MIN}s 后重启 (窗口内第 ${restarts} 次)"
|
||||
sleep "$BACKOFF_MIN"
|
||||
done
|
||||
Reference in New Issue
Block a user