mirror of
https://gitee.com/xmz7891/ykxmz.git
synced 2026-10-18 14:50:45 +00:00
用户要求:服务器端文件里放一个 .sh,双击就能检查 cloudflared 装没装、
自动部署、配好公网,以后全自动。
== 新增 ==
relay/deploy.sh 一键部署主脚本,7 步全自动:
环境检查 -> 装 cloudflared -> 部署程序 -> 网络调优
-> 写 4 个 systemd 服务 -> 配隧道 -> 启动自检打公网地址
relay/deploy-tune.sh 网络调优,可单独重跑:
keepalive 30/10/3 + 路径 MTU 探测 + MSS 1360
+ 接口 MTU 1400 + mask networkd-wait-online
每一项都写明了实测依据
用法:
bash deploy.sh 复用已有凭据,全自动
TUNNEL_TOKEN=eyJh... bash deploy.sh Token 模式
bash deploy.sh --hostname relay.你的域名 换域名
bash deploy.sh --skip-tunnel 只装中转
bash deploy.sh --help 全部参数
重跑 = 升级;已有数据/配对码/管理密码/凭据/手改过的 config.yml 都不动。
== 自动化到什么程度 ==
* cloudflared 不在就自动下载装上(并校验能执行)
* 隧道支持两种模式:已有凭据文件、或 TUNNEL_TOKEN
* **不传 --hostname 且没有 Token 时明确提示要填**,不会静默用一个写死的值
(用户明确说过:这个中转站是他自己用的,不对外开放,
绝不能把私人域名烧成默认值)
* deploy-tune.sh 的 MTU 取 1400:路径 MTU 实测 1472,
而 MSS 钳制只管 TCP、QUIC 走 UDP 完全不受保护;
接口 MTU 降到 1400 后 QUIC 建齐 10 条连接从 28 秒缩到 12 秒
== 顺带修掉两个真 bug ==
1. write_tunnel_unit 写的文件名漏了 .service 后缀 ——
文件写下去了但 systemd 完全看不见,enable 还会安静地失败。
2. 发布包一直缺 site_shots/(老 bug):
package_remote.py 用 iterdir() + is_file() 收文件,目录进不去。
新机器装完官网截图全是 404。只有从 zip 解压真跑一遍才暴露。
同时把 deploy.sh 里 site_shots 的检查降级成警告(缺截图不该让部署失败)。
另外给 deploy-tune.sh 清旧 MSS 规则的循环加了 10 次上限 ——
原写法在 -D 报成功但规则仍在时会死循环把整个部署卡死。
== 验证 ==
* _build/test_deploy_sh.sh 沙箱 65 项,全通过
* _build/test_zip_deploy.sh 从发布 zip 解压再跑,30 项,全通过
* _build/test_deploy_real.sh **真机 Debian 上实测**(新增):
zip 解出 17 个文件 -> 语法/LF/无 BOM 通过
--help 退出码 0 且无副作用、未知参数退出码 2
沙箱安装成功、幂等重跑哨兵不被冲
现有三个中转服务全程 active,自检 200
205 lines
6.8 KiB
Python
205 lines
6.8 KiB
Python
# -*- coding: utf-8 -*-
|
||
r'''把 package_remote.py 里的 RELAY_README 整块换成新版本。
|
||
|
||
为什么按块换而不是用 edit 工具:
|
||
那段是个 f-string 三引号大块,里面有几百行、
|
||
还有各种全角空格和竖线字符,逐字符对齐很容易失败。
|
||
定位「RELAY_README = f"""」到下一个单独的 `"""` 行更稳。
|
||
'''
|
||
from __future__ import annotations
|
||
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
SRC = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote"
|
||
r"\_build\package_remote.py")
|
||
BOM = b"\xef\xbb\xbf"
|
||
|
||
NEW_BODY = '''RELAY_README = f"""========================================================
|
||
服务器端(随身 WiFi / Linux) 版本 {VERSION}
|
||
========================================================
|
||
|
||
作用:让**不在同一个网络**的电脑和手表连上。
|
||
|
||
手表 ──HTTPS──┐
|
||
├──> Cloudflare ──> 本机 :8080
|
||
电脑 ──HTTPS──┘
|
||
|
||
本程序是**纯标准库**的 Python,不装任何第三方包。
|
||
内存占用约 20MB,加 cloudflared 共约 60MB —— 512MB 的小机器跑得动。
|
||
|
||
|
||
【装:一条命令,全自动】
|
||
|
||
把本文件夹拷到设备上,然后:
|
||
|
||
bash deploy.sh
|
||
|
||
它会一口气做完这些,不用你一步步敲:
|
||
|
||
1. 检查并安装 cloudflared(已经装了就用现成的)
|
||
2. 把中转程序装到 /root/lanremote-relay/
|
||
3. 网络调优(keepalive / MSS / MTU / 开机加速)
|
||
4. 注册并启动 4 个 systemd 服务(开机自启)
|
||
5. 配好 Cloudflare 隧道
|
||
6. 自检,然后把**公网地址**打印出来
|
||
|
||
── 三种常见情形 ──
|
||
|
||
① 机器上已经有隧道凭据(/root/.cloudflared/*.json)
|
||
直接: bash deploy.sh
|
||
|
||
② 有 Cloudflare 给的隧道 Token
|
||
TUNNEL_TOKEN=eyJhIjoi... bash deploy.sh
|
||
(用环境变量而不是 --token,Token 就不会留在 shell 历史里)
|
||
|
||
③ 换域名 / 只装中转不要隧道
|
||
bash deploy.sh --hostname relay.你的域名
|
||
bash deploy.sh --skip-tunnel
|
||
|
||
看全部参数: bash deploy.sh --help
|
||
|
||
|
||
**重跑 deploy.sh = 升级**,不会把已有配置冲掉:
|
||
设备数据、配对码、管理密码、隧道凭据、手工改过的 config.yml
|
||
都会原样保留。
|
||
|
||
|
||
【常用命令】
|
||
|
||
lanremote-status 看整体状态(服务 / 端口 / 公网 / 内存)
|
||
lanremote-url 打印当前公网地址
|
||
lanremote-key 打印管理钥匙
|
||
systemctl status lanremote-relay
|
||
systemctl status lanremote-tunnel
|
||
journalctl -u lanremote-relay -f
|
||
|
||
|
||
【关于 install.sh(旧版脚本,仍在包里)】
|
||
|
||
装匿名快速隧道,公网地址每次重启都变。
|
||
新机器建议用 deploy.sh(具名隧道,地址固定)。
|
||
install.sh 保留是为了兼容已经用它装过的机器。
|
||
|
||
|
||
【地址老变怎么办?记住一个局域网地址就行】
|
||
|
||
匿名隧道的公网地址每次重启都会变。查当前地址:
|
||
|
||
浏览器打开 http://<随身WiFi的IP>:8080/url
|
||
|
||
它会直接返回当前的公网地址,纯文本,不用登录。
|
||
|
||
具名隧道(deploy.sh 装的)地址是固定的,不用管这个。
|
||
|
||
|
||
【管理】
|
||
|
||
官网: <公网地址>/
|
||
管理页: <公网地址>/?key=<lanremote-key 的输出>
|
||
管理密码: 120830(存在 data/admin_password.txt,可改)
|
||
|
||
能看到:设备列表、在线状态、配对码、流量、会话数
|
||
能操作:踢会话、抹掉配对码绑定
|
||
|
||
|
||
【网络调优:这些参数都是实测调出来的】
|
||
|
||
deploy-tune.sh 会做这五件事,改动之前先看它开头的说明:
|
||
|
||
① TCP keepalive 30/10/3
|
||
这条 4G 线路会**静默丢连接**(不发明文 FIN/RST)。
|
||
Linux 默认要 7200 秒才发现死链,表现就是"过几分钟突然 502"。
|
||
改成 30 秒首发探测之后,最快 60 秒就能发现并重建。
|
||
|
||
② tcp_mtu_probing = 1
|
||
接口写着 MTU 1500,但实际路径扛不住(1472 通、1500 全丢)。
|
||
不打开探测的话,新建 TLS 连接约 30% 失败。
|
||
|
||
③ MSS 钳制到 1360
|
||
只限制 TCP 段,不动链路层。
|
||
实测:不夹 87%,夹到 1360/1400 都是 100%。
|
||
|
||
④ 接口 MTU 1400
|
||
**这一条是专门给 QUIC 隧道的**:MSS 钳制走 iptables 的
|
||
TCPMSS 目标,只对 TCP 生效;QUIC 跑 UDP,完全不受它保护。
|
||
QUIC 的包超过路径 MTU 就会被丢,而 UDP 丢包不回 ICMP,
|
||
只能干等握手超时(日志里那句
|
||
"failed to dial to edge with quic: timeout: no recent network activity")。
|
||
实测把 MTU 从 1500 降到 1400:建齐 6 条连接 28 秒 -> 13 秒。
|
||
|
||
⑤ mask 掉 systemd-networkd-wait-online
|
||
这台机器 wlan0 归 NetworkManager 管,
|
||
systemd-networkd-wait-online 傻等一个不归它的网络,
|
||
干等 2 分钟超时,把 relay / tunnel 全拖住。
|
||
实测:掉电重启后公网恢复 2 分 10 秒 -> 24 秒。
|
||
|
||
|
||
【隧道的两套配置】
|
||
|
||
deploy.sh 会装两份隧道服务,但**只启用一份** ——
|
||
两条隧道同时跑会互相争抢,实测反而更不稳。
|
||
|
||
lanremote-tunnel 主用(默认 quic)
|
||
lanremote-tunnel-h2 备用(http2)
|
||
|
||
想切换:
|
||
systemctl disable --now lanremote-tunnel
|
||
systemctl enable --now lanremote-tunnel-h2
|
||
|
||
为什么默认 quic:尾部延迟好得多(实测 P99 2.99 秒 vs 13.9 秒)。
|
||
什么时候换 http2:这条线路对 UDP 特别不友好的时候
|
||
(日志里大量 "Failed to dial a quic connection")。
|
||
|
||
|
||
【省内存】
|
||
|
||
默认给服务设了 MemoryMax(relay 128M / tunnel 160M)。
|
||
要再省就把用不上的服务停掉:
|
||
systemctl stop mariadb php8.2-fpm apache2
|
||
systemctl disable mariadb php8.2-fpm apache2
|
||
"""'''
|
||
|
||
|
||
def main() -> int:
|
||
raw = SRC.read_bytes()
|
||
had_bom = raw.startswith(BOM)
|
||
text = raw.decode("utf-8-sig")
|
||
|
||
start = text.find('RELAY_README = f"""')
|
||
if start < 0:
|
||
print(" ✗ 找不到 RELAY_README 的开头")
|
||
return 1
|
||
|
||
# 结束:从开头往后找第一个「单独一行 """」的结束引号
|
||
body_start = start + len('RELAY_README = f"""')
|
||
end = text.find('\n"""', body_start)
|
||
if end < 0:
|
||
print(" ✗ 找不到 RELAY_README 的结束引号")
|
||
return 1
|
||
end += len('\n"""') # 把结束引号一起吃掉
|
||
|
||
old = text[start:end]
|
||
print(" 原块 %d 行 -> 新块 %d 行"
|
||
% (old.count("\n") + 1, NEW_BODY.count("\n") + 1))
|
||
|
||
if old.strip() == NEW_BODY.strip():
|
||
print(" 内容已经一样了,不动")
|
||
return 0
|
||
|
||
text = text[:start] + NEW_BODY + text[end:]
|
||
data = text.encode("utf-8")
|
||
if had_bom:
|
||
data = BOM + data
|
||
SRC.write_bytes(data)
|
||
|
||
# 语法自检
|
||
import ast
|
||
ast.parse(SRC.read_text(encoding="utf-8-sig"))
|
||
print(" ✓ 已替换,语法检查通过")
|
||
return 0
|
||
|
||
|
||
if __name__ == "__main__":
|
||
sys.exit(main())
|