All checks were successful
docker-images / build-and-push (admin, admin, termi-astro-admin, admin/Dockerfile) (push) Successful in 43s
docker-images / build-and-push (backend, backend, termi-astro-backend, backend/Dockerfile) (push) Successful in 25m9s
docker-images / build-and-push (frontend, frontend, termi-astro-frontend, frontend/Dockerfile) (push) Successful in 51s
223 lines
6.1 KiB
Markdown
223 lines
6.1 KiB
Markdown
# 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-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
|