Files
termi-blog/deploy/docker/README.md
limitcool 497a9d713d
Some checks failed
docker-images / build-and-push (admin, admin, termi-astro-admin, admin/Dockerfile) (push) Failing after 13s
docker-images / build-and-push (frontend, frontend, termi-astro-frontend, frontend/Dockerfile) (push) Has been cancelled
docker-images / build-and-push (backend, backend, termi-astro-backend, backend/Dockerfile) (push) Has been cancelled
feat: ship public ops features and cache docker builds
2026-04-01 13:22:19 +08:00

7.7 KiB
Raw Blame History

Docker 部署Package 镜像)

补充架构说明见:

  • deploy/docker/ARCHITECTURE.md
  • deploy/caddy/Caddyfile.tohka.example
  • deploy/caddy/Caddyfile.tohka.production.example
  • deploy/docker/TOHKA_POCKET_ID.md
  • deploy/docker/TOHKA_DEPLOY_RUNBOOK.md
  • deploy/docker/config.yaml.example
  • deploy/docker/BACKUP_AND_RECOVERY.md

1) 准备主配置文件config.yaml

现在推荐把:

  • deploy/docker/config.yaml

作为部署主配置源;deploy/docker/.env 改为脚本生成产物。

先复制模板:

cp deploy/docker/config.yaml.example deploy/docker/config.yaml

然后填写至少这些核心项:

  • compose_env.DATABASE_URL
  • compose_env.REDIS_URL
  • compose_env.JWT_SECRET
  • 邮件确认 / 邮件通知上线前,请同时补齐 SMTP 配置

填完后执行:

python deploy/scripts/render_compose_env.py \
  --input deploy/docker/config.yaml \
  --output deploy/docker/.env

如果你们内部喜欢先审 YAML再部署这就是现在的推荐流程。

建议在 config.yaml -> compose_env 下同时检查这些运行时变量:

  • INTERNAL_API_BASE_URLfrontend SSR 容器访问 backend 用compose 默认推荐 http://backend:5150/api
  • PUBLIC_API_BASE_URL:浏览器访问 backend API 用;留空时前台会回退到“当前主机 + :5150/api
  • PUBLIC_COMMENT_TURNSTILE_SITE_KEY:前台评论 / 订阅表单使用的 Cloudflare Turnstile site key
  • PUBLIC_WEB_PUSH_VAPID_PUBLIC_KEY:前台浏览器推送订阅使用的 VAPID public key
  • PUBLIC_IMAGE_ALLOWED_HOSTS:前台 /_img 图片优化端点允许的额外图片 host逗号分隔
  • ADMIN_API_BASE_URLadmin 浏览器访问 backend API 用;留空时后台会回退到“当前主机 + :5150
  • ADMIN_FRONTEND_BASE_URLadmin 里“打开前台 / 问答页 / 文章页预览”跳转用
  • TERMI_ADMIN_TRUST_PROXY_AUTH:是否信任前置代理(如 Caddy + TinyAuth注入的后台认证头
  • TERMI_ADMIN_LOCAL_LOGIN_ENABLED:是否保留本地账号密码登录兜底
  • TERMI_ADMIN_PROXY_SHARED_SECRET:代理 SSO 共享密钥;建议和 Caddy 的 X-Termi-Proxy-Secret 配套使用
  • TERMI_TURNSTILE_SECRET_KEYbackend 评论 / 订阅接口使用的 Cloudflare Turnstile secret key兼容旧的 TERMI_COMMENT_TURNSTILE_SECRET_KEY
  • TERMI_WEB_PUSH_VAPID_PRIVATE_KEYbackend / worker 发送浏览器推送时使用的 VAPID private key
  • TERMI_WEB_PUSH_VAPID_SUBJECT:浏览器推送 VAPID subject推荐 mailto:xxx@example.com
  • SMTP_ENABLE / SMTP_HOST / SMTP_PORT / SMTP_SECURE / SMTP_USER / SMTP_PASSWORD / SMTP_HELLO_NAME:订阅确认和邮件通知需要

例如:

compose_env:
  PUBLIC_API_BASE_URL: https://api.blog.init.cool
  PUBLIC_COMMENT_TURNSTILE_SITE_KEY: 1x00000000000000000000AA
  PUBLIC_WEB_PUSH_VAPID_PUBLIC_KEY: replace-with-web-push-vapid-public-key
  ADMIN_API_BASE_URL: https://admin.blog.init.cool
  ADMIN_FRONTEND_BASE_URL: https://blog.init.cool
  TERMI_ADMIN_TRUST_PROXY_AUTH: true
  TERMI_ADMIN_LOCAL_LOGIN_ENABLED: false
  TERMI_ADMIN_PROXY_SHARED_SECRET: replace-with-a-long-random-secret
  TERMI_TURNSTILE_SECRET_KEY: replace-with-turnstile-secret-key
  TERMI_WEB_PUSH_VAPID_PRIVATE_KEY: replace-with-web-push-vapid-private-key
  TERMI_WEB_PUSH_VAPID_SUBJECT: mailto:noreply@blog.init.cool

这些值最终会被渲染成 deploy/docker/.env,再由 compose.package.yml 读取。
如果镜像构建期也注入了 FRONTEND_PUBLIC_API_BASE_URL / ADMIN_VITE_API_BASE / ADMIN_VITE_FRONTEND_BASE_URL,则运行时变量优先级更高。

2) 启动

