# 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. 为什么上层已经有 Caddy,admin 容器里还要 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`,通知会入队但没人消费。 ## 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-markdown.sh` - `deploy/scripts/backup/backup-media.sh` - `deploy/scripts/backup/restore-postgres.sh` - `deploy/scripts/backup/restore-markdown.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