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

6.3 KiB
Raw Permalink Blame History

Docker / 反代架构说明

本文记录当前项目在 tohka 这类宿主机上的推荐部署结构,以及为什么会同时出现:

  • 宿主机层的 Caddy
  • admin 容器内的 Nginx

1. 总体分层

推荐生产结构:

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 容器内部的静态文件服务器

也就是说:

Browser
  -> Caddy
    -> admin nginx
      -> /usr/share/nginx/html

这样做的好处:

  • admin 镜像本身自带可用的静态文件服务能力
  • 宿主机层仍然保留统一的域名 / HTTPS / 路由管理
  • admin 作为独立前端,可以单独构建、单独发布

2.2 为什么现在多了 backend-worker

因为当前通知系统已经改成:

backend (web)
  -> 写入 notification_deliveries
  -> enqueue 到 Redis
backend-worker
  -> 消费队列
  -> 发送 email / webhook / discord / telegram / ntfy

如果只启动 backend 而没有 backend-worker,通知会入队但没人消费。

补充说明:

  • backend-worker 目前主要消费 Redis 队列里的通知相关任务。
  • AI 索引重建会直接在 backend 进程本地启动,这样创建任务后会立即进入执行,不再依赖独立 worker 消费。

2.1 推荐的后台认证链路

当前最推荐:

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 模式?

因为当前 frontendAstro SSR

  • output: 'server'
  • @astrojs/node standalone

它需要在请求期执行服务端逻辑,因此更适合:

Caddy -> frontend Node server

而不是先打成纯静态文件再由 Nginx 托管。

4. admin 容器内 Nginx 当前负责什么?

当前 admin/nginx.conf 主要负责:

  • SPA fallbacktry_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 overridedeploy/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