<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Linux | 个人主页</title><link>https://page.syscall.cn/tags/linux/</link><atom:link href="https://page.syscall.cn/tags/linux/index.xml" rel="self" type="application/rss+xml"/><description>Linux</description><generator>Hugo Blox Builder (https://hugoblox.com)</generator><language>zh-Hans</language><lastBuildDate>Mon, 14 Sep 2026 00:00:00 +0000</lastBuildDate><image><url>https://page.syscall.cn/media/icon_hu7729264130191091259.png</url><title>Linux</title><link>https://page.syscall.cn/tags/linux/</link></image><item><title>🏠 家庭实验室的四层架构：边缘 / 应用 / 云端 / 接入</title><link>https://page.syscall.cn/post/homelab-architecture/</link><pubDate>Mon, 14 Sep 2026 00:00:00 +0000</pubDate><guid>https://page.syscall.cn/post/homelab-architecture/</guid><description>&lt;blockquote>
&lt;p>这篇是我这套家庭自托管集群的总览。它不是&amp;quot;买台服务器装个 Docker&amp;quot;的入门贴，而是&lt;strong>为什么这样分层&lt;/strong>的复盘 —— 每一层都在解决上一层解决不了的问题。&lt;/p>
&lt;/blockquote>
&lt;h2 id="一动机东西越堆越多然后失控">一、动机：东西越堆越多，然后失控&lt;/h2>
&lt;p>一开始只是想把照片从手机里挪出来。于是有了一台小主机跑 Immich。后来想有个自己的网盘，又加了 OpenList。再后来要远程访问、要监控、要备份、要证书、要域名……&lt;/p>
&lt;p>半年之后我发现：&lt;strong>我不知道哪个服务跑在哪台机器上，也不知道某个端口是谁开的。&lt;/strong>&lt;/p>
&lt;p>真正的问题不是&amp;quot;服务太少&amp;quot;，而是&lt;strong>缺少架构&lt;/strong>。于是我按职责把设备重新划成四层。&lt;/p>
&lt;h2 id="二四层架构">二、四层架构&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl"> ┌──────────────────────────────┐
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 公网 ────────────▶│ 接入层 光猫 / SD-WAN 组网 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └──────────────┬───────────────┘
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │ 端口映射（增量式）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ┌──────────────▼───────────────┐
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │ 边缘层 1.50 Edge Box │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │ nginx / 门户 / 防护 bouncer │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │ 代理客户端 / 流量统计 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └──────────────┬───────────────┘
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │ 内网 TLS 反代
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ┌──────────────▼───────────────┐
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │ 应用层 1.40 App Node │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │ Docker: 照片库 / 网盘 / DNS │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └──────────────────────────────┘
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ┌──────────────────────────────┐
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │ 云端层 VPS（公网入口 / 中继） │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │ 反向代理 / 穿透服务端 / 数据库 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └──────────────────────────────┘
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="21-边缘层所有入站流量的唯一入口">2.1 边缘层：所有入站流量的唯一入口&lt;/h3>
&lt;p>边缘盒是一台 ARM 小主机（4 核 / 1GB 级），它承担：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>职责&lt;/th>
&lt;th>实现&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Web 入口&lt;/td>
&lt;td>nginx，静态托管门户站与内部工具页&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>TLS 终止&lt;/td>
&lt;td>TrustAsia DV 证书，统一挂 &lt;code>home.***.cn&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>入侵防护&lt;/td>
&lt;td>CrowdSec bouncer（决策来自应用层的 LAPI）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>出站代理&lt;/td>
&lt;td>本地 SOCKS5 + HTTP 代理，供内网其他主机借用&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>流量审计&lt;/td>
&lt;td>vnStat 按日/月统计双网卡&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>状态探针&lt;/td>
&lt;td>fcgiwrap + CGI，输出网卡地址等实时信息&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>为什么把入口单独拎出来？&lt;/strong> 因为它必须是&lt;strong>唯一&lt;/strong>的。一旦有两个入口，你就有了两套证书、两套防火墙规则、两套日志，然后开始不一致。&lt;/p>
&lt;h3 id="22-应用层只跑应用不碰边界">2.2 应用层：只跑应用，不碰边界&lt;/h3>
&lt;p>应用节点的职责非常窄：跑容器。上面是 Docker 全家桶 —— 照片库（含 PostgreSQL + Redis）、网盘，以及 DDNS 客户端和 CrowdSec LAPI。&lt;/p>
&lt;p>它的网络暴露策略是&lt;strong>最小化&lt;/strong>：容器端口只监听内网，对外的 TLS 反代由边缘层做。这样即使某个应用出了 RCE，攻击者也只拿到一个没有公网身份的内网容器。&lt;/p>
&lt;h3 id="23-云端层只有公网身份不存隐私数据">2.3 云端层：只有公网身份，不存隐私数据&lt;/h3>
&lt;p>云端那台 VPS 承担两类工作：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>公网入口&lt;/strong>：反向代理 + 内网穿透服务端，把外部请求送回家里。&lt;/li>
&lt;li>&lt;strong>纯服务&lt;/strong>：递归 DNS、关系库、缓存、远程桌面中继 —— 这些&amp;quot;要么需要固定公网 IP，要么需要独立可用性&amp;quot;的东西。&lt;/li>
&lt;/ol>
&lt;p>一条硬规则：&lt;strong>隐私数据（尤其照片）绝不上云。&lt;/strong> 云主机只放可重建的数据。&lt;/p>
&lt;h3 id="24-接入层最不该被自动化碰的地方">2.4 接入层：最不该被自动化碰的地方&lt;/h3>
&lt;p>光猫和 SD-WAN 盒子属于运营商/厂商资产，配置有&lt;strong>整体替换&lt;/strong>的坑（改一条规则可能把整个映射表覆盖掉）。&lt;/p>
&lt;p>所以这一层我只做两件事：&lt;strong>只增不改&lt;/strong>（新增映射时严格走 add-only，改前导出、改后校验、失败回滚），以及&lt;strong>记录下来&lt;/strong>（哪条映射对应哪个服务，写进文档而不是记在脑中）。&lt;/p>
&lt;h2 id="三三条关键设计决策">三、三条关键设计决策&lt;/h2>
&lt;h3 id="31-域名必须统一">3.1 域名必须统一&lt;/h3>
&lt;p>站点早期用过 &lt;code>aaa.site&lt;/code> 和 &lt;code>bbb.site&lt;/code> 两套域名，结果：&lt;/p>
&lt;ul>
&lt;li>证书要签两遍&lt;/li>
&lt;li>反代 &lt;code>server_name&lt;/code> 要写两组&lt;/li>
&lt;li>文档要维护两份映射表&lt;/li>
&lt;li>&lt;strong>其中一个域名忘了续费，被抢注后全站 canonical 指向了诈骗页&lt;/strong>&lt;/li>
&lt;/ul>
&lt;p>统一到一套域名后，证书、反代、DNS 记录全部收敛成一条线。&lt;/p>
&lt;h3 id="32-公网暴露面用高位端口--域名代替ip--默认端口">3.2 公网暴露面用&amp;quot;高位端口 + 域名&amp;quot;代替&amp;quot;IP + 默认端口&amp;quot;&lt;/h3>
&lt;p>有备案要求的网络环境下，80/443 上的未备案域名会被边缘劫持到一个阻断页。应对方式不是放弃，而是&lt;strong>把 HTTPS 挪到高位端口&lt;/strong>，靠域名 + TLS 提供服务。代价是 URL 难看，收益是链路可控。&lt;/p>
&lt;h3 id="33-服务与数据分离">3.3 服务与数据分离&lt;/h3>
&lt;p>备份策略按层设计：&lt;/p>
&lt;ul>
&lt;li>边缘层：配置 + 证书，改动前必 &lt;code>cp -p&lt;/code> 备份，带回滚路径&lt;/li>
&lt;li>应用层：数据库卷 + 对象存储目录，定期快照&lt;/li>
&lt;li>云端层：可重建，所以只需要配置即代码&lt;/li>
&lt;/ul>
&lt;h2 id="四可观测性先能看见才能优化">四、可观测性：先能看见，才能优化&lt;/h2>
&lt;p>早期我靠&amp;quot;感觉&amp;quot;判断负载高低，后来改成本地采集 + 静态 JSON 热缓存：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">采集进程（应用身份） → 写 /run 下的热缓存 → nginx 读文件返回
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>关键在于&lt;strong>读写分离&lt;/strong>：HTTP 侧只读缓存，绝不触发采集。这样即使页面被脚本高频轮询，也不会把负载打上去 —— 再叠一层 &lt;code>limit_req&lt;/code> 限流，正常用户完全无感。&lt;/p>
&lt;h2 id="五踩过的坑">五、踩过的坑&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>坑&lt;/th>
&lt;th>现象&lt;/th>
&lt;th>结论&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>限流区定义位置&lt;/td>
&lt;td>nginx 起不来&lt;/td>
&lt;td>&lt;code>limit_req_zone&lt;/code> 只能在 &lt;code>http{}&lt;/code> 内&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>include fastcgi_params&lt;/code>&lt;/td>
&lt;td>所有 CGI 请求 404&lt;/td>
&lt;td>它会用 &lt;code>$document_root$fastcgi_script_name&lt;/code> 覆盖 &lt;code>SCRIPT_FILENAME&lt;/code>，必须手工列参数&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>光猫映射批量改&lt;/td>
&lt;td>映射表被整体替换&lt;/td>
&lt;td>必须 add-only 增量 + 校验 + 回滚&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>域名续费&lt;/td>
&lt;td>canonical 全指向诈骗站&lt;/td>
&lt;td>定期检查 whois 到期时间&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>容器端口直接对公网&lt;/td>
&lt;td>暴露面失控&lt;/td>
&lt;td>应用层端口只在内网可达&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="六小结">六、小结&lt;/h2>
&lt;p>这套架构的核心不是设备数量，而是&lt;strong>分层带来的可推理性质&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>想改证书 → 只动边缘层&lt;/li>
&lt;li>想加服务 → 只动应用层&lt;/li>
&lt;li>想改公网路由 → 只动云端层&lt;/li>
&lt;li>想调接入 → 只动光猫，且只增不改&lt;/li>
&lt;/ul>
&lt;p>当你需要同时在心里维护超过三台机器的状态时，架构就不是奢侈品，而是必需品。&lt;/p></description></item><item><title>家庭主页</title><link>https://page.syscall.cn/project/homepage/</link><pubDate>Mon, 28 Oct 2024 00:00:00 +0000</pubDate><guid>https://page.syscall.cn/project/homepage/</guid><description>&lt;p>家庭服务器入口主页，汇总家庭网络中的自托管服务、访问地址与运行状态。&lt;/p>
&lt;p>当前地址：&lt;a href="https://home.syscall.cn:8888/" target="_blank" rel="noopener">https://home.syscall.cn:8888/&lt;/a>&lt;/p></description></item></channel></rss>