mirror of
https://gitee.com/xmz7891/ykxmz.git
synced 2026-10-19 02:30:43 +00:00
服务端 zip 加一键部署脚本(cloudflared 安装 + 部署 + 公网,全自动)
用户要求:服务器端文件里放一个 .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
This commit is contained in:
+96
-48
@@ -241,7 +241,7 @@ AGENT_README = f"""========================================================
|
||||
|
||||
RELAY_README = f"""========================================================
|
||||
服务器端(随身 WiFi / Linux) 版本 {VERSION}
|
||||
========================================================
|
||||
========================================================
|
||||
|
||||
作用:让**不在同一个网络**的电脑和手表连上。
|
||||
|
||||
@@ -253,22 +253,45 @@ RELAY_README = f"""========================================================
|
||||
内存占用约 20MB,加 cloudflared 共约 60MB —— 512MB 的小机器跑得动。
|
||||
|
||||
|
||||
【装】
|
||||
【装:一条命令,全自动】
|
||||
|
||||
把本文件夹拷到设备上,然后:
|
||||
|
||||
bash install.sh
|
||||
bash deploy.sh
|
||||
|
||||
它会:
|
||||
1. 检查 python3 / cloudflared(没有就下载)
|
||||
2. 装两个 systemd 服务并设为开机自启:
|
||||
lanremote-relay 中转服务
|
||||
lanremote-tunnel Cloudflare 隧道
|
||||
3. 打印公网地址和管理页地址
|
||||
它会一口气做完这些,不用你一步步敲:
|
||||
|
||||
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
|
||||
@@ -276,76 +299,86 @@ RELAY_README = f"""========================================================
|
||||
journalctl -u lanremote-relay -f
|
||||
|
||||
|
||||
【关于 install.sh(旧版脚本,仍在包里)】
|
||||
|
||||
装匿名快速隧道,公网地址每次重启都变。
|
||||
新机器建议用 deploy.sh(具名隧道,地址固定)。
|
||||
install.sh 保留是为了兼容已经用它装过的机器。
|
||||
|
||||
|
||||
【地址老变怎么办?记住一个局域网地址就行】
|
||||
|
||||
匿名隧道的公网地址每次重启都会变。查当前地址:
|
||||
|
||||
浏览器打开 http://<随身WiFi的IP>:8080/url
|
||||
|
||||
比如 http://192.168.2.156:8080/url
|
||||
它会直接返回当前的公网地址,纯文本,不用登录。
|
||||
|
||||
下面还有两个命令(SSH 上去用):
|
||||
lanremote-url 打印当前公网地址
|
||||
systemctl restart lanremote-tunnel 换个地址(超时的时候用)
|
||||
具名隧道(deploy.sh 装的)地址是固定的,不用管这个。
|
||||
|
||||
|
||||
【管理】
|
||||
|
||||
管理页: <公网地址>/?key=<lanremote-key 的输出>
|
||||
管理页顶部也会大字显示"当前公网地址"。
|
||||
管理密码:120830(存在 data/admin_password.txt,可改)
|
||||
官网: <公网地址>/
|
||||
管理页: <公网地址>/?key=<lanremote-key 的输出>
|
||||
管理密码: 120830(存在 data/admin_password.txt,可改)
|
||||
|
||||
能看到:设备列表、在线状态、配对码、流量、会话数
|
||||
能操作:踢会话、抹掉配对码绑定
|
||||
|
||||
|
||||
【重要:这几个参数都是实测调出来的】
|
||||
【网络调优:这些参数都是实测调出来的】
|
||||
|
||||
install.sh 里给 cloudflared 加了:
|
||||
--edge-ip-version 4 只走 IPv4
|
||||
--ha-connections 1 并发连接数
|
||||
--retries 5 连边缘失败时多重试
|
||||
deploy-tune.sh 会做这五件事,改动之前先看它开头的说明:
|
||||
|
||||
为什么(都是踩出来的,不是拍脑袋):
|
||||
① TCP keepalive 30/10/3
|
||||
这条 4G 线路会**静默丢连接**(不发明文 FIN/RST)。
|
||||
Linux 默认要 7200 秒才发现死链,表现就是"过几分钟突然 502"。
|
||||
改成 30 秒首发探测之后,最快 60 秒就能发现并重建。
|
||||
|
||||
① IPv6:这条线路**分配了 IPv6 地址但实际不通**,
|
||||
而 Cloudflare 边缘默认优先 IPv6,会导致建隧道一直超时
|
||||
(日志里是 context deadline exceeded)。
|
||||
② tcp_mtu_probing = 1
|
||||
接口写着 MTU 1500,但实际路径扛不住(1472 通、1500 全丢)。
|
||||
不打开探测的话,新建 TLS 连接约 30% 失败。
|
||||
|
||||
② 协议:**用默认的 QUIC,不要写 --protocol http2**。
|
||||
实测这条线路把 HTTP/2 用的 TCP 7844 挡了:
|
||||
TLS handshake with edge error: read tcp ...:7844: i/o timeout
|
||||
而 QUIC 走 UDP 7844 是通的。
|
||||
(早期版本这里误写成 --protocol http2,隧道完全起不来。)
|
||||
③ MSS 钳制到 1360
|
||||
只限制 TCP 段,不动链路层。
|
||||
实测:不夹 87%,夹到 1360/1400 都是 100%。
|
||||
|
||||
③ 连接数:用 1(默认是 4)。这条线路对并发 UDP 很敏感:
|
||||
链路好时 ha=4 只成功注册 1 条(9/10),ha=2 是 10/10;
|
||||
链路退化时 ha=2 掉到 0/10(一条失败重试把另一条也拖下水),
|
||||
而 ha=1 是 15/15 + 10/10(100%)。
|
||||
多一条连接不是多一份冗余,而是多一份争抢。
|
||||
冗余交给看门狗(每 30 秒查一次,断了就重启)更靠谱。
|
||||
④ 接口 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 秒。
|
||||
|
||||
|
||||
【关于隧道地址会变】
|
||||
【隧道的两套配置】
|
||||
|
||||
默认用的是 Cloudflare **匿名快速隧道**,地址每次重启会变。
|
||||
想固定地址要用**具名隧道**(需要你自己的域名):
|
||||
deploy.sh 会装两份隧道服务,但**只启用一份** ——
|
||||
两条隧道同时跑会互相争抢,实测反而更不稳。
|
||||
|
||||
cloudflared tunnel login # 浏览器点授权
|
||||
cloudflared tunnel create lanremote
|
||||
cloudflared tunnel route dns lanremote relay.你的域名
|
||||
# 然后把 lanremote-tunnel.service 的 ExecStart 换成:
|
||||
# cloudflared tunnel --no-autoupdate run lanremote
|
||||
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=128M。
|
||||
默认给服务设了 MemoryMax(relay 128M / tunnel 160M)。
|
||||
要再省就把用不上的服务停掉:
|
||||
systemctl stop mariadb php8.2-fpm apache2
|
||||
systemctl disable mariadb php8.2-fpm apache2
|
||||
@@ -490,11 +523,25 @@ def main() -> int:
|
||||
key=lambda f: f.name)
|
||||
for src in wanted:
|
||||
shutil.copy2(src, server_dir / src.name)
|
||||
# ⚠ 产品截图是个**目录**,上面那个 iterdir() 只收文件、而且不递归,
|
||||
# 所以 site_shots/ 一直没被打进服务端包 ——
|
||||
# 装到新机器上官网的截图全是 404。
|
||||
# 这个是拿「从 zip 解压出来真跑一遍」的端到端测试才发现的。
|
||||
shots_src = RELAY_SRC / "site_shots"
|
||||
if shots_src.is_dir():
|
||||
shutil.copytree(shots_src, server_dir / "site_shots")
|
||||
else:
|
||||
print(" [警告] relay/site_shots 不存在,官网截图会缺失")
|
||||
write_readme(server_dir / "README-server.txt", RELAY_README)
|
||||
_copy_docs(server_dir)
|
||||
for must in ("relay.py", "install.sh", "watchdog.sh", "net-tune.sh"):
|
||||
if not (server_dir / must).is_file():
|
||||
print(f" [警告] 服务器端少了 {must}")
|
||||
# 一键部署脚本必须进包 —— 用户拿到的 zip 里要有它才能双击/一条命令跑起来
|
||||
for must in ("deploy.sh", "deploy-tune.sh"):
|
||||
if not (server_dir / must).is_file():
|
||||
print(f" [失败] 服务器端少了一键部署脚本 {must}")
|
||||
return 1
|
||||
server_zip = dist / f"{server_name}.zip"
|
||||
n = _zip_dir(server_zip, server_dir, server_name)
|
||||
results.append(("服务器端", server_zip, n))
|
||||
@@ -544,7 +591,8 @@ def main() -> int:
|
||||
return 1
|
||||
with zipfile.ZipFile(results[1][1]) as archive:
|
||||
names = archive.namelist()
|
||||
for must in ("relay.py", "install.sh", "watchdog.sh", "net-tune.sh"):
|
||||
for must in ("relay.py", "install.sh", "watchdog.sh", "net-tune.sh",
|
||||
"deploy.sh", "deploy-tune.sh"):
|
||||
if not any(must in n for n in names):
|
||||
print(f" [失败] 服务器端包里没有 {must}")
|
||||
return 1
|
||||
|
||||
Reference in New Issue
Block a user