Homer Dashboard 完整部署与优化:Docker 配置、CORS 健康检查、Proxmox 集成

我的 NAS Dashboard 折腾过好几轮了。Homepage 太复杂,Glance 太素,最后落到了 Homer—— 一个单 HTML 文件就能跑的轻量 Dashboard,配置用 YAML,图标用 Font Awesome,热加载不用重启。

但默认的 Docker 配置有几个坑,健康检查(Ping)也因为浏览器 CORS 策略全挂。踩完一遍坑后记录这篇,下次重建直接抄。

环境:fnOS(<NAS_IP>),Docker Compose,Homer v26.04.2(b4bz/homer)。

Docker Compose 优化

Homer 的官方示例 docker-compose.yml 有几个小问题,长期跑建议改掉:

services:
homer:
image: b4bz/homer
container_name: homer
restart: unless-stopped # 尊重手动停止
volumes:
- ./www/assets:/www/assets
ports:
- 8888:8080
deploy:
resources:
limits:
memory: 128M
cpus: "0.5"
logging:
6 collapsed lines
driver: json-file
options:
max-size: "10m"
max-file: "3"
security_opt:
- no-new-privileges:true
改动原因
alwaysunless-stoppedNAS 维护时手动 stop 不会被 daemon 拉起
移除 INIT_ASSETS=1非首次部署,避免覆盖自定义主题文件
移除 user: 1000:1000消除 root-owned 文件权限冲突
添加资源限制128MB 内存 / 0.5 CPU,防止异常耗光资源
添加日志轮转10MB × 3 文件,避免日志撑满磁盘
添加安全加固no-new-privileges:true 禁止容器内提权

CORS 健康检查代理

这是整个配置里最折腾的部分。

Homer 的 Ping 组件通过浏览器向目标服务发 HEAD 请求来检测状态。问题在于:Homer 页面跑在 <NAS_IP>:8888,目标服务在 <NAS_IP>:xxxx。不同端口 = 不同源,浏览器直接拦截跨域请求。结果就是所有服务全部显示 offline,Ping 组件成了摆设。

解决思路:在 NAS 上跑一个轻量 Python 代理(端口 8889),浏览器请求发给代理,代理转发给目标并附加 CORS 头。

浏览器 → Homer(:8888) → CORS代理(:8889) → 各服务端口
↑ 直连导航 ↑ 健康检查(加 CORS 头)

代理代码

app.py 核心逻辑:

from http.server import HTTPServer, BaseHTTPRequestHandler
from socketserver import ThreadingMixIn
import urllib.request
import ssl
class ThreadingServer(ThreadingMixIn, HTTPServer):
"""多线程处理并发请求"""
class ProxyHandler(BaseHTTPRequestHandler):
def do_GET(self):
target_url = self.path.split('?url=', 1)[-1]
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE # Proxmox 自签名证书
17 collapsed lines
req = urllib.request.Request(target_url)
# 透传 Authorization 头(Proxmox API 需要)
auth = self.headers.get('Authorization')
if auth:
req.add_header('Authorization', auth)
try:
with urllib.request.urlopen(req, timeout=10, context=ctx) as resp:
self.send_response(200)
except Exception:
self.send_response(502)
self.send_header('Access-Control-Allow-Origin', '*')
self.end_headers()
server = ThreadingServer(('0.0.0.0', 8889), ProxyHandler)
server.serve_forever()

踩坑记录

  1. 单线程排队:Python http.server 默认单线程,12 个 Ping 同时请求导致排队超时。ThreadingMixIn 解决。

  2. fnOS 防火墙<NAS_IP> 外部访问被拦,只有 127.0.0.1 能通。NAS 本机服务的健康检查 URL 统一用 127.0.0.1

  3. HEAD 不支持:飞牛终端 (5122)、OpenList (5255) 等服务返回 405。代理内部 HEAD 自动转 GET。

  4. 超时不够:Ping 默认 2 秒,云端服务(尤其海外)来不及。timeout: 10000

端点格式速查

