153 lines
7.1 KiB
Markdown
153 lines
7.1 KiB
Markdown
---
|
||
name: Hermes Glass UI 个人版 · kang-kang.com 部署
|
||
description: 从公司版 hermes.milejoy.com fork + 合规隔离重建的个人 VPS 部署完整细节
|
||
type: project
|
||
---
|
||
|
||
# 架构
|
||
|
||
```
|
||
浏览器
|
||
↓ https://hermes.kang-kang.com/
|
||
Coolify Traefik (443, letsencrypt 自动签)
|
||
↓ Host(`hermes.kang-kang.com`) → http://10.0.0.1:8088
|
||
宿主 nginx 1.24 (listen 10.0.0.1:8088, /etc/nginx/sites-available/hermes.kang-kang.com)
|
||
├─ /login.html + /_auth/verify + /_auth/logout cookie 门禁
|
||
├─ / /var/www/hermes-kang/(Glass UI 静态)
|
||
├─ /v1/ + /api/v1/(rewrite) + /api/jobs 注入 Bearer → LXC
|
||
├─ /memory/ + /hermes-skills/ + /classic/ 空目录占位
|
||
└─ /health 免门禁
|
||
Incus LXC hermes-personal (10.3.73.137, Debian trixie, privileged + nesting)
|
||
└─ Docker 26.1.5 (hermes-agent container)
|
||
└─ Hermes Agent v0.7, gateway run, 0.0.0.0:8642
|
||
└─ OpenRouter → google/gemini-3.1-pro-preview
|
||
```
|
||
|
||
# 关键决策
|
||
|
||
## CORS 白名单必须配(2026-04-21 事后补的坑)
|
||
- Hermes gateway 的 API Server 会检查 `Origin` header 白名单
|
||
- `.env` 里必须 `API_SERVER_CORS_ORIGINS=https://hermes.kang-kang.com`
|
||
- 不配的话:curl 测试一切 200(curl 不发 Origin),但**真实浏览器发 POST /api/v1/chat/completions 会返回 403 body=0**(Hermes CORS 拒绝)
|
||
- 公司版 .env 原本就配了 `https://hermes.milejoy.com`,我 fork 时抄漏了
|
||
- 修复:加环境变量 → `docker compose down && up`(不能 restart)
|
||
|
||
## 为什么用 Debian trixie 不用 Ubuntu 24.04
|
||
- Ubuntu 24.04 + Docker 29 在非/特权 LXC 内启动容器报 `net.ipv4.ip_unprivileged_port_start: permission denied`
|
||
- Debian trixie + Docker 26 没这个 bug(公司版 hermes-box 用的就是这个组合,直接照搬)
|
||
- 也加了 `security.syscalls.intercept.mknod/setxattr=true`(跟公司版对齐)
|
||
|
||
## 为什么宿主 nginx 听 10.0.0.1:8088 不是 127.0.0.1:8088
|
||
- Coolify-proxy (Traefik) 跑在 docker coolify 网络,从容器内访问宿主要走 docker0 gateway
|
||
- 宿主 docker0 IP 是 `10.0.0.1`(非标准,Coolify 自定义)
|
||
- 127.0.0.1 从 Traefik 容器里访问是容器自己的 localhost,不是宿主
|
||
- Traefik 容器 /etc/hosts 有 `10.0.0.1 host.docker.internal` —— 用宿主 docker0 IP 直连宿主 nginx
|
||
|
||
## 为什么 gitea.yaml 和 notebooklm-mcp.yaml 被改了
|
||
- Traefik watcher 解析 `dynamic/` 目录时遇到这两个文件里 `dialTimeout/responseHeaderTimeout` **不在 forwardingTimeouts 下**,报 `field not found`
|
||
- 这个错误**阻止了整个 directory 的 reload**(不是 per-file 隔离),所以我新写的 hermes-kang.yaml 一直没被 pick up
|
||
- 修复:把两个文件里的 `dialTimeout`/`responseHeaderTimeout` 包到 `forwardingTimeouts:` 下(Traefik v3 正确嵌套)
|
||
- 备份:`gitea.yaml.bak-<ts>`, `notebooklm-mcp.yaml.bak-<ts>`
|
||
- gitea/lobehub/notebooklm-mcp 都继续工作(verified via curl 200/302)
|
||
|
||
## 为什么不用 Traefik basicauth middleware / forwardAuth sidecar
|
||
- 保留公司版的自定义 Liquid Glass login.html + cookie 门禁体验
|
||
- 单用户场景 Authelia sidecar 不划算
|
||
- nginx 熟悉度高,排障快
|
||
|
||
# 凭证
|
||
|
||
存 `credentials.md` 不在本文件重复。
|
||
|
||
# 文件清单
|
||
|
||
## 宿主 76.13.31.179
|
||
|
||
| 路径 | 作用 | 备注 |
|
||
|---|---|---|
|
||
| `/etc/nginx/sites-available/hermes.kang-kang.com` | nginx 站点,listen 10.0.0.1:8088 | sites-enabled 软链已建 |
|
||
| `/etc/nginx/.htpasswd-hermes-kang` | bcrypt 密码 | 只 `kang` 一个用户 |
|
||
| `/data/coolify/proxy/dynamic/hermes-kang.yaml` | Traefik 路由+letsencrypt | owner 9999:root, mode 700 |
|
||
| `/var/www/hermes-kang/` | Glass UI 静态 | rsync 自本机个人版 src/ |
|
||
| `/var/www/hermes-memory-kang/` | `/memory/` 路由空目录 | 自己以后填 |
|
||
| `/var/www/hermes-skills-kang/` | `/hermes-skills/` 路由空目录 | 自己以后填 |
|
||
| `/opt/hermes-build/` | 镜像 build 源码(rsync 自本机)| 32MB |
|
||
| `/tmp/hermes-build.log` | build 过程 log | 留作参考 |
|
||
|
||
## Incus LXC hermes-personal
|
||
|
||
| 路径 | 作用 |
|
||
|---|---|
|
||
| `/opt/hermes-agent/docker-compose.yml` | compose |
|
||
| `/opt/hermes-agent/config.yaml` | Hermes gateway 配置(OpenRouter + gemini-3.1-pro-preview)|
|
||
| `/opt/hermes-agent/.env` | `OPENROUTER_API_KEY` + `API_SERVER_KEY`, mode 600 |
|
||
| `/opt/hermes-agent/data/` | Hermes workspace(HERMES_HOME)|
|
||
|
||
# 常用操作
|
||
|
||
## 改前端代码后同步
|
||
```bash
|
||
cd ~/Projects/code/20260421-hermes-glass-ui-personal
|
||
# 编辑 src/*
|
||
rsync -az --delete src/ root@76.13.31.179:/var/www/hermes-kang/
|
||
# sw.js 如需强刷:bump CACHE 版本号
|
||
```
|
||
|
||
## 改后端配置/模型
|
||
```bash
|
||
ssh root@76.13.31.179
|
||
incus exec hermes-personal -- bash
|
||
cd /opt/hermes-agent
|
||
vi config.yaml # 改模型
|
||
vi .env # 改 key
|
||
docker compose down && docker compose up -d
|
||
# ⚠️ docker restart 不 reload env_file,必须 down + up
|
||
```
|
||
|
||
## 换 OpenRouter key
|
||
```bash
|
||
incus exec hermes-personal -- bash -c "sed -i 's|^OPENROUTER_API_KEY=.*|OPENROUTER_API_KEY=<新key>|' /opt/hermes-agent/.env && cd /opt/hermes-agent && docker compose down && docker compose up -d"
|
||
```
|
||
|
||
## 查日志
|
||
```bash
|
||
incus exec hermes-personal -- docker logs hermes-agent --tail 50
|
||
ssh root@76.13.31.179 tail -f /var/log/nginx/error.log
|
||
ssh root@76.13.31.179 docker logs coolify-proxy --since 2m 2>&1 | grep -i hermes
|
||
```
|
||
|
||
# 不破坏的约束
|
||
|
||
- ✅ 不碰 Coolify 现有 22+ 容器 + Coolify-proxy 本体
|
||
- ✅ 不碰 `/opt/lobechat-mirror/` 独立 docker compose
|
||
- ✅ 不碰 `/opt/gitea/` `/opt/postgres/` `/opt/mysql/`
|
||
- ✅ hermes-kang 走独立 LXC + 独立 nginx 站点 + 独立 Traefik dynamic file
|
||
- ✅ 宿主 nginx 独自启动(systemd nginx.service 新启用)
|
||
- ✅ 禁用了 sites-enabled/default 和 sites-enabled/styles.kang-kang.com(冲突 listen 80,且 styles 实际由 style-gallery-nginx docker 容器跑)
|
||
|
||
# 与公司版的差异
|
||
|
||
| 维度 | 公司版 hermes.milejoy.com | 个人版 hermes.kang-kang.com |
|
||
|---|---|---|
|
||
| 宿主 | 公司 VPS 2.24.28.41 | 个人 VPS 76.13.31.179 |
|
||
| 入口 | 宿主 nginx 直听 443 | Coolify Traefik 443 → 宿主 nginx 10.0.0.1:8088 |
|
||
| 证书 | certbot ai.milejoy.com 复用 | Traefik letsencrypt certresolver |
|
||
| LXC | hermes-box, Debian trixie, 10.146.223.10 | hermes-personal, Debian trixie, 10.3.73.137 |
|
||
| 模型 | Gemini 3 Pro Preview 直连(GOOGLE_API_KEY)| Gemini 3.1 Pro Preview via OpenRouter |
|
||
| 认证 | basic auth boss/mile | basic auth kang |
|
||
| Skills/Memory | 78 真实 skill + 同步真实 memory | 空目录(未来按需填充) |
|
||
|
||
# 合规边界(离职场景)
|
||
|
||
✅ **可搬**:
|
||
- 个人编写的 Glass UI 前端源码(fork 到 `code/20260421-hermes-glass-ui-personal/`)
|
||
- Hermes Agent 开源代码(NousResearch 上游)
|
||
- 架构和 nginx/Traefik 配置思路
|
||
|
||
❌ **未搬**:
|
||
- 公司 API Server Key `ffd2f8af...`(重新用 openssl 生成新的)
|
||
- 公司 GOOGLE_API_KEY(换 OpenRouter + 个人 key)
|
||
- 公司 LXC 里的 memory/skills/sessions/对话历史(个人版从零起)
|
||
- 公司 basic auth 账号 `boss/mile`(改 `kang` 单账号)
|
||
- 公司 nginx 证书(letsencrypt 新签)
|