# 一套自托管的家庭实验室：支撑整个工作室，每天维护

Company: Webanion
Service: DevOps 工程
Period: 2026-02 - present
Tech: Kubernetes, Traefik, Cloudflare, Go, React.js, TypeScript, SQLite, VictoriaMetrics, Grafana, PostgreSQL, MongoDB, zot, Verdaccio, systemd, Linux, GitHub Actions
Canonical: https://webanion.com/zh/portfolio/self-hosted-homelab-that-runs-the-studio-infrastructure

工作室自己的基础设施，运行在自托管的 Kubernetes 集群上，于 2026 年 2 月规划，此后每天维护：网站、CMS 和 MCP 服务器，以及它负责维护的客户平台，都通过 Cloudflare 隧道提供服务；每个数据库每晚导出并校验；指标和告警规则推送到手机；家门之外有一个宕机监控；还有一个容器镜像仓库和一个 npm 仓库；一个私有控制台把合并、运行、镜像、Pod 和节点汇集在同一个页面上。

## 完整故事

不再为每家供应商各租一个控制面板，而是自己掌握整套技术栈，这需要付出什么；每天照看它，又能为工作室带来什么。

### 工作室交付的一切，都运行在自己的服务器上

<p>一家为客户打造产品的工作室，自己要运行的东西也多得出人意料。Webanion 以它的全部语言提供自己的网站，还有其背后的 CMS，公开和私有的 MCP 服务器，供 AI 助手读取和管理这些内容，以及它为客户开发并负责维护的平台，其中包括一个金融平台、一个预订平台、一个账本应用和一个研究团队的网站。每一项都需要一个运行的地方、一个有备份的数据库、一张按时续期的证书，以及一个在出问题时能察觉的人。如果每一部分都向各自的供应商租用，就意味着每家供应商各有一份账单、一个控制面板和一个盲区，却没有一个地方能回答决定一天工作的三个问题：什么在运行，它是否健康，今天需要处理什么。</p><p>这里给出的答案，是在工作室自有服务器上运行的一个 Kubernetes 集群，于 2026 年 2 月规划，此后每天维护。每个网站和平台都通过 Cloudflare 隧道从这里提供服务，每个数据库每晚导出一次，指标和告警规则从内部盯着集群，一个宕机监控则从家门之外盯着它，一个容器镜像仓库和一个 npm 仓库保存着工作室自己的镜像和软件包，一个私有控制台统揽全局。在截图当天，也就是 2026 年 9 月 30 日，控制台统计到 2 个节点、54 个工作负载和 93 个 Pod。</p>

### 一个页面，看清什么在运行、什么需要处理

<p>控制台是早上打开的第一个页面，也是合并期间一直开着的那个页面。它的总览同时读取集群、GitHub、镜像仓库和各台主机：就绪的节点，健康的工作负载和 Pod，待处理和已合并的拉取请求，近期运行中通过的比例，GitHub 请求额度还剩多少，每台主机的负载、内存、磁盘、温度和构建缓存，最近执行的操作，以及一份今天需要动手处理的简短清单，最严重的排在最前。</p><p>它真正的价值在于串联：这次合并、它触发的运行、这次运行推送的镜像、运行该镜像的 Pod，以及这个 Pod 所在的节点，都在一个页面上，而不是分散在五个标签页里。它位于 Cloudflare Access 之后，在手机宽度下同样可用，因为需要查看情况的时刻，往往不是笔记本电脑打开的时候。截图当天，它显示 2 个节点中 2 个就绪，54 个工作负载中 54 个正常，93 个 Pod 中 93 个健康，前两天共合并 13 次。</p>

### 每一次合并，都从同一块看板发出

<p>来自工作室各个 GitHub 组织的拉取请求都在同一块看板上，分为待处理、运行中和已完成，每张卡片都写明合并后会发生什么：部署到线上、部署到预发布环境，或者什么都不做。看板下方有一张表格，逐个仓库列出能否从这里合并、不能时的原因、它的并发组，以及它的工作流能否手动触发。</p><p>从看板发起的合并靠防护，而不是靠信任。在按下按钮的那一刻，服务器会重新读取拉取请求，绝不在检查未通过或仍是草稿时合并，始终使用合并提交，并且在任何会部署到线上的操作之前，要求输入仓库的完整名称。对于一周交付好几个产品的工作室来说，这就是事先知道一次合并会做什么与事后才发现之间的区别。截图当天，看板上有 2 个待处理的拉取请求和 13 个已完成的，每张已完成的卡片都附有它部署的镜像。</p>

### 每晚都会报到的备份

