ops: 把 systemd unit 纳入版本控制,统一 timeout/限流/资源限制

背景:
  生产机 /etc/systemd/system/socks-manager.service 长期脱管:参数跟
  README 文档不一致(-w 2 vs -w 1), 也没有 -w 4 的孤儿 gunicorn 抢占
  5000 端口、 systemd 启不来服务。已删孤儿, 改用本仓库的 unit 作为
  唯一权威源。

变更:
  + deploy/socks-manager.service  单元文件唯一权威源
  + deploy/install.sh              一键同步: install + daemon-reload +
                                  enable + restart + 验证 active
  M README.md                      部署章节改为引用 deploy/, 删除内联
                                  unit 代码(避免再次漂移)

调优:
  - --timeout 30            网络扫描器半截请求占满 worker, 30s 及时回收
  - --max-requests 1000     周期性回收 worker, 防内存泄漏
  - --limit-request-line    防御大请求行导致 worker 长时间阻塞
  - MemoryMax=512M          cgroup 硬上限, 防单 worker 把整台 1GB VPS
                            拖死再拖到 OOM
  - LimitNOFILE=65535       SOCKS5 大并发客户端连接所需
  - Type=notify + 保留 Restart=always / RestartSec=5
This commit is contained in:
cnbugs
2026-08-10 23:06:28 +08:00
parent 70b6da45c2
commit f948a410b6
3 changed files with 136 additions and 43 deletions
+30 -43
View File
@@ -187,66 +187,53 @@ Flask 内置开发服务器不适合生产环境。使用 **Gunicorn**(生产
# 安装 gunicorn # 安装 gunicorn
pip install gunicorn --break-system-packages pip install gunicorn --break-system-packages
# 启动(4 worker 进程,适合 4 核服务器 # 最小启动(不推荐生产,worker 数量 = CPU 核心数,单核机器用 -w 1
gunicorn -w 4 -b 0.0.0.0:5000 --timeout 120 run:app gunicorn -w 1 -b 0.0.0.0:5000 run:app
# 推荐参数(更长超时 + 优雅关闭 # 推荐参数:30s 超时 + 优雅关闭 + 周期性回收 worker 防内存泄漏
gunicorn -w 2 -b 0.0.0.0:5000 \ gunicorn -w 1 -b 0.0.0.0:5000 \
--timeout 300 \ --timeout 30 \
--graceful-timeout 30 \ --graceful-timeout 30 \
--max-requests 10000 \ --max-requests 1000 \
--max-requests-jitter 1000 \ --max-requests-jitter 100 \
--limit-request-line 8190 \
--limit-request-fields 100 \
run:app run:app
``` ```
> **注意**:SOCKS5 代理实例运行在独立的 asyncio 线程中,不受 Gunicorn worker 数量影响。 > **注意**:SOCKS5 代理实例运行在独立的 asyncio 线程中,不受 Gunicorn worker 数量影响。
> **生产请用 systemd 托管**,见下节;裸 gunicorn 没有开机自启和崩溃重启。
--- ---
### 2. Systemd 服务配置(推荐) ### 2. Systemd 服务配置(推荐)
创建 `/etc/systemd/system/socks-manager.service` 服务单元文件的**唯一权威源**在仓库 `deploy/socks-manager.service`
所有参数、调优注释、`MemoryMax` 限制都集中在那一份,生产机只放一份拷贝。
```ini 部署步骤:
[Unit]
Description=SOCKS5 Proxy Manager
After=network.target
[Service] ```bash
Type=notify # 1. 在本地修改 deploy/socks-manager.service
User=root # 2. git commit + push
WorkingDirectory=/opt/socks-manager # 3. 在生产机 (/opt/socks-manager) 拉取并执行:
Environment="PATH=/usr/local/bin:/usr/bin:/bin" git pull
sudo bash deploy/install.sh
# 生产环境变量(按需修改)
Environment="SM_ADMIN_PASSWORD=你的强密码"
Environment="SM_SECRET_KEY=生产环境用openssl rand -hex 32生成"
Environment="SM_LOG_LEVEL=WARN"
# Environment="SM_COOKIE_SECURE=true" # HTTPS 时开启
# Environment="SM_SESSION_DOMAIN=你的域名或IP" # 跨域时设置
ExecStart=/usr/local/bin/gunicorn -w 2 -b 0.0.0.0:5000 \
--timeout 300 \
--graceful-timeout 30 \
run:app
# 优雅关闭:先给 SOCKS5 实例时间关闭活跃连接
ExecStop=/bin/kill -s TERM $MAINPID
TimeoutStopSec=60
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
``` ```
启动服务: `install.sh` 做的事:复制 unit 到 `/etc/systemd/system/``daemon-reload``enable` (开机自启) → `restart` → 等 15s 验证 active。
完整 unit 见 [`deploy/socks-manager.service`](deploy/socks-manager.service),关键参数说明:
- `-w 1` / `--timeout 30` / `--graceful-timeout 30` — 1 核 + 1GB 内存机型的基线,按 `nproc` 调整 `-w`
- `--max-requests 1000` + jitter — 周期性回收 worker 防内存泄漏
- `MemoryMax=512M` — cgroup 硬上限,防整台 VPS 被拖死
- `LimitNOFILE=65535` — SOCKS5 大并发客户端连接需要
常用命令:
```bash ```bash
systemctl daemon-reload
systemctl enable --now socks-manager
systemctl status socks-manager systemctl status socks-manager
journalctl -u socks-manager -f # 查看实时日志 journalctl -u socks-manager -f # 实时日志
systemctl restart socks-manager # 改完代码后重启
``` ```
--- ---
+43
View File
@@ -0,0 +1,43 @@
#!/usr/bin/env bash
# 把 deploy/socks-manager.service 同步到生产机并重启。
#
# 使用: 在生产机 /opt/socks-manager 下执行
# bash deploy/install.sh
#
# 假定: 仓库已 git pull 到最新,当前在 /opt/socks-manager 工作目录下。
set -euo pipefail
SERVICE_FILE="deploy/socks-manager.service"
DEST="/etc/systemd/system/socks-manager.service"
if [[ ! -f "$SERVICE_FILE" ]]; then
echo "ERROR: $SERVICE_FILE not found. Run from /opt/socks-manager." >&2
exit 1
fi
echo "[1/5] install unit -> $DEST"
install -m 0644 "$SERVICE_FILE" "$DEST"
echo "[2/5] daemon-reload"
systemctl daemon-reload
echo "[3/5] enable (开机自启)"
systemctl enable socks-manager.service
echo "[4/5] restart"
systemctl restart socks-manager.service
echo "[5/5] wait for active (max 15s)"
for i in $(seq 1 15); do
if systemctl is-active --quiet socks-manager.service; then
echo "active after ${i}s"
systemctl status socks-manager.service --no-pager | head -15
exit 0
fi
sleep 1
done
echo "ERROR: service did not become active in 15s" >&2
journalctl -u socks-manager.service -n 80 --no-pager >&2
exit 1
+63
View File
@@ -0,0 +1,63 @@
# SOCKS Manager — systemd unit (authoritative source)
#
# 这个文件是服务器上 /etc/systemd/system/socks-manager.service 的唯一定义源。
# 修改流程:
# 1. 编辑本文件
# 2. 提交并 push 到 origin
# 3. 在生产机执行: bash deploy/install.sh
#
# 调优说明(按机器规格):
# -w N gunicorn worker 数。1 核 CPU 建议 1,2 核建议 2。
# --timeout 30 HTTP 请求行/头/体的总超时(秒)。网络扫描器发 PRI * HTTP/2.0 这类
# 半截请求会占满 worker,30s 既能容忍慢客户端也能及时回收。
# --graceful-timeout 30 systemd 停止时给 worker 写完响应的最大等待时间。
# --max-requests 1000 worker 处理 N 个请求后自杀重启,防止内存泄漏累积。
# --max-requests-jitter 100 错峰重启,避免所有 worker 同时重启。
# MemoryMax=512M cgroup 内存硬上限,超限立即 OOM kill,保护宿主机。
# LimitNOFILE=65535 SOCKS5 代理每个实例一个监听 socket + 大量客户端连接,
# 默认 1024 远远不够。
[Unit]
Description=SOCKS5 Proxy Manager
After=network.target
[Service]
Type=notify
User=root
WorkingDirectory=/opt/socks-manager
Environment="PATH=/usr/local/bin:/usr/bin:/bin"
# 生产环境变量(按需修改)— 敏感信息建议放到 /opt/socks-manager/.env 或
# /etc/socks-manager/socks-manager.env,用 EnvironmentFile= 引入,不要写进 git。
Environment="SM_ADMIN_PASSWORD=test123"
Environment="SM_SECRET_KEY=xxxx111wwwddadljkkjkjnmooihjklhhgk"
Environment="SM_LOG_LEVEL=WARN"
# Environment="SM_COOKIE_SECURE=true" # HTTPS 时开启
# Environment="SM_SESSION_DOMAIN=你的域名或IP" # 跨域时设置
ExecStart=/usr/local/bin/gunicorn \
-w 1 \
-b 0.0.0.0:5000 \
--timeout 30 \
--graceful-timeout 30 \
--max-requests 1000 \
--max-requests-jitter 100 \
--limit-request-line 8190 \
--limit-request-fields 100 \
--limit-request-field_size 8190 \
run:app
# 优雅关闭:先给 SOCKS5 实例时间关闭活跃连接
ExecStop=/bin/kill -s TERM $MAINPID
TimeoutStopSec=60
Restart=always
RestartSec=5
# 资源限制:1GB 内存的 VPS 单 worker 留 512M 足够,超了立刻被 OOM kill 而非慢慢拖死整机
MemoryMax=512M
MemoryHigh=384M
LimitNOFILE=65535
[Install]
WantedBy=multi-user.target