Files
termi-blog/deploy/docker/ARCHITECTURE.md
limitcool 11ec00281c
Some checks failed
docker-images / resolve-build-targets (push) Failing after 1s
docker-images / build-and-push (admin) (push) Has been cancelled
docker-images / submit-indexnow (push) Has been cancelled
docker-images / build-and-push (backend) (push) Has been cancelled
docker-images / build-and-push (frontend) (push) Has been cancelled
ui-regression / playwright-regression (push) Has been cancelled
Fix AI reindex job execution and progress
2026-04-04 00:40:46 +08:00

228 lines
6.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Docker / 反代架构说明
本文记录当前项目在 `tohka` 这类宿主机上的推荐部署结构,以及为什么会同时出现:
- 宿主机层的 **Caddy**
- `admin` 容器内的 **Nginx**
## 1. 总体分层
推荐生产结构:
```text
Internet
-> Host Caddy (:80 / :443)
-> frontend container (Astro SSR / Node :4321)
-> admin container (Nginx :80, 静态 SPA)
-> backend container (Loco.rs API :5150)
-> backend-worker container (Loco.rs worker / Redis queue)
```
职责划分:
- **Host Caddy**
- 统一接收公网流量
- 处理域名、HTTPS、证书续签
- 反向代理到各个内部容器
- **frontend**
- Astro SSR(Node) 应用
- 不是纯静态站,所以容器内直接运行 Node 服务即可
- **admin**
- React/Vite 打包后的纯静态 SPA
- 容器内使用 Nginx 提供静态文件
- 生产推荐前面接 TinyAuth / Pocket ID 做 SSO
- **backend**
- API、后台鉴权、审计、版本历史、订阅投递
- **backend-worker**
- 消费 Redis 队列
- 负责通知异步投递、失败重试、digest 任务触发后的实际发送
## 2. 为什么上层已经有 Caddyadmin 容器里还要 Nginx
这两层并不冲突,职责不同:
- **Caddy** 是入口网关
- **Nginx** 是 admin 容器内部的静态文件服务器
也就是说:
```text
Browser
-> Caddy
-> admin nginx
-> /usr/share/nginx/html
```
这样做的好处:
- admin 镜像本身自带可用的静态文件服务能力
- 宿主机层仍然保留统一的域名 / HTTPS / 路由管理
- admin 作为独立前端,可以单独构建、单独发布
## 2.2 为什么现在多了 `backend-worker`
因为当前通知系统已经改成:
```text
backend (web)
-> 写入 notification_deliveries
-> enqueue 到 Redis
backend-worker
-> 消费队列
-> 发送 email / webhook / discord / telegram / ntfy
```
如果只启动 `backend` 而没有 `backend-worker`,通知会入队但没人消费。
补充说明:
- `backend-worker` 目前主要消费 Redis 队列里的通知相关任务。
- AI 索引重建会直接在 `backend` 进程本地启动,这样创建任务后会立即进入执行,不再依赖独立 worker 消费。
## 2.1 推荐的后台认证链路
当前最推荐:
```text
Browser
-> Caddy (import tinyauth)
-> TinyAuth
-> Pocket ID (OIDC)
-> admin nginx / backend API
```
关键点:
- admin 页面和它调用的 `/api/*` 建议走 **同一个受保护后台域名**
- Caddy 使用 `forward_auth`
- backend 开启 `TERMI_ADMIN_TRUST_PROXY_AUTH=true`
- 生产推荐再配 `TERMI_ADMIN_PROXY_SHARED_SECRET=<随机长字符串>`
- Caddy 在 `/api/*` 反代到 backend 时补 `X-Termi-Proxy-Secret`
- backend 读取 TinyAuth 转发的 `Remote-User / Remote-Email / Remote-Groups`
- 本地开发可以保留 `TERMI_ADMIN_LOCAL_LOGIN_ENABLED=true`
## 3. 为什么 frontend 不使用同样的 Nginx 模式?
因为当前 `frontend`**Astro SSR**
- `output: 'server'`
- `@astrojs/node` standalone
它需要在请求期执行服务端逻辑,因此更适合:
```text
Caddy -> frontend Node server
```
而不是先打成纯静态文件再由 Nginx 托管。
## 4. admin 容器内 Nginx 当前负责什么?
当前 `admin/nginx.conf` 主要负责:
- SPA fallback`try_files ... /index.html`
- `assets/` 长缓存hash 资源可 `immutable`
- `index.html` / `runtime-config.js` 禁缓存,避免配置或入口文件陈旧
- `gzip` 压缩
- 基础安全响应头
- `/healthz` 健康检查入口
### 为什么 `runtime-config.js` 要禁缓存?
因为 admin 现在支持运行时环境变量注入,例如:
- `ADMIN_API_BASE_URL`
- `ADMIN_FRONTEND_BASE_URL`
容器启动时会生成 `runtime-config.js`
如果它被强缓存,改完环境变量重启容器后,浏览器可能仍然读到旧地址。
## 5. 为什么没有在 admin 容器里启用 Brotli
当前基础镜像是官方 `nginx:alpine`
这个镜像默认不一定带 Brotli 模块,所以这里先启用通用的 `gzip`
如果后续确实需要 Brotli有两个常见做法
- 让宿主机层的 Caddy 统一负责压缩
- 改用带 Brotli 模块的自定义 Nginx 镜像
对当前项目而言,优先让 **宿主机 Caddy 做统一公网入口**admin 容器内部只负责稳妥地提供静态文件,是更简单的方案。
## 6. 推荐的 tohka 思路
如果 `tohka` 上已经有一个统一的大 Caddyfile推荐继续保持
- Caddy 统一暴露 `80/443`
- `frontend/admin/backend` 只走内网端口
- 不把数据库 / Redis 直接暴露到公网
- backend 如果启用了代理 SSO不要再把 `:5150` 直接开放给公网
仓库里已经额外提供:
- `deploy/docker/compose.tohka.override.yml`
用它叠加 `compose.package.yml` 后,会把:
- `frontend:4321`
- `admin:4322`
- `backend:5150`
都只绑定到 `127.0.0.1`
## 7. 当前和配置相关的关键文件
- 宿主机入口反代:`tohka` 上的大 Caddyfile
- admin 静态服务:`admin/nginx.conf`
- admin 镜像:`admin/Dockerfile`
- Caddy 参考模板:`deploy/caddy/Caddyfile.tohka.example`
- compose 示例:`deploy/docker/compose.package.yml`
- tohka override`deploy/docker/compose.tohka.override.yml`
- OIDC / Pocket ID 落地:`deploy/docker/TOHKA_POCKET_ID.md`
- 运行时环境示例:`deploy/docker/.env.example`
## 8. Caddy 路由推荐
默认更推荐:
- `blog.init.cool` -> frontend
- `admin.blog.init.cool` -> admin + backend(`/api/*`)
- `api.blog.init.cool` -> backend
这样最省心也最不容易碰到路径前缀、资源基路径、Cookie Path 等问题。
如果一定要用:
- `init.cool/admin`
- `init.cool/api`
也可以,但 `admin` 需要在构建时设置:
- `VITE_ADMIN_BASENAME=/admin`
对应模板见:
- `deploy/caddy/Caddyfile.tohka.example`
## 9. 备份 / 恢复入口
当前仓库内已经补了:
- `deploy/scripts/backup/backup-postgres.sh`
- `deploy/scripts/backup/backup-media.sh`
- `deploy/scripts/backup/restore-postgres.sh`
- `deploy/scripts/backup/restore-media.sh`
- `deploy/docker/BACKUP_AND_RECOVERY.md`
建议把这些接进生产上的 cron / systemd timer而不是只停留在仓库里。
## 10. 健康检查与启动顺序
当前推荐闭环:
- backend 启动时先自动跑 migration
- backend 提供 `/healthz`
- frontend 提供 `/healthz`
- admin 由 Nginx 提供 `/healthz`
- compose 中 frontend / admin / backend-worker 都依赖 backend healthy