# NAS 本机服务(代理用 127.0.0.1)
endpoint: "http://<NAS_IP>:8889/?url=http://127.0.0.1:10880"
# 其他物理机服务
endpoint: "http://<NAS_IP>:8889/?url=http://<N8N_HOST_IP>:5678"
# 云端 HTTPS 服务
endpoint: "http://<NAS_IP>:8889/?url=https://marxchou.com"

Proxmox 监控集成

Homer 内置 Proxmox 类型支持,可以直接展示节点 CPU / 内存使用率。不过有个小坑:认证方式要用 api_token 而不是 username/password

- name: Proxmox(监控)
type: Proxmox
url: https://<PVE_HOST_IP>:8006
endpoint: http://<NAS_IP>:8889/?url=https://<PVE_HOST_IP>:8006
node: zcpve
api_token: PVEAPIToken=root@pam!homer=<secret>
warning_value: 50
danger_value: 80
updateIntervalMs: 30000

由于 Proxmox 使用自签名 SSL 证书,代理端必须忽略证书验证(ssl.CERT_NONE),同时透传 Authorization 头。不然 API Token 认证过不去。

配置文件结构

Homer 支持多页面,我把服务按位置分了三页:

文件页面内容
config.yml主页 (Nas)本地 NAS 服务
localhost.yml#localhost局域网其他设备 + Proxmox
cloud-server.yml#cloud-server公网云端服务

服务条目模板:

# 本地服务
- name: "Gogs"
icon: "fa-brands fa-git-alt"
subtitle: "轻量级 Git 服务 · :10880"
tag: "dev"
url: "http://<NAS_IP>:10880"
endpoint: "http://<NAS_IP>:8889/?url=http://127.0.0.1:10880"
type: "Ping"
# 云端服务(注意 timeout)
- name: "MarxChou"
icon: "fa-solid fa-feather"
subtitle: "个人博客"
tag: "blog"
url: "https://marxchou.com"
5 collapsed lines
endpoint: "http://<NAS_IP>:8889/?url=https://marxchou.com"
successCodes: [200, 301, 302, 308, 401, 403]
timeout: 10000
updateIntervalMs: 60000
type: "Ping"

NAS 上目前监控了 18 个服务:Gogs、Rustpad、Syncthing、Linkwarden、Wallos、Tiny Tiny RSS、RSSHub、Donetick、Linkding 等。

排障速查

服务显示 offline:

  1. curl "http://localhost:8889/?url=http://127.0.0.1:PORT" — 测试代理可达性
  2. ss -tlnp | grep PORT — 确认目标端口在监听
  3. docker ps -a --filter name=xxx — 确认容器在跑
  4. docker logs homer-health | grep PORT — 查看代理日志
  5. curl http://localhost:8888/assets/config.yml — 确认配置被正确加载

代理挂了:

Terminal window
cd /vol1/1000/docker/homer-health
docker compose down && docker compose up -d --build

添加新服务: 在对应 yml 里加条目 → Homer 热加载,无需重启。导航 URL 用 <NAS_IP>:PORT,健康检查用 127.0.0.1:PORT

云端服务误报 offline: 默认 Ping 超时 2 秒不够。加 timeout: 10000,顺手加 updateIntervalMs: 60000 还能解决 “Invalid registration parameters” 警告。

文件清单

/vol1/1000/docker/
├── homer/
│ ├── docker-compose.yml # Homer 主容器
│ └── www/assets/
│ ├── config.yml # NAS 主页配置
│ ├── localhost.yml # 局域网服务 + Proxmox
│ ├── cloud-server.yml # 云端服务
│ └── catppuccin-macchiato.css # 主题
└── homer-health/
├── docker-compose.yml # CORS 代理容器
├── Dockerfile
└── app.py # 代理核心代码

总结

Homer 本身很简单:一个 HTML 文件加一个 YAML 配置就能跑。但要让健康检查真正工作,CORS 代理是绕不过去的。Python 几行代码的事,不过单线程、HEAD 不支持这些坑踩过才知道。

Dashboard 这种东西,折腾的乐趣大于实用价值。但每天打开浏览器看到一排绿灯,心情确实好一点。