Homelab 网络结构总结
基于 PVE、RouterOS、Tailscale、Traefik 与 Caddy 构建兼顾安全、隐私与自动化的 Homelab 网络架构
最开始我是把所有服务都部署在云服务器上的,除了费钱,其实省心省力没啥毛病。后来跑的服务越来越多、越来越重,手头刚好又有了闲置的机器,索性就把服务全部迁回了本地 Homelab,云服务器退居二线,只负责对外访问的网络中转。毕竟在云端兜里掏空撑死了也就买得起 4C6G 这种入门配置,而搬回家里不仅能轻松拉到 8C16G 甚至更高,后续升级硬盘和内存也是丰俭由人。
硬件设备
盘点一下硬件阵容,可以说是相当“勤俭持家”了:2022 年买的 J4125 工控机服役至今,笔记本也是公司淘汰下来的二手旧设备,核心诉求就是省电与省钱。
| 设备 | 数量 | CPU | 内存 | 网口 |
|---|---|---|---|---|
| J4125 工控机 | 1 | J4125 | 8G | 2.5G Ethernet × 4 |
| 笔记本电脑 | 1 | i5-1135G7 | 8G × 2 | 1G Ethernet × 1 |
外加一台腾讯云轻量锐驰型服务器(大峰值带宽、不限流量套餐),专门充当所有对外访问的前置代理。
软件环境
两台物理机均使用 Proxmox Virtual Environment (PVE) 搭建虚拟化平台。部署原则也很明确:除了必须独立内核或系统形态限制只能以虚拟机(VM)安装的服务,其余一律优先使用资源开销极低的 LXC 容器部署。
两台机器的分工如下:
- J4125 工控机:主攻网络基础设施,负责软路由、透明代理以及轻量级的常驻网络服务。
- Notebook 笔记本:承担数据库、缓存、NAS 以及主力 Docker 应用容器等较重的负载。
| 主机 | 服务 | 类型 | IP |
|---|---|---|---|
| J4125 PVE | 10.1.1.254 | ||
| RouterOS | VM | 10.1.1.1 | |
| sing-box | LXC | 10.1.1.2 | |
| Docker | LXC | 10.1.1.100 | |
| Tailscale | LXC | 10.1.1.101 | |
| Notebook PVE | 10.1.1.253 | ||
| PostgreSQL | LXC | 10.1.1.201 | |
| Valkey | LXC | 10.1.1.202 | |
| Home Assistant OS | VM | 10.1.1.252 | |
| 飞牛 (fnOS) | VM | 10.1.1.250 | |
| Docker | LXC | 10.1.1.200 |
网络拓扑
整体拓扑结构十分清晰直观:J4125 板载的 4 个 2.5G 物理网口在 PVE 底层分别对应一个 Linux 虚拟网桥(vmbr0 ~ vmbr3),全部桥接给 RouterOS 虚拟机进行统一纳管。其中 vmbr0 作为 WAN 口承接光猫拨号,其余 3 个作为 LAN 口组网;笔记本则通过千兆网口直连工控机的其中一个物理 LAN 口。
外网访问
我家的宽带是有公网 IP 的,理论上最直接的做法就是在 RouterOS 上配置端口映射配合 DDNS,把需要访问的服务端口逐个丢出去。但实际用起来,有几个让我非常难受的地方。
首先,我并不想直接对外暴露家宽的真实公网 IP,因为这能被轻易推算出家里的大致地理位置,也容易沦为全网端口扫描的目标。其次,随着服务数量增多,每个服务都得分配并记住一堆杂乱的高位端口。理论上当然也可以在本地起一个 Nginx 或 Caddy 来做反向代理,但在 80/443 被封锁的前提下,对外依然摆脱不了带端口访问的尴尬,真实 IP 也依然处于裸奔状态。最后,三大运营商之间的跨网互联偶尔也会抽风。
第一阶段:云端反代 + DDNS 白名单回源
为了解决这些痛点,我最初选择在腾讯云服务器上部署 Caddy 来做反向代理,由它来监听公网标准的 80/443 端口并自动申请管理 HTTPS 证书。
在回源链路上,第一版依然使用 DDNS 和本地端口映射,但在 RouterOS 的防火墙中严格限制了入站 IP 白名单,只允许腾讯云服务器的固定公网 IP 访问。这样既隐藏了家里的真实 IP、统一了访问域名与端口,又杜绝了外部随意扫描本地端口的风险。大致逻辑如图:
但随着服务日常增减,这套方案的维护成本变得不可接受。每次只要新增或调整一个服务,就得经历一套繁琐的连环操作:
- 登录 RouterOS 增减 NAT 端口映射;
- 登录腾讯云服务器修改 Caddy 配置并 reload;
- 在 DNS 解析后台增减子域名的 A 记录;
- 本地启动服务容器。
“部署 5 分钟,配网半小时”,极其折腾。所以我又进行了一次改造。
第二阶段:全链路自动化改造
首先是域名解析:直接在 DNS 服务商处配置一条泛域名解析(*.example.com)指向腾讯云服务器的公网 IP,一劳永逸,以后再添新服务无需频繁操作 DNS。
其次是内网回源通道:利用 Tailscale 打通虚拟局域网。在腾讯云上安装 Tailscale 并加入内网网段后,云端即可直接通过内网 IP 与端口访问 Homelab 中的服务,彻底告别 RouterOS 上繁琐的端口映射。虽然 Tailscale 的 WireGuard 封装带来了一丁点性能损耗,但在家宽上行场景下完全可以忽略不计。
最后是最核心的问题:反向代理配置该如何全自动感知?
和 ChatGPT + Gemini 一通左右脑互搏之后,我决定使用 Traefik + traefik-kop 的方案:
- 多机容器感知:原生 Traefik 只能监听本地宿主机的 Docker Socket,而我的 Docker 容器分散在两台宿主机的 LXC 容器中。
- 解决方案:在两个 Docker LXC 中都部署轻量的 traefik-kop,由它分别监听各自宿主机的 Docker 容器标签(Labels),解析后统一写入共享的 Valkey(兼容 Redis 协议)配置中心。
- Traefik 统一入口:在其中一个 Docker LXC 上部署 Traefik,配置使用 Valkey 作为动态配置源(Provider),从而自动汇聚两台机器上的所有服务路由规则。
- 证书管理:由内网的 Traefik 通过 DNS-01 验证统一申请并自动续期
*.example.com的通配符证书。
这样一来,腾讯云上的 Caddy 彻底卸下了七层反代与证书管理的负担,只需要做纯粹的四层(Layer 4)流量透传,所有的七层路由与证书处理全部交由内网的 Traefik 自动化完成。
不过,由于我在腾讯云上还运行着少量自身的监控服务,且共用了同一个主域名,因此 Caddy 的四层配置需要通过 TLS SNI 做一次分流:
- 匹配到
a.example.com和b.example.com时,路由至云端本机的 8443 端口处理; - 其余泛域名流量全部通过 Tailscale 内网透传到 Homelab 的 Traefik 443 端口。
这里有一点非常关键:必须开启 proxy_protocol v2,否则四层透传后 Traefik 看到的来源 IP 全是云服务器的内网 IP,开启 PROXY Protocol 协议后 Traefik 才能正确还原出客户端真实的公网 IP。
{
layer4 {
:443 {
@local_sni tls sni a.example.com b.example.com
route @local_sni {
proxy 127.0.0.1:8443
}
@homelab_sni tls sni example.com *.example.com
route @homelab_sni {
proxy 10.1.1.100:443 {
proxy_protocol v2
}
}
}
}
}
a.example.com:8443 {
reverse_proxy 127.0.0.1:8080
}
b.example.com:8443 {
reverse_proxy 127.0.0.1:8081
}
至此,以后部署新服务时,只需在 docker-compose.yml 中给容器贴上 Traefik 标签,整套反向代理与域名解析便会全自动生效:
labels:
traefik.enable: "true"
traefik.http.routers.service-name.rule: "Host(`a.example.com`)"
traefik.http.services.service-name.loadbalancer.server.port: "8080"
traefik.http.routers.service-name.entrypoints: websecure
traefik.http.routers.service-name.tls: "true"
traefik.http.routers.service-name.service: service-name
DNS 解析与内网回流
由于 Traefik 反代与通配符 HTTPS 证书都在局域网内部,这就给我们带来了一个巨大的好处:实现内网回流(Split-Horizon DNS)。
在局域网内部通过公网域名访问服务时,完全没必要让流量白白去腾讯云公网兜上一圈。只需在路由器上配置静态 DNS 解析,将 *.example.com 直接指向内网 Traefik 所在的服务器 IP(10.1.1.100)即可。
同样地,因为腾讯云上还部署了共用该域名的服务,在 RouterOS 中需要通过静态 DNS 正则规则做一次分流:
- 属于云端服务的子域名匹配正则后设为
FWD,正常转发给上游公共 DNS 解析回云服务器; - 其余 Homelab 服务域名全部直接解析为内网 Traefik 的 IP。
/ip dns static
add regexp="^(a|b|c)[.]example[.]com\$" type=FWD
add address=10.1.1.100 regexp="^[^.]+[.]example[.]com\$" type=A
这样一来,无论身处外网还是内网,访问体验完全一致(同一套域名 + 自动 HTTPS 证书);而且在内网访问时直接跑在局域网内网带宽上,零延迟、不占公网流量,体验丝滑顺畅。
其实还有一些关于 Tailscale 组网细节和 sing-box 透明代理的配置考量,篇幅有限,有空再单独写一篇聊聊吧。