mirror of
https://gitee.com/xmz7891/ykxmz.git
synced 2026-10-18 16:00:43 +00:00
2.1.1 修掉了 nettune 死锁之后,公网上到 200,但**过一阵又 530**。
这次抓到的是隧道自己:
cloudflared 的 precheck 输出(tunnel.log):
| UDP Connectivity region1.v2.argotunnel.com FAIL QUIC connection failed |
| TCP Connectivity region1.v2.argotunnel.com FAIL HTTP/2 connection is blocked or unreachable |
| Cloudflare API api.cloudflare.com:443 PASS API is reachable |
| ERROR: Allow outbound QUIC traffic on port 7844 or use HTTP2. |
也就是 **443 通、7844 不通**,而且是**间歇性**的(过几分钟自己又好了,
然后再次断)。Cloudflare 隧道的边缘连接走 7844(QUIC 走 UDP 7844、
HTTP/2 走 TCP 7844)。这条线路对它是间歇性阻断 —— 属于运营商那一侧,
设备改不了,但可以让 cloudflared 自己去适应。
== 两个改动 ==
1. **协议从写死改成 auto**
lanremote-tunnel --protocol quic -> --protocol auto
lanremote-tunnel-h2 --protocol http2 -> --protocol auto
原来两条实例各赌一个协议:那一刻 7844 的 UDP 和 TCP 都不通就一起死,
没有退路(实测连接数掉到 1/0、公网 530)。改成 auto 之后
cloudflared 自己在 QUIC / HTTP/2 之间挑当前能通的那条,
10 条连接全都有自适应能力。
2. **--retries 5 -> 1000000**
原来失败 5 次就退出、靠 systemd Restart=always 拉起来,
每次退出到重启之间网站都是断的。而这条线路的抖动是分钟级的 ——
实测隧道每 3~10 分钟就被重启一次(19:37 / 19:40 / 19:43 / 19:50 / 19:52),
公网跟着一起断。拉大之后抖动期间连接自己恢复,整条隧道不退出。
3. 顺手加日志轮转(tunnel.log 已经 2.98 MB,这台机器硬盘不大)。
== 实测 ==
改前:连接数 1/0,公网 530
改后:连接数 7+2(接近满配 10),公网连测 5 次全部 200
== 同步 ==
relay/deploy.sh 里的 MAIN_PROTO / ALT_PROTO 也改成 auto、
TUNNEL_BASE 的 --retries 也拉到 1000000,新机器装出来就是加固过的。
全量回归 46 个阶段全部通过。
163 lines
6.4 KiB
Python
163 lines
6.4 KiB
Python
# -*- coding: utf-8 -*-
|
||
r'''增强隧道稳定性 —— 「网站还是容易挂」的真因修法。
|
||
|
||
== 查到什么 ==
|
||
|
||
cloudflared 自己的 precheck 输出(tunnel.log):
|
||
|
||
| UDP Connectivity region1.v2.argotunnel.com FAIL QUIC connection failed |
|
||
| UDP Connectivity region2.v2.argotunnel.com FAIL QUIC connection failed |
|
||
| TCP Connectivity region1.v2.argotunnel.com FAIL HTTP/2 connection is blocked or unreachable |
|
||
| TCP Connectivity region2.v2.argotunnel.com FAIL HTTP/2 connection is blocked or unreachable |
|
||
| Cloudflare API api.cloudflare.com:443 PASS API is reachable |
|
||
| ERROR: Allow outbound QUIC traffic on port 7844 or use HTTP2. |
|
||
|
||
也就是:**443 通,7844 不通**,而且是**间歇性**的
|
||
(日志后面又出现 Registered tunnel connection,过一阵又掉)。
|
||
|
||
Cloudflare 隧道的边缘连接走 7844(QUIC 走 UDP 7844、HTTP/2 走 TCP 7844)。
|
||
这条线路对 7844 是间歇性阻断的 —— 这不是设备能修的,
|
||
但**可以让 cloudflared 自己去适应**。
|
||
|
||
== 现在的配置有两个问题 ==
|
||
|
||
1. **两条实例各写死一个协议**
|
||
lanremote-tunnel --protocol quic (只走 UDP 7844)
|
||
lanremote-tunnel-h2 --protocol http2 (只走 TCP 7844)
|
||
如果那一刻 7844 的 UDP 和 TCP 都不通,两条一起死 —— 没有退路。
|
||
实测就是这样:连接数掉到 1/0,公网 530。
|
||
|
||
2. **--retries 5**
|
||
cloudflared 重试 5 次就**退出**,靠 systemd Restart=always 拉起来。
|
||
每次退出/重启期间网站都是断的。而这个线路的抖动是分钟级的,
|
||
5 次远远不够扛过去。
|
||
|
||
== 改法 ==
|
||
|
||
1. `--protocol auto` —— 让 cloudflared **自己**在 QUIC 和 HTTP/2 之间选,
|
||
哪条通走哪条。两条实例都改成 auto,等于 10 条连接全都能自适应。
|
||
(而不是现在这样"8 条赌 UDP、2 条赌 TCP")
|
||
|
||
2. `--retries` 提到很大(相当于"一直重试,别退出")——
|
||
抖动期间连接会自己恢复,不会整条隧道退出让网站断掉。
|
||
|
||
3. 顺手加日志轮转:tunnel.log 已经涨到 2.98 MB,
|
||
这台机器只有 460MB 内存 + 小硬盘,无限增长不合适。
|
||
'''
|
||
from __future__ import annotations
|
||
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
ROOT = Path(__file__).resolve().parent.parent
|
||
sys.path.insert(0, str(ROOT / "_build"))
|
||
from devssh import run # noqa: E402
|
||
|
||
|
||
def patch_unit(name: str, proto: str) -> str:
|
||
"""读设备上现有的 unit,改协议和重试次数。
|
||
|
||
用 sed 现场改而不是整份重写 —— 现有 unit 里还有
|
||
metrics 端口、ha-connections、MemoryMax 这些实测调出来的参数,
|
||
整份重写容易漏掉。
|
||
"""
|
||
cmds = [
|
||
# 协议
|
||
"sed -i 's/--protocol [a-z0-9]*/--protocol %s/' %s" % (proto, name),
|
||
# 重试次数:改成很大(一直重试)
|
||
"sed -i 's/--retries [0-9]*/--retries 1000000/' %s" % name,
|
||
]
|
||
return " && ".join(cmds)
|
||
|
||
|
||
def main() -> int:
|
||
print("=" * 62)
|
||
print(" 隧道稳定性:协议改 auto + 加大重试 + 日志轮转")
|
||
print("=" * 62)
|
||
|
||
UNIT_DIR = "/etc/systemd/system"
|
||
|
||
print()
|
||
print(" ── 1. 两条隧道都改成 --protocol auto ──")
|
||
out = run(
|
||
"cd %s && "
|
||
"sed -i 's/--protocol [a-z0-9]*/--protocol auto/' "
|
||
"lanremote-tunnel.service lanremote-tunnel-h2.service && "
|
||
"echo ' tunnel:'; grep -o -- '--protocol [a-z0-9]*' "
|
||
"lanremote-tunnel.service; "
|
||
"echo ' h2:'; grep -o -- '--protocol [a-z0-9]*' "
|
||
"lanremote-tunnel-h2.service; "
|
||
# 重试次数
|
||
"sed -i 's/--retries [0-9]*/--retries 1000000/' "
|
||
"lanremote-tunnel.service lanremote-tunnel-h2.service && "
|
||
"echo ' 重试:'; grep -o -- '--retries [0-9]*' "
|
||
"lanremote-tunnel.service lanremote-tunnel-h2.service"
|
||
% UNIT_DIR, timeout=120)
|
||
for ln in out[1].strip().splitlines():
|
||
print(" " + ln)
|
||
|
||
print()
|
||
print(" ── 2. 日志轮转(tunnel.log 已经 2.98 MB)──")
|
||
out = run(
|
||
"cat > /etc/logrotate.d/lanremote <<'EOF'\n"
|
||
"/root/lanremote-relay/*.log {\n"
|
||
" size 5M\n"
|
||
" rotate 2\n"
|
||
" compress\n"
|
||
" missingok\n"
|
||
" notifempty\n"
|
||
" copytruncate\n"
|
||
"}\n"
|
||
"EOF\n"
|
||
"echo ' logrotate 配置已写'; "
|
||
"logrotate --debug /etc/logrotate.d/lanremote 2>&1 | tail -3",
|
||
timeout=120)
|
||
for ln in out[1].strip().splitlines():
|
||
print(" " + ln)
|
||
|
||
print()
|
||
print(" ── 3. 重启两条隧道让新参数生效 ──")
|
||
out = run(
|
||
"systemctl daemon-reload && "
|
||
"systemctl restart lanremote-tunnel lanremote-tunnel-h2 && "
|
||
"echo ' 等 40 秒让连接建立…' && sleep 40 && "
|
||
"echo \" tunnel: $(systemctl is-active lanremote-tunnel)\"; "
|
||
"echo \" h2: $(systemctl is-active lanremote-tunnel-h2)\"",
|
||
timeout=300)
|
||
for ln in out[1].strip().splitlines():
|
||
print(" " + ln)
|
||
|
||
print()
|
||
print(" ── 4. 连接数 ──")
|
||
out = run(
|
||
"for p in 20241 20242; do "
|
||
"n=$(curl -s --max-time 5 http://127.0.0.1:$p/metrics 2>/dev/null "
|
||
"| grep '^cloudflared_tunnel_ha_connections' | awk '{print $2}' "
|
||
"| head -1); "
|
||
"echo \" $p -> ${n:-拿不到}\"; done; "
|
||
"echo ' --- 用的什么协议 ---'; "
|
||
"grep -o 'protocol=[a-z0-9]*' /root/lanremote-relay/tunnel.log 2>/dev/null "
|
||
"| tail -6 | sort | uniq -c | sed 's/^/ /'", timeout=120)
|
||
for ln in out[1].strip().splitlines():
|
||
print(" " + ln)
|
||
|
||
print()
|
||
print(" ── 5. 公网(连测 5 次看稳不稳)──")
|
||
out = run(
|
||
"for i in 1 2 3 4 5; do "
|
||
"c=$(curl -s -o /dev/null -w '%{http_code}' --max-time 15 "
|
||
"https://relay.xxmxxs.top/v1/health); "
|
||
"echo \" 第 $i 次: $c\"; sleep 3; done", timeout=200)
|
||
for ln in out[1].strip().splitlines():
|
||
print(" " + ln)
|
||
|
||
print()
|
||
print(" 说明:3844/7844 那条线路封不封是运营商的事,设备改不了。")
|
||
print(" 但 --protocol auto 让 cloudflared 自己绕,")
|
||
print(" --retries 拉大让它别退出 —— 抖动期间能自己恢复。")
|
||
return 0
|
||
|
||
|
||
if __name__ == "__main__":
|
||
sys.exit(main())
|