一、背景与目标
1. 项目背景
A. 业务场景
作者维护个人基础设施已超过十年,自托管服务包括邮件服务器、博客、IRC 服务器、图片托管、RSS 阅读器等。原有部署方式混乱,有的使用容器,有的直接使用 nginx 托管静态文件,有的则是从 Debian 包管理器安装的二进制文件,缺乏统一管理。
B. 痛点分析
- 部署方式不一致,难以维护
- 服务分散在各个位置,缺乏统一管理
- 家庭网络 ISP 使用 NAT,难以直接暴露服务到公网
2. 设计目标
A. 功能目标
- 在本地家庭网络托管个人基础设施和服务
- 支持内部服务和面向互联网的服务
- 统一容器化部署方案
B. 非功能目标
- 易于部署和配置
- 尽可能自托管,愿意牺牲部分便利性
- 可靠性强,无需频繁维护
二、硬件资源
1. 设备清单
- NAS 服务器:2×8 TB 磁盘,2012 年硬件
- Intel NUC(byggmester):Intel i5-6260U,16 GB RAM,2015 年
- AMD NUC(amd):AMD Ryzen 7 8745HS,64 GB RAM,购自 AliExpress
- 路由器:OpenWRT One
2. 网络架构
家庭网络采用扁平化设计,所有设备在同一网段。
graph LR
Internet[互联网] -->|WireGuard 隧道| VPS[VPS 服务器]
VPS --> byggmester[Intel NUC<br/>byggmester]
byggmester --> amd[AMD NUC<br/>amd]
byggmester --> NAS[NAS 服务器]
amd --> Router[OpenWRT One]
Router --> Clients[客户端设备]三、容器化平台:Incus
1. Incus 简介
Incus(原 LXD)是一个容器管理器,支持 LXC 容器、QEMU 虚拟机和 OCI 容器。作者已使用多年,具有高度可靠性,版本间更新不会突然破坏功能。
2. 集群配置
Incus 集群包含两个节点:
- amd:https://10.100.100.3:8443
- byggmester:https://10.100.100.2:8443
3. 网络配置
两个节点都创建了名为 br0 的网桥设备,所有容器使用桥接网络获取本地 IP,路由器提供域名解析。
systemd-networkd 配置示例:
# /etc/systemd/network/20-wired.network
[Match]
Name=en*
[Network]
Bridge=br0
# /etc/systemd/network/50-br0.netdev
[NetDev]
Name=br0
Kind=bridge
# /etc/systemd/network/50-br0.network
[Match]
Name=br0
[Network]
Address=192.168.1.2/24
Gateway=192.168.1.1
DNS=192.168.1.1Incus 默认配置:
# incus profile show default
description: Default Incus profile
devices:
eth0:
nictype: bridged
parent: br0
type: nic
root:
path: /
pool: default
type: disk
name: default4. OCI 容器支持
Incus 支持从容器镜像仓库直接拉取 OCI 容器。例如启动 Valkey 容器:
incus remote add docker https://docker.io/ --protocol=oci --public
incus launch docker:valkey/valkey valkey容器启动后可直接通过本地域名访问:
ping valkey.local
# PING valkey.local (192.168.1.148) 56(84) bytes of data.四、基础设施即代码:OpenTofu
1. OpenTofu 简介
OpenTofu(Terraform 的开源替代品)通过声明式配置管理 Incus 资源,将所有持久化数据存储在 NAS 上,实现基础设施的可重现部署。
2. 项目管理
所有服务按项目分类,每个项目共享通用模板,包括权限、网络和默认配置。OpenTofu 模块结构:
- project 模块:所有其他模块的基础模板
- 各服务模块:从 project 模块派生
3. 项目列表
当前运行的项目:
| 项目名称 | 描述 | 容器数量 |
|---|---|---|
| ca | CA 项目 | 4 |
| default | 默认项目 | 48 |
| dns | DNS 项目 | 7 |
| immich | Immich 项目 | 14 |
| mediaserver | 媒体服务器项目 | 18 |
| miniflux | Miniflux 项目 | 6 |
| syncthing | Syncthing 项目 | 3 |
| test | 测试项目 | 2 |
| user-1000 | 用户受限项目 | 2 |
4. Immich 部署示例
Immich 项目包含 7 个容器:
- auto-album-jpg
- auto-album-raw
- database
- immich
- immich-machine-learning
- redis
OpenTofu 配置示例:
resource "incus_image" "redis" {
project = incus_project.immich.name
alias {
name = "redis"
}
source_image = {
remote = "docker"
name = "valkey/valkey:8-bookworm"
}
}
resource "incus_instance" "immich_redis" {
name = "redis"
image = incus_image.redis.fingerprint
project = incus_project.immich.name
target = "amd"
config = {
"boot.autorestart" = true
}
}五、网络暴露方案
1. WireGuard 反向隧道
由于家庭 ISP 使用 NAT,无法直接暴露服务到公网。解决方案是在具有静态 IPv4 和 IPv6 的 VPS 上建立 WireGuard 点对点 VPN 隧道。
sequenceDiagram
participant Internet as 互联网用户
participant VPS as VPS 服务器<br/>静态 IP
participant WG as WireGuard 隧道
participant byggmester as Intel NUC<br/>byggmester
participant Incus as Incus 容器
Internet->>VPS: HTTPS 请求
VPS->>WG: 转发流量
WG->>byggmester: 隧道传输
byggmester->>Incus: 反向代理
Incus->>byggmester: 返回内容
byggmester->>WG: 隧道传输
WG->>VPS: 转发响应
VPS->>Internet: HTTPS 响应2. Nginx 反向代理
在 byggmester 节点上运行 nginx,将需要对外暴露的服务反向代理到内部 Incus 容器。使用内部 DNS 服务器解析域名,避免在 nginx 配置中硬编码 IP。
nginx-acme 插件自动处理 TLS 证书,无需手动运行 certbot。
nginx 配置示例:
# /etc/nginx/nginx.d/10-acme.conf
acme_issuer letsencrypt {
uri https://acme-v02.api.letsencrypt.org/directory;
contact root@linderud.dev;
state_path /var/lib/nginx/acme;
accept_terms_of_service;
}
acme_shared_zone zone=ngx_acme_shared:1M;
server {
listen 80;
location / {
return 404;
}
}
# /etc/nginx/snippets/acme.conf
acme_certificate letsencrypt;
ssl_certificate $acme_certificate;
ssl_certificate_key $acme_certificate_key;
ssl_certificate_cache max=2;
# /etc/nginx/nginx.d/30-bilder.linderud.dev.conf
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name bilder.linderud.dev;
access_log /var/log/nginx/bilder.linderud.dev/access.log;
error_log /var/log/nginx/bilder.linderud.dev/error.log;
include snippets/acme.conf;
include snippets/sslsettings.conf;
client_max_body_size 50000M;
resolver 192.168.1.1 ipv6=off;
location / {
set $immich immich.local;
proxy_pass http://$immich:2283;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_redirect off;
}
}六、静态网站部署
1. Syncthing 同步方案
对于静态网站(如博客),作者使用 Syncthing 将构建后的文件同步到 Web 服务器,避免了复杂的 CI/CD 流程。
2. 部署流程
graph LR
Local[本地计算机] -->|Hugo 构建| Build[构建输出]
Build -->|Syncthing| Sync[同步目录]
Sync -->|/srv/linderud.dev/blog| Web[Web 服务器]服务器目录结构:
/srv/linderud.dev/
├── blog # 博客
├── coredns01 # DNS 服务器配置
└── pub # 公共共享文件部署命令:
hugo build -d /srv/linderud.dev/blog/优点:
- 无服务依赖
- 无需 CI/CD 设置
- 无需 GitHub Actions
- 无需 Ansible Playbooks
七、网络管理:OpenWRT One
1. 硬件设备
OpenWRT One 是面向 OpenWRT 开发者的路由器,购自 AliExpress。
2. 配置管理
作者编写了 OpenTofu provider 管理路由器配置,实现了基础设施即代码的网络管理。
3. 未来规划
计划添加支持 OpenWRT 的交换机,实现 VLAN 网络分段。
八、运维工具
1. USB KVM
在 38C3 活动中购买的 USB KVM 设备,通过 HDMI 和 USB-C 线缆连接到各个小计算机,简化了多设备管理。
九、技术总结
1. 核心技术栈
- 容器平台:Incus(LXC 容器 + QEMU 虚拟机 + OCI 容器)
- 基础设施即代码:OpenTofu
- 网络暴露:WireGuard + nginx
- 文件同步:Syncthing
- 路由器:OpenWRT One
2. 架构优势
- 统一容器化部署
- 声明式配置管理
- 简化的网络暴露方案
- 无依赖的静态网站部署
3. 设计理念
- 简单性优于复杂性
- 自托管优于云服务
- 可靠性优于功能丰富