摘要 :家里有一台高性能工作站(如 ThinkStation P520),如何利用一台配置普通的公网云服务器,安全、优雅地从外网访问家里的服务?本文将介绍一套基于 Docker 的全栈解决方案,实现零侵入云端现有环境 、本地 Nginx 统一网关 以及基于 URL 路径的服务分发。
1. 场景与需求
很多 HomeLab 玩家都有这样的困扰:
- 家庭宽带无公网 IP:高性能设备(P520, NAS)只能在局域网"自嗨"。
- 云服务器配置低:为了公网 IP 买的 VPS 通常只有 1核2G 或 2核4G,跑不动重型应用。
- 端口管理混乱 :每次加一个服务就要在防火墙开一个端口,记不住
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 7000 和 8080 端口。
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 验证效果
- 内网验证 : 打开浏览器访问 P520 的内网 IP:
http://192.168.x.x/test/,应该能看到测试页面。 - 公网验证 : 打开手机 4G,访问:
http://云服务器IP:8080/test/。
如果一切顺利,你应该能看到同样的页面。这意味着:请求 -> 云服务器 -> FRP 隧道 -> 家里 P520 -> Nginx -> 具体容器 的链路已经打通。
6. 技术避坑指南 (必读)
虽然"路径转发"(如 /app/)看起来很美,但在实际部署复杂应用时,你可能会遇到 静态资源 404 的问题。
- 原因 : 比如你访问
/alist/,页面加载了一个 CSS 文件/style.css。浏览器会去请求http://云IP:8080/style.css,Nginx 会发现根目录下没有这个文件,因为这个文件实际在/alist/下。 - 解决方案 :
- 首选 :在应用本身的设置里,寻找
Base URL或Web Root选项,将其设置为/alist/。 - 备选 :如果应用不支持 Base URL,建议放弃路径转发,改用 子域名 方案(如
alist.yourdomain.com),这需要你有自己的域名。
- 首选 :在应用本身的设置里,寻找
7. 总结
通过这套方案,我们成功地将家庭服务器纳入了云端网络架构中。未来如果想增加新服务(比如 HomeAssistant),只需:
- 在 Docker Compose 里添加服务容器。
- 在
nginx/conf.d/default.conf里增加一段location /ha/ { ... }。 - 重启 Nginx,无需修改云服务器和防火墙设置。
Happy Hacking!