<p>集群上的每个数据库每晚都会导出，彼此间隔一刻钟，避免两个同时开始；每次导出都会在旁边写下每张表的行数，恢复验证把一次备份恢复到一个一次性的命名空间时，核对的正是这些数字。控制台自己的归档是唯一无法由其他东西重建的存储，它通过 SQLite 的在线备份复制，并在保存之前检查完整性和行数。每个任务在一次经过验证的运行之后，都会向家门之外的一个“死人开关”发出信号，因此一个失败、卡住或不再被调度的任务，会在第二天早上被发现，而不是等到需要恢复的那一天。</p><p>Backups 视图根据每小时的测量，绘制出每个存储卷的增长。截图当天，它显示 10 个存储卷共有 664.2 MB 的备份数据，10 个备份任务，最新一次在 9 小时前，没有失败的任务；Schedules 标签页列出了全部 14 个定时任务，最近一次运行都已成功。</p>

### 主机、指标，以及家门之外的警报

<p>每台主机每五分钟上报一次负载、内存、磁盘、温度和构建缓存，指标存储则在页面内通过 Grafana 查看。集群的告警规则会推送到手机，每小时一次的检查会标出已合并却无人应用的清单文件，Cloudflare 边缘上的宕机监控每分钟检查一次每个公开网站，因为运行在集群上的监控，无法报告集群本身宕机。</p>

### 所有设施，一张图看全

<p>家庭实验室提供的一切，按角色而不是按机器画出：边缘的隧道和路由器，集群，数据库及其存储卷，容器镜像仓库和 npm 仓库，运行器池，每晚的备份及其开关，指标存储及其告警，家门之外的宕机监控，以及读取这一切的控制台。</p>

### 每一次操作都有记录，被拒绝的也不例外

<p>控制台不只是读取。合并、重新运行、手动触发、重启、扩缩第二个副本以及回滚，都可以在页面上直接完成，每一次都通过第二个身份写入，这个身份的权限逐一列出它可以触碰的每个对象，并且只为这一次操作申请一个令牌，用完即弃。在任何操作发生之前，控制台都会重新读取实时状态，把每个前提条件列为通过、警告或拒绝，任何会部署到线上的操作都需要输入其名称来确认。</p><p>每一次操作都会变成一行只追加、不修改的记录：时间、操作、目标、结果和返回的说明，被拒绝的也包括在内，因此一次被拒绝操作的记录，和一次成功操作的记录一样完整。同一个归档还保存着 GitHub 会删除的内容：每次运行在完成时即被归档，日志正文保留一年，记录行永久保留，超出 GitHub 默认九十天的保留期。</p>

### 每天维护，以及它的价值

<p>日常工作很短，因为大部分由工具承担：早上控制台里那份需要动手处理的清单，每小时一次、能发现已合并却无人应用的清单文件的漂移检查，夜里有任何故障都会通知的手机，只要每个任务按时报到就保持安静的备份开关，以及在证书到期前几周就发出提醒的证书检查。截图当天，那份清单上只有一项：一个超出上限的构建缓存，每晚的清理会把它降回来。这类小偏差，当天修复代价很小，一个月后才发现代价就很大。</p><p>坦白说，代价是必须有人负责。自己掌握整套技术栈，意味着证书、磁盘和升级是工作室的问题，而不是供应商的问题；一个没有人照看基础设施的团队，还是租用更好。对于希望自己的基础设施是能看懂的东西、而不是一叠发票的创始人或小团队，以及希望看到证据、证明自己付费的平台每天都有人照看的客户，这就是它在实践中的样子。<a href="/portfolio/webanion-headless-cms-portfolio-platform-with-mcp-server">Webanion 平台</a>就运行在它上面，而它促成的事情也有各自的故事，例如<a href="/portfolio/scaling-a-homelab-from-one-node-to-a-multi-node-cluster">在不改动任何应用的情况下，把一个节点扩展成集群</a>，以及<a href="/portfolio/replacing-hosted-ci-with-a-home-runner-pool-and-private-registries">把构建从托管 CI 迁移到家中的运行器池和私有镜像仓库</a>。</p>

### 警报送达手机，警报本身也有人看守

<p>不需要有人一直盯着控制面板，问题也能被发现：警报通过 Telegram、Discord 和 ntfy 送达手机，Telegram 上的来自宕机监控和集群自身的告警规则，Discord 和 ntfy 上的来自监控的看守者。监控本身又从家门之外被看守着，所以 9 月 30 日它沉默了四分钟时，这段沉默本身就作为警报到来，先是宕机，然后恢复，同时出现在 Discord 和 ntfy 上。</p>