在仓库根目录执行:

python deploy/scripts/render_compose_env.py \
  --input deploy/docker/config.yaml \
  --output deploy/docker/.env

docker compose -f deploy/docker/compose.package.yml --env-file deploy/docker/.env up -d

如果是在 tohka 上并且前面接宿主机 Caddy推荐改用

python deploy/scripts/render_compose_env.py \
  --input deploy/docker/config.yaml \
  --output deploy/docker/.env

docker compose \
  -f deploy/docker/compose.package.yml \
  -f deploy/docker/compose.tohka.override.yml \
  --env-file deploy/docker/.env up -d

当前 compose 推荐一起启动 4 个容器:

  • backendAPI / migration / /healthz
  • backend-workerRedis 队列消费者(通知异步投递、失败重试)
  • frontendAstro SSR(Node)
  • admin:静态 SPA + Nginx

compose.tohka.override.yml 额外会把三个对外端口绑到 127.0.0.1,避免把 backend / admin 直接暴露到公网。

3) 更新镜像后重拉

docker compose -f deploy/docker/compose.package.yml --env-file deploy/docker/.env pull
docker compose -f deploy/docker/compose.package.yml --env-file deploy/docker/.env up -d

4) 问答记录2026-03-31

Q1: 为什么前台容器是 4321 端口?

A: 前台是 Astro SSR(Node)output: 'server'),容器内运行 Node 服务(dist/server/entry.mjs),所以需要监听内部端口 4321

Q2: 两个前端都能做成纯静态 + 反代吗?

A:

  • admin 可以,当前就是静态资源由 Nginx 提供。
  • frontend 当前不行(未做纯静态改造):项目里使用了 prerender = falseAstro.requestAstro.cookies 等请求期逻辑。

Q3: SSR 和 CSR 怎么选?

A: 当前站点对外内容页优先 SEO 与首屏可见性,保留 SSR 更稳;后台管理台继续 CSR/静态站即可。

Q4: 生产推荐端口设计是什么?

A: 推荐前置 Caddy/Nginx 统一暴露 80/443frontend:4321 / backend:5150 / admin:80 仅走内网。 当前 compose.package.yml 属于直连端口版,便于快速部署与联调。
另外因为通知已经走异步队列,生产务必同时启动 backend-worker

Q5: 为什么 compose 里没看到 ADMIN_VITE_FRONTEND_BASE_URL

A:

  • 现在 compose 运行时可以直接设置 ADMIN_FRONTEND_BASE_URL,用于覆盖 admin 里所有前台跳转链接。
  • ADMIN_VITE_FRONTEND_BASE_URL 仍然可以作为 镜像构建期默认值 保留在 CI / workflow 里。
  • 如果两者都存在,运行时 ADMIN_FRONTEND_BASE_URL 优先

Q6: 前台 / 后台为什么拆成 public/internal 两种 API 地址?

A:

  • frontend 是 Astro SSR服务端渲染请求 backend 时更适合走内网地址(如 http://backend:5150/api)。
  • 但浏览器里的评论、问答、搜索请求必须走用户可访问的公开地址。
  • admin 是纯静态 SPA也只能使用浏览器可访问的公开 API 地址。
  • 所以现在区分为:
    • INTERNAL_API_BASE_URLfrontend SSR 内部访问
    • PUBLIC_API_BASE_URL:前台浏览器访问
    • ADMIN_API_BASE_URLadmin 浏览器访问

Q7: 现在后台 OIDC / Pocket ID 怎么接?

A:

  • 推荐直接复用 tohka 上现成的 TinyAuth + Pocket ID
  • Caddy 在 admin 域名入口加 import tinyauth,并把 /api/* 同域转发到 backend。
  • backend 开启:
    • TERMI_ADMIN_TRUST_PROXY_AUTH=true
    • TERMI_ADMIN_LOCAL_LOGIN_ENABLED=false
    • TERMI_ADMIN_PROXY_SHARED_SECRET=<随机长字符串>
  • Caddy 在 /api/* -> backend 时补:
    • X-Termi-Proxy-Secret: {$TERMI_ADMIN_PROXY_SHARED_SECRET}
  • backend 会信任 TinyAuth 转发的:
    • Remote-User
    • Remote-Email
    • Remote-Groups
  • 本地开发仍可保留内置账号密码登录。

Q8: 备份现在放在哪看?

A:

  • 参考脚本:deploy/scripts/backup/
  • 恢复文档:deploy/docker/BACKUP_AND_RECOVERY.md

Q9: 现在健康检查和 migration 怎么处理?

A:

  • backend 镜像启动时会先执行 db migrate
  • backend 提供 /healthz
  • frontend 提供 /healthz
  • admin 继续由 Nginx 提供 /healthz
  • compose 现在使用 depends_on.condition: service_healthy