构建高可用家庭代理:从 Ngrok 到 FRP + Nginx 故障转移
构建高可用家庭代理:深入解析 FRP 和 Nginx Stream 故障转移
在个人网络和家庭实验室(Home Lab)的世界里,安全地暴露本地服务或创建一个供家人使用的稳定代理是一个常见的挑战。最初只是一个简单的需求——“我需要一个能使用我干净家庭 IP 的代理给家人用”——最终演变成了一堂关于高可用性(HA)系统设计的课程。
本文将介绍我如何从不可靠的免费层解决方案迁移到利用 FRP (Fast Reverse Proxy) 和 Nginx Stream 的健壮自托管架构,实现了在我的 Mac(主节点)和 VPS(备用节点)之间的零停机故障转移。
⚠️ 场景说明与免责声明
本文所探讨的技术仅用于合法的家庭网络远程访问与网络稳定性优化。
主要适用场景包括:
- 服务连续性保障(HA 需求来源):确保在家庭主服务器(Mac)进行维护、休眠或家庭宽带意外断网时,依赖该网络通道的关键服务(如远程监控、智能家居控制)仍能保持最低限度的可用性,避免单点故障。
- 家庭实验室回连:在外网环境下安全访问家中的 NAS、Home Assistant 或自建代码仓库。
- 开发环境统一:为移动办公设备提供统一的家庭宽带 IP,以便通过企业防火墙白名单或进行特定网络环境下的调试。
- 隐私保护:在公共 Wi-Fi 环境下加密流量,防止中间人攻击。
请勿将相关技术用于违反当地法律法规的用途。
🛑 问题:配额、封锁和不稳定性
我最初的设置依赖于 ngrok,后来是 Cloudflare Tunnel。这两者都是优秀的工具,但在我的特定用例中遇到了瓶颈:
- Ngrok:非常适合快速测试,但免费层有连接限制和动态域名。对于日常家庭使用来说,它不够“生产级”稳定。
- Cloudflare Tunnel:零信任(Zero-trust)很棒,但在我受限的网络环境中,它在处理特定的 UDP 流量和非标准端口时表现不佳。
- 仅本地运行:仅仅在我的 Mac 上运行代理意味着如果我合上笔记本电脑或网络断开,其他人的互联网也就“断”了。
我需要一个既快速(在可用时使用我的家庭带宽/IP)又可靠(当我的 Mac 离线时回退到 VPS)的解决方案。
🛠 解决方案:主备高可用架构
我设计了一个**主-备(Primary-Backup)**架构。
- 主节点 (Primary - Mac):在本地运行代理。低延迟,“干净”的住宅 IP。
- 备用节点 (Backup - VPS):廉价的云服务器。永远在线,作为故障保险。
- “开关” (The Switch):运行在 VPS 上的 Nginx 负载均衡器。
graph TD
User["客户端 (Family Devices)"] -->|"访问 VPS:10086"| Nginx["Nginx Stream 负载均衡"]
subgraph VPS ["VPS Server (Cloud)"]
direction TB
Nginx
Backup["备用代理 (Port 10088)"]
FRPS["FRPS Server"]
end
subgraph Home ["Home Network"]
direction TB
FRPC["FRPC Client"]
Mac["Mac 主代理 (Port 10086)"]
end
Nginx -->|"主路径 (Primary)"| FRPS
FRPS <==>|"FRP 隧道 (:7000)"| FRPC
FRPC -->|"转发流量"| Mac
Mac -->|"访问互联网"| Internet1
Nginx -.->|"故障转移 (Failover)"| Backup
Backup -->|"访问互联网"| Internet2
Internet1(("Internet"))
Internet2(("Internet"))
style Mac fill:#a7f3d0,stroke:#047857,stroke-width:2px
style Backup fill:#fde68a,stroke:#d97706,stroke-width:2px
style Nginx fill:#bfdbfe,stroke:#1d4ed8
1. 隧道:为什么选择 FRP?
由于我的 Mac 没有公网 IP,我需要一个反向隧道。我选择了 FRP 而不是其他工具,因为:
- 协议支持:它完美支持 TCP/UDP,这对于代理流量至关重要(不像某些仅支持 HTTP 的隧道)。
- 自托管:我控制服务器。没有第三方的速率限制。
- 轻量级:
frpc(客户端)和frps(服务端)都是单二进制文件。
配置:
我的 Mac (frpc) 连接到 VPS (frps) 的 7000 端口,将其本地代理端口 (10086) 映射到 VPS 的 10087 端口。
2. 魔法所在:Nginx Stream 模块与故障转移
这是最大的技术学习点。标准 Nginx 以 HTTP 反向代理闻名,但对于原始 TCP 代理(如 VMess),我们需要 Stream 模块。
我使用了 Nginx 的 upstream 模块配合 backup 参数来实现主备故障转移逻辑。
1 | stream { |
工作原理:
- 客户端连接到
VPS:10086。 - Nginx 尝试将流量转发到 10087(通往 Mac 的 FRP 隧道)。
- 正常路径:Mac 在线 → 流量通过隧道 → Mac -> 互联网。
- 故障路径:Mac 休眠/离线 → 10087 连接被拒绝。
- 故障转移:Nginx 立即检测到故障,并将流量路由到 10088(直接在 VPS 上运行的备份代理)。
- 恢复:每隔 3 秒(由
fail_timeout定义),Nginx 会试探性地再次尝试主节点。如果 Mac 唤醒,流量会自动切回。
3. “无状态”的客户端体验
为了让终端用户(我的家人)感觉不到切换,我确保 Mac 代理和 VPS 备用代理共享完全相同的 UUID 和加密设置。
对于客户端(例如 Shadowrocket)来说,它只看到“Server A”。它完全不知道底层的后端已经从西雅图的 Mac 切换到了洛杉矶的 VPS。
🚀 关键收获
1. 被动健康检查 > 主动探测
在简单的 TCP 设置中,你并不总是需要复杂的主动健康检查(ping 端点)。Nginx 的被动健康检查(监控实际的连接响应)极其高效。它能对连接拒绝做出即时反应。
2. “等待”的艺术 (fail_timeout)
调整 fail_timeout 是一门艺术。设置得太高,主节点恢复后你会停留在备用服务器上太久。设置得太低,不稳定的连接会导致“震荡”(快速切换)。3 秒 被证明是家庭网络环境的最佳平衡点。
3. 自托管赋予控制权
通过在这个特定用例中摆脱 Cloudflare Tunnel 等托管服务,我重新获得了传输层的控制权。我可以确切地看到连接在哪里失败——是 FRP 隧道、本地代理进程,还是 Nginx 路由。
总结
这个项目不仅仅是为了节省几美元的代理服务费。这是一次关于**弹性工程(Resiliency Engineering)**的实践练习。我们构建了一个能够优雅降级而不是灾难性崩溃的系统。
对于任何希望增强家庭实验室可访问性的人:不要只构建服务;构建一个备份。然后让 Nginx 处理剩下的事情。
使用的工具:FRP (v0.66.0), Nginx (1.26), Docker, X-UI