云边协同:基于 Docker 与 FRP 的家庭实验室全栈内网穿透指南

摘要 :家里有一台高性能工作站(如 ThinkStation P520),如何利用一台配置普通的公网云服务器,安全、优雅地从外网访问家里的服务?本文将介绍一套基于 Docker 的全栈解决方案,实现零侵入云端现有环境本地 Nginx 统一网关 以及基于 URL 路径的服务分发

1. 场景与需求

很多 HomeLab 玩家都有这样的困扰:

  1. 家庭宽带无公网 IP:高性能设备(P520, NAS)只能在局域网"自嗨"。
  2. 云服务器配置低:为了公网 IP 买的 VPS 通常只有 1核2G 或 2核4G,跑不动重型应用。
  3. 端口管理混乱 :每次加一个服务就要在防火墙开一个端口,记不住 IP:5000, IP:8081 这种地址。

本文的目标方案:

  • 云端 (Server):只做流量中转,不占用 80/443 端口(避免影响云端现有的建站业务)。
  • 边缘端 (Client) :家庭服务器通过 Docker 运行 Nginx 网关,所有服务通过 http://云IP:8080/app1/http://云IP:8080/app2/ 的方式访问。

2. 架构设计

我们采用 FRP (Fast Reverse Proxy) 建立隧道,配合 Nginx 进行七层反向代理。

复制代码
graph TD
    User[用户] -->|访问 http://云IP:8080/test/| Cloud_Firewall
    
    subgraph Cloud_Server [云服务器 (VPS)]
        Cloud_Firewall[防火墙 TCP:8080] --> Cloud_FRPS
        Existing_Nginx[现有 Nginx (Port 80)] -.->|互不干扰| Cloud_FRPS
        Cloud_FRPS[Docker: FRP 服务端]
    end

    Cloud_FRPS <==>|FRP 加密隧道 (TCP:7000)| Home_FRPC

    subgraph Home_Server [家庭服务器 (P520)]
        Home_FRPC[Docker: FRP 客户端] -->|流量转发| Home_Nginx
        Home_Nginx[Docker: Nginx 网关 (Port 80)]
        
        Home_Nginx -->|Location /test/| Web_App_1[容器: 测试页]
        Home_Nginx -->|Location /alist/| Web_App_2[容器: 网盘]
    end

3. 云服务器端部署 (FRP Server)

策略 :为了不影响云服务器上已经运行的 Nginx(占用 80 端口),我们将 FRP 的 Web 访问端口设为 8080

3.1 配置文件 frps.toml

复制代码
# frps.toml
bindPort = 7000           # FRP 客户端连接端口
vhostHTTPPort = 8080      # 【关键】外部访问 Web 的入口端口
auth.token = "YourStrongPassword2025" # 通信密钥

# 仪表盘配置 (可选)
webServer.addr = "0.0.0.0"
webServer.port = 7500
webServer.user = "admin"
webServer.password = "admin"

3.2 启动容器 docker-compose.yml

复制代码
version: '3.8'
services:
  frps:
    image: snowdreamtech/frps:latest
    container_name: frps
    restart: always
    network_mode: host  # 使用 host 模式,直接监听宿主机端口
    volumes:
      - ./frps.toml:/etc/frp/frps.toml

注意 :启动后,请务必在云服务器控制台(安全组)放行 TCP 70008080 端口。


4. 家庭服务器端部署 (FRP Client + Nginx)

策略:在 P520 上,我们使用 Docker Compose 编排三个服务:FRP 客户端、Nginx 网关、以及一个测试用的 Web 服务。

4.1 目录结构

建议在 ~/homelab 下统一管理:

复制代码
homelab/
├── docker-compose.yml
├── frpc.toml
└── nginx/
    ├── conf.d/
    │   └── default.conf
    └── html/
        └── index.html

4.2 穿透配置 frpc.toml

复制代码
serverAddr = "x.x.x.x"  # 你的云服务器公网 IP
serverPort = 7000
auth.token = "YourStrongPassword2025" # 必须与服务端一致

[[proxies]]
name = "home-gateway"
type = "http"
localIP = "127.0.0.1"
localPort = 80             # 将流量转发给本地的 Nginx
customDomains = ["x.x.x.x"] # 填云服务器 IP,如果有域名则填域名

4.3 Nginx 网关配置 nginx/conf.d/default.conf

这是实现 "路径分发" 的核心配置。

复制代码
server {
    listen       80;
    server_name  localhost;
    charset utf-8;  # 防止中文乱码

    # 1. 默认首页
    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

    # 2. 业务示例:测试服务
    # 外部访问: http://云IP:8080/test/
    # 内部转发: http://127.0.0.1:8081/
    location /test/ {
        # 【重点】proxy_pass 结尾加 / 表示截断路径
        # 即把 /test/ 后面的内容传给后端,去掉 /test 本身
        proxy_pass http://127.0.0.1:8081/; 
        
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

4.4 整体编排 docker-compose.yml

复制代码
version: '3.8'

services:
  # 服务 1: 穿透客户端
  frpc:
    image: snowdreamtech/frpc:latest
    container_name: frpc
    restart: always
    depends_on:
      - nginx-gateway
    volumes:
      - ./frpc.toml:/etc/frp/frpc.toml
    network_mode: "host" # 使用 Host 模式,方便访问本机 Localhost

  # 服务 2: 本地 Nginx 网关
  nginx-gateway:
    image: nginx:latest
    container_name: nginx-gateway
    restart: always
    ports:
      - "80:80" # 占据 P520 的 80 端口
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d
      - ./nginx/html:/usr/share/nginx/html

  # 服务 3: 模拟业务应用 (用于测试)
  web-demo:
    image: nginx:alpine
    container_name: web-demo
    restart: always
    ports:
      - "8081:80" # 实际运行在 8081,通过网关转发
    # 简单映射个页面进去方便看效果
    volumes:
      - ./nginx/html:/usr/share/nginx/html 

5. 部署与验证

5.1 启动服务

在 P520 的 homelab 目录下执行:

复制代码
docker compose up -d

5.2 验证效果

  1. 内网验证 : 打开浏览器访问 P520 的内网 IP:http://192.168.x.x/test/,应该能看到测试页面。
  2. 公网验证 : 打开手机 4G,访问:http://云服务器IP:8080/test/

如果一切顺利,你应该能看到同样的页面。这意味着:请求 -> 云服务器 -> FRP 隧道 -> 家里 P520 -> Nginx -> 具体容器 的链路已经打通。


6. 技术避坑指南 (必读)

虽然"路径转发"(如 /app/)看起来很美,但在实际部署复杂应用时,你可能会遇到 静态资源 404 的问题。

  • 原因 : 比如你访问 /alist/,页面加载了一个 CSS 文件 /style.css。浏览器会去请求 http://云IP:8080/style.css,Nginx 会发现根目录下没有这个文件,因为这个文件实际在 /alist/ 下。
  • 解决方案
    1. 首选 :在应用本身的设置里,寻找 Base URLWeb Root 选项,将其设置为 /alist/
    2. 备选 :如果应用不支持 Base URL,建议放弃路径转发,改用 子域名 方案(如 alist.yourdomain.com),这需要你有自己的域名。

7. 总结

通过这套方案,我们成功地将家庭服务器纳入了云端网络架构中。未来如果想增加新服务(比如 HomeAssistant),只需:

  1. 在 Docker Compose 里添加服务容器。
  2. nginx/conf.d/default.conf 里增加一段 location /ha/ { ... }
  3. 重启 Nginx,无需修改云服务器和防火墙设置。

Happy Hacking!

相关推荐
木二_4 小时前
附057.RustFS云原生Kubernetes部署指南
云原生·kubernetes·云原生存储·对象存储·分布式存储·rustfs
HarrySunCn6 小时前
Linux系统Docker安装
云原生·eureka
福大大架构师每日一题6 小时前
go-zero v1.9.4 版本发布详解:云原生适配升级与稳定性性能全面提升
开发语言·云原生·golang
汪碧康19 小时前
【k8s-1.34.2安装部署】五.worker端containerd2.2.1、kubelet-1.34.2安装
docker·云原生·容器·kubernetes·jenkins·kubelet·xkube
忍冬行者1 天前
kubernetes安装traefik Gateway API,应对Ingress NGINX停止维护
云原生·kubernetes·云计算
-指短琴长-1 天前
Docker-Desktop修改WSL文件系统到D盘
docker·容器·eureka
没有bug.的程序员1 天前
微服务网关:从“必选项”到“思考题”的深度剖析
java·开发语言·网络·jvm·微服务·云原生·架构
没有bug.的程序员1 天前
Sentinel 流控原理深度解析:构建高可用微服务的底层架构
java·算法·微服务·云原生·架构·sentinel·负载均衡
前端程序猿之路1 天前
AI大模型应用开发之容器化部署
人工智能·python·语言模型·云原生·eureka·ai编程·改行学it