文档新增说明restart
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,182 @@
|
||||
# Xray 定时重启:可配置的「每 N 天 + 固定时间点」
|
||||
|
||||
日期:2026-08-06
|
||||
涉及文件:`vps-xray/deploy.sh`、`vps-xray/.env.example`、`vps-xray/uninstall.sh`、`vps-xray/README.md`
|
||||
|
||||
## 背景
|
||||
|
||||
当前 `deploy.sh` 只有一个布尔开关 `XRAY_DAILY_RESTART`,为真时创建 `OnCalendar=*-*-* 04:00:00` 的每日重启 timer,为假时删除该 timer。默认关闭,理由是崩溃恢复已由 `Restart=always` + `RestartSec=3` 覆盖(秒级),而每日硬重启会切断全部活动连接。
|
||||
|
||||
这个判断本身没变,但「一天一次」粒度太粗:想要「每 7 天凌晨 4 点重启一次」这种低频维护性重启时,只能在「每天重启」和「完全不重启」之间二选一。
|
||||
|
||||
## 目标
|
||||
|
||||
1. 支持任意 N 天间隔 + 指定时间点,如「每 7 天 04:00」「每 3 天 05:30」
|
||||
2. 保持默认关闭,已部署机器重跑脚本时行为不变
|
||||
3. 提供一个轻量命令行入口,只改重启计划,不触碰 Xray 与现有 vless 链接
|
||||
|
||||
## 非目标
|
||||
|
||||
- 不做「每月/每季度」等日历语义(真有需要时用户可直接编辑 timer)
|
||||
- 不做多时间点(如一天重启两次)
|
||||
- 不改动 `Restart=always` 这一层崩溃恢复逻辑
|
||||
|
||||
## 关键约束:systemd 表达不了任意 N 天
|
||||
|
||||
`OnCalendar` 的 `*-*-1/7 04:00:00` 是「每月的 1、8、15、22、29 号」,**跨月会重置**——29 号到下月 1 号只隔 2~3 天。因此不能直接用 `OnCalendar` 表达「每 N 天」。
|
||||
|
||||
`OnUnitActiveSec=7d` 是真实间隔,但无法钉住具体时间点,且会随每次触发缓慢漂移。
|
||||
|
||||
本设计采用第三种:**timer 每天到点唤醒,由 service 里的守卫脚本决定这次要不要真的重启。**
|
||||
|
||||
## 配置模型
|
||||
|
||||
`.env` 三个字段:
|
||||
|
||||
```bash
|
||||
XRAY_RESTART_EVERY_DAYS=0 # 0 或缺失 = 关闭;N = 每 N 天重启一次
|
||||
XRAY_RESTART_TIME=04:00 # 24 小时制 HH:MM,默认 04:00
|
||||
XRAY_RESTART_TIMEZONE=Asia/Shanghai # 默认保持现状
|
||||
```
|
||||
|
||||
首次部署默认 `0`(关闭),与当前行为一致。
|
||||
|
||||
### 旧字段迁移
|
||||
|
||||
只在 `XRAY_RESTART_EVERY_DAYS` **未设置**时才回退去读 `XRAY_DAILY_RESTART`:
|
||||
|
||||
| 旧值 | 迁移结果 |
|
||||
|---|---|
|
||||
| `true` / `yes` / `on` / `1`(大小写不敏感) | `XRAY_RESTART_EVERY_DAYS=1` |
|
||||
| 其它或缺失 | `XRAY_RESTART_EVERY_DAYS=0` |
|
||||
|
||||
`save_env` 之后只写新字段,不再写 `XRAY_DAILY_RESTART`,`.env` 随重跑自动换代。两个字段同时存在时以新字段为准。
|
||||
|
||||
现存四台服务器的 `.env` 都是 `XRAY_DAILY_RESTART=false`,迁移后为 `0`,行为不变。
|
||||
|
||||
## 实现架构
|
||||
|
||||
```
|
||||
xray-restart.timer OnCalendar=*-*-* <HH:MM>:00 每天到点唤醒
|
||||
TimeZone=<tz>
|
||||
Persistent=true
|
||||
↓
|
||||
xray-restart.service Type=oneshot
|
||||
ExecStart=/usr/local/bin/xray-restart-guard
|
||||
↓
|
||||
xray-restart-guard 读 /var/lib/xray/last-restart
|
||||
距上次不足 N 天 → 打日志后 exit 0
|
||||
满 N 天 → 写入新时间戳 → systemctl restart xray
|
||||
```
|
||||
|
||||
「每 N 天」因此是**距上次实际重启**的真实间隔,不受跨月重置影响,N=3 与 N=7 同样准确。
|
||||
|
||||
### 守卫脚本的两个关键细节
|
||||
|
||||
**300 秒容差。** systemd timer 默认 `AccuracySec=1min`,第 N 次触发的实际间隔可能是 `N×86400 - 30s`。严格比较会判定「不满 N 天」从而顺延整整一天,且误差会持续累积。阈值取 `N×86400 - 300` 规避。容差远小于一天,不会造成重复触发。
|
||||
|
||||
**先写时间戳,再重启。** `systemctl restart xray` 会中断 guard 所在的 systemd 事务。顺序反了会丢失时间戳记录,导致每天都重启。代价是:重启失败时时间戳仍已更新,本轮被跳过——这比陷入每日重启循环可接受。
|
||||
|
||||
### 状态文件
|
||||
|
||||
`/var/lib/xray/last-restart`,内容为一个 Unix 时间戳。guard 以 root 运行,读写无权限问题。内容非法(非纯数字)时按 `0` 处理,即立即允许重启。
|
||||
|
||||
## 参数校验
|
||||
|
||||
在脚本早期(`.env` 载入后)完成,不合法直接报错退出,**不静默回退到默认值**——避免用户以为设了每 7 天、实际在每天重启。
|
||||
|
||||
| 字段 | 规则 |
|
||||
|---|---|
|
||||
| `XRAY_RESTART_EVERY_DAYS` | `^[0-9]+$` |
|
||||
| `XRAY_RESTART_TIME` | `^([01]?[0-9]\|2[0-3]):[0-5][0-9]$`,校验通过后**补零归一化**为 `HH:MM` |
|
||||
| `XRAY_RESTART_TIMEZONE` | 非空即可,交由 systemd 校验并在启动失败时报错 |
|
||||
|
||||
时间同时接受 `4:00` 与 `04:00`,但写入 `.env` 和 timer 前一律归一化成两位小时(`04:00`),保证 `OnCalendar` 字符串格式统一、`.env` 内容可预测。
|
||||
|
||||
## `--restart` 轻量入口
|
||||
|
||||
### 用法
|
||||
|
||||
```bash
|
||||
bash deploy.sh --restart 7 04:00 # 每 7 天 04:00
|
||||
bash deploy.sh --restart 3 # 每 3 天,时间沿用 .env
|
||||
bash deploy.sh --restart 0 # 关闭
|
||||
bash deploy.sh --restart # 按 .env 现有值重建 timer
|
||||
```
|
||||
|
||||
### 参数解析
|
||||
|
||||
`--restart` 后可跟 0~2 个位置参数,按形态判断,不会误吞后续选项:
|
||||
|
||||
- 下一个 token 匹配 `^[0-9]+$` → 取作 N
|
||||
- 再下一个匹配 `^([01]?[0-9]|2[0-3]):[0-5][0-9]$` → 取作 HH:MM
|
||||
- 不匹配则不消费,沿用 `.env` 现值
|
||||
|
||||
`--restart` 与 `--mode` / `--show` / `--redetect` 互斥,同时给出直接报错。
|
||||
|
||||
### 执行路径
|
||||
|
||||
在 `main()` 中早退,与 `--show` 同一位置:
|
||||
|
||||
1. 校验 N 与 HH:MM,时间归一化为 `HH:MM`
|
||||
2. 就地更新 `.env`:命令行给了 N 就写 `XRAY_RESTART_EVERY_DAYS`,给了时间才写 `XRAY_RESTART_TIME`;两者都没给则不改 `.env`,只按现值重建 timer。字段不存在时追加(`TIME` 追加时用默认值 `04:00`)
|
||||
3. 调用 `configure_restart_timer()`
|
||||
4. `systemctl daemon-reload`,按需 enable / disable timer
|
||||
5. 打印结果与 `systemctl list-timers xray-restart.timer`
|
||||
|
||||
### 明确不做的事
|
||||
|
||||
不装依赖、不升级 Xray、不生成密钥、不选伪装目标、不重写 `config.json`、不动防火墙与 sysctl、**不重启 xray**。因此 vless 链接与现有连接均不受影响。这些约束会以注释形式写在函数头部。
|
||||
|
||||
### 前置检查
|
||||
|
||||
- `.env` 不存在 → 报错退出(无法确认这是已部署的机器)
|
||||
- xray 主单元不存在 → 报错退出(避免在未部署的机器上留下孤儿 timer)
|
||||
|
||||
## 代码组织
|
||||
|
||||
**timer / service / guard 的创建逻辑抽成独立函数 `configure_restart_timer()`**,由 `harden_system()` 与 `--restart` 路径共用。两条路径各写一份实现迟早会漂移成不一致。
|
||||
|
||||
`.env` 更新走两条不同路径,这是有意的:
|
||||
|
||||
- 完整部署走既有的 `save_env()`(`cat >` 全量重写)
|
||||
- `--restart` 走 `sed` 就地替换 —— 轻量入口只应改自己那两行,不该抹掉用户在 `.env` 里加的注释和自定义字段
|
||||
|
||||
## 关闭与清理
|
||||
|
||||
`XRAY_RESTART_EVERY_DAYS=0` 时:
|
||||
|
||||
- `systemctl disable --now xray-restart.timer`
|
||||
- 删除 `xray-restart.timer`、`xray-restart.service`、`/usr/local/bin/xray-restart-guard`
|
||||
- **保留** `/var/lib/xray/last-restart`
|
||||
|
||||
保留状态文件的理由:以后重新启用时,若距上次已超过 N 天则首次触发即重启,符合直觉;若刚重启过则不会立刻再来一次。
|
||||
|
||||
`uninstall.sh` 需补上 guard 脚本与状态文件的清理(当前只删了 timer 和 service)。
|
||||
|
||||
## 受影响文件
|
||||
|
||||
| 文件 | 改动 |
|
||||
|---|---|
|
||||
| `vps-xray/deploy.sh` | 参数解析、配置校验与迁移、`configure_restart_timer()`、`--restart` 路径、`save_env`、`usage` |
|
||||
| `vps-xray/.env.example` | 三个新字段与说明 |
|
||||
| `vps-xray/uninstall.sh` | 清理 guard 脚本与状态文件 |
|
||||
| `vps-xray/README.md` | 定时重启一节 |
|
||||
|
||||
## 验证方案
|
||||
|
||||
在真实 VPS 上执行,不接受仅靠阅读代码判断:
|
||||
|
||||
1. **语法** —— `bash -n deploy.sh`
|
||||
2. **启用** —— `bash deploy.sh --restart 7 04:00`,确认:`.env` 两字段已更新;`systemctl list-timers xray-restart.timer` 有下次触发时间;xray 主服务的 `ActiveEnterTimestamp` **未变**(证明没重启)
|
||||
3. **守卫生效** —— 手动连跑 `xray-restart-guard` 两次,第一次应重启并写入时间戳,第二次应打印「跳过」且 xray 未重启
|
||||
4. **关闭** —— `bash deploy.sh --restart 0`,确认 timer / service / guard 三个文件均被删除,状态文件仍在
|
||||
5. **迁移** —— 构造一份只含 `XRAY_DAILY_RESTART=true` 的 `.env`,跑完整部署,确认得到 `XRAY_RESTART_EVERY_DAYS=1` 且 timer 已建立
|
||||
6. **校验** —— `bash deploy.sh --restart 7 25:00` 与 `--restart abc` 均应报错退出,且不留下任何单元文件
|
||||
7. **互斥** —— `bash deploy.sh --restart 7 --redetect` 应报错退出
|
||||
|
||||
## 已知取舍
|
||||
|
||||
- **间隔锚点是「上次实际重启时间」,不是固定日历日期。** 若某次因机器关机错过了触发点,下次开机后的第一个触发点会补上(`Persistent=true`),此后间隔重新从那一刻起算,会缓慢偏移触发日期。这是「真实间隔」语义的必然结果,也是用户选择的语义。
|
||||
- **重启失败时时间戳仍已更新**,本轮被跳过,下一轮照常。理由见上文「先写时间戳」。
|
||||
- **时区错误要到 timer 启动时才暴露**。脚本不预校验时区字符串,靠 `systemctl` 报错——自行维护时区白名单成本高且易过期。
|
||||
Reference in New Issue
Block a user