去年年底,我下线了许多以前 self-host 的应用,认为维护成本过高,并写了篇博客纪念。
半年过去,我发现我错了:AI 让各种服务的部署和运维变得如此容易。我想要的一切都能够梦幻般地随着几句话而立即变为现实。我再也不用淹没在文档或日志中,再也不用苦恼于某个未知环节的未知问题。这种感觉太爽了。这是 HomeLab 玩家最好的时代。
最近,我全面复活了我的 HomeLab,并搞了许多好玩的东西。我将会写几篇博客与你分享。预计分为:基础设施篇、应用篇和数据与监控篇。所有代码和配置都放在 Skywt2003/homelab,你可以让你的 agent 参考。
我将会侧重于介绍「我达成了什么效果」和「为什么这么做」。至于「如何达成」,你可以直接让 AI 去做。这可以称为 Vibe Deploying。😎
Tailscale & Headscale
我们将会开很多 VM。我个人也有很多设备:MacBook、iPad、iPhone。我希望它们随时随地都能互相连接,就像在同一个局域网一样。因此我选择了心智负担很小的解决方案:让他们 24 小时在同一 Tailscale 网络中。
之所以没有在我家的局域网环境进行过多配置,是因为我租的房属于上下双钥匙的公寓,因此我的路由器只作为 AP,没有设为 DHCP 服务器(否则会有双层 NAT);而上下两户公用的网关,是中国电信的设备,无法进行太多自定义。连 DNS 都无法配置。
Tailscale 的官方节点在境内使用并不方便,最好的方案是在国内主机自建 Headscale。Headscale 还内置 DERP 服务器,在无法成功打洞时中转流量,自建比用境外 DERP 体验好很多。通过 Headscale 的 OIDC 配置,新设备的接入流程能直接复用 skywt.net 的账号系统。在新的设备上连接时,我只需要指定 vpn.skywt.cn,会跳转 skywt.net 登录,登录即可加入网络(当然,开了白名单,只有我的账号能加入 😁)。我再也不需要 SSH 到服务器生成 auth key 来注册新机器了。Cool!
硬件配置、PVE 和它的 VM 们
我的这台设备是 2023 年购入的零刻 SER6 Pro Vest 小主机,内存 32G,硬盘 512G SSD。
此外,我还有一块很旧的东芝 2T HDD 移动硬盘,通过 USB 接入,作为 NAS 的主要存储盘。(这个移动硬盘的接口是非常奇葩的 Micro-B SuperSpeed,这个数据线如今都买不到了 🤣)
为了方便管理资源,我在主机上装了 PVE,并划分了如下四个 VM:
- Lab:Debian,纯容器化的 VM,用 Docker 跑着各种服务。这是我的 HomeLab 最主要的部分。所有服务都通过 Docker Compose 管理。这台机器安装了 Codex app-server,也配置了能连到所有其他主机的 SSH key,同时担当主操作入口。
- Dev:Debian,各种项目开发空间,用 PM2 让各个项目的 dev server 保持常驻,不用 Docker。这台主机也安装了 Codex app-server,方便开发和项目管理。
- Nas:OpenMediaVault,2T 移动硬盘直通给这台 VM,通过 NFS 和 Samba 共享给其他主机。
- Proxy:Debian,统一的网络代理基础设施,在下方会介绍。
内网域名、DNS 和 Caddy
我将会在 Lab 上部署很多服务,我希望每个服务都有域名方便访问。这个域名应该只在我们的内网生效,因此应该定一个和公网不冲突的 TLD。按照规范,其实应该用 .home.arpa。但它又长又没个性。我决定用 .skywt。这样我就可以这样访问:
- Lab 上部署的 Immich(相册管理应用):
https://photos.lab.skywt。 - Dev 上项目 A 的 dev server:
https://project-a.dev.skywt。 - Nas 上的 OMV WebUI:
https://nas.skywt。
当然,这些域名都不存在于公网,因此我们要配置自己的 DNS。我在 Lab 上开了个 AdGuard Home,并在其中配置我们要的记录:*.lab.skywt 解析到 Lab,*.dev.skywt 解析到 Dev 等。让 Headscale 下发 AdGuard Home 作为 DNS。这样,只要进入这个 Tailscale 网络,就能自动启用这些配置。
AdGuard Home 该管理所有 DNS 查询,还是只管理 .skywt 呢?有两种方案:
- 配置 split DNS 只管理
.skywt查询。让 AdGuard Home 只解析.skywt内网域名,其他域名还是走公共 DNS。缺点是没法作为统一 DNS 入口做统计分析、隐私保护、对抗 DNS 污染、广告屏蔽等功能。 - 唯一下发,管理所有 DNS 查询。让它作为 Headscale 唯一下发的 DNS。风险是如果 Lab 或者 PVE 故障,DNS 就会不可用;此外如果 Tailscale 没有成功打洞,通过 relay 连接延迟可能较高。(注意:如果选择这种方案,Headscale 不能除了 AdGuard Home 以外下发其他公共 DNS,否则会造成竞态导致
.skywt时而能解析时而不能)
综合考虑,我选了第二种方案。保留对一切的控制对我来说更重要。
下一步是在 Lab 上开一个反向代理网关,让 photos.lab.skywt 能访问到 Immich 服务。主流的选择是 Nginx、Caddy、Traefik 等。我一直比较喜欢 Caddy,因为它的配置简洁清晰,搭配 caddy-docker-proxy 能在各个服务的 compose.yml 里指定对应的反代配置,而无需每次修改 Caddyfile。(不过这个似乎也没有那么必要了,毕竟现在 compose.yml 和 Caddyfile 也不是由我们来写 🤣)
现在,Lab 运行了 Immich,我们能通过 photos.lab.skywt 访问了,但此时还没有 HTTPS。域名都不在公网,Caddy 是无法申请到真实的 TLS 证书的。我希望在我们的网络中使用统一的自签证书。Caddy 正好提供了这个功能,从 Lab 上的 Caddy 导出根证书,在我们用的所有客户端安装并信任即可。我在 ca.lab.skywt 做了一个「证书安装指南」。
Lab 上的服务能通过 HTTPS 访问,但目前 Dev 上还不行。Lab 和 Dev 的 Caddy 是相互独立的,而我希望整个网络统一只有一个根证书需要信任。好在 Caddy 本身可以作为 ACME server,可以让 Lab 给 Dev 签发证书。在 Dev 中的 Caddy 设置让它向 Lab 申请证书即可。这样,我们也能通过 HTTPS 访问 project-a.dev.skywt。
统一的网络代理基础设施
由于众所周知的原因,上面的所有 VM 的网络访问环节都可能遇到问题。如果要在每台 VM 都配置代理客户端,不仅很重复,维护起来也很麻烦。
因此,我专门开了一个名为 Proxy 的 VM,作为「统一的网络代理基础设施」。这台 VM 只做一件事:运行 Mihomo,配置好路由分流规则,每五分钟 benchmark 一次机场提供的节点并择优,暴露 HTTP/SOCKS 代理端口 10810。任何网络中的主机都能通过将代理设为 http://proxy:10810 而直接使用代理。
这个 Proxy 已经可以服务我的各个 VM 和 MacBook。然而,无法直接设置 HTTP 代理的 iOS 设备还是无法使用。iOS 还有个限制:同时只能开启一个 VPN Profile,因此如果开启了 Tailscale,就无法开启其他用于代理的 VPN。如前文所述,我还是希望实现「24 小时在 Tailscale 网络中」。能否将这两者直接合二为一呢?
我的解决方案是:在 Proxy 配置透明代理,让 Tailscale 进入 Proxy 的流量通过 Mihomo 分流出口。这样,只要任何设备将 Proxy 设为 exit node,就能无感地实现分流代理。我再也不用额外安装配置任何 Surge/Shadowrocket/Clash 等第三方客户端了,也不需要没完没了地设置/传入环境变量了,只要一个 Tailscale,all in one。这太酷了!
结语:沧海桑田
似乎就在不久前,每个 compose.yml 还都要手写,每个 Caddyfile 还都要手动更新,每个问题还都要去翻日志排查,去 Stack Overflow 找答案。人类的知识、注意力和耐心如此有限,总有些想折腾的东西没精力去做。
这么快,那样的日子就已经一去不复返。在今天,完成上面的一切,只是偶尔在 ChatGPT 聊聊天的结果,无需手写一行代码。我想要的一切都能够梦幻般地随着几句话而立即变为现实。
让人感慨的同时,也很令人兴奋。