实际生产拓扑(本仓库采用)
deploy/README.md 记录的真实架构是:前端静态托管在阿里云 Nginx + 后端跑在本地 WSL 的 Docker + FRP 内网穿透双端联通。
1 | 公网用户 |
要点:
- 数据库是 PostgreSQL(库名
printing-factory),不是 MySQL;且 PostgreSQL / Redis / MinIO 跑在 WSL 宿主机、不在 compose 里,容器通过host.docker.internal(extra_hosts: host.docker.internal:host-gateway)访问。 - 所有容器
TZ=Asia/Shanghai;pf-service/compose/face各自 bind-mount./<svc>/logs → /app/logs落盘到 WSL 宿主。
docker-compose 服务矩阵
deploy/docker-compose.yml(单一文件,无 override/env 变体):
| 服务 (container) | 镜像 / 构建 | 端口 (host:container) | 说明 |
|---|---|---|---|
pf-service |
build ./pf-service |
3001:3001 |
主 API,depends_on face+compose |
pf-compose-service |
build ./pf-compose-service |
3031:3030 |
Bun + Puppeteer/Chromium |
pf-face-service |
build ./pf-face-service |
3011:3011 |
Rust 单二进制 |
pf-imgproxy |
darthsim/imgproxy:latest |
3020:8080 |
动态 WebP 缩略图 |
pf-frpc |
fatedier/frpc:v0.61.1 |
host 网络 | 内网穿透客户端 |
各后端镜像基础层:pf-service / pf-face-service 用 debian:trixie-slim(拷预编译二进制);pf-compose-service 用 ghcr.io/puppeteer/puppeteer:24.4.0 + 中日韩字体。
pf_img_cache/pf_raw_cache不是 Docker 卷,而是 Nginx 的proxy_cache_path磁盘缓存区(见下)。
imgproxy + Nginx 边缘缓存
imgproxy(darthsim/imgproxy:latest,host 3020→8080)关键 env:IMGPROXY_ENFORCE_WEBP=true、IMGPROXY_AUTO_ROTATE=true、IMGPROXY_QUALITY=80、IMGPROXY_MAX_SRC_RESOLUTION=50(MP)、IMGPROXY_READ_TIMEOUT=15、IMGPROXY_PROCESSING_TIMEOUT=10。
⚠️ imgproxy 当前未配置签名(无
IMGPROXY_KEY/SALT,Nginx 走/imgproxy/unsafe/)——任何人可请求任意变换。若对外暴露,建议开启签名。
Nginx(源文件 deploy/aliyun-nginx.conf → 部署到 ECS /etc/nginx/conf.d/printing-factory.conf,域名 pf.wangijun.com,HTTP→HTTPS 301,client_max_body_size 50M):
1 | # 两块磁盘缓存区,均 levels=1:2, inactive=30d, use_temp_path=off |
原图与缩略图分两块缓存区,避免 2–10MB 原图挤掉缩略图。
FRP 内网穿透
deploy/frpc.toml:server 123.57.205.60:7000;隧道 pf-service-api 3001→12016、pf-imgproxy 3020→12020,MinIO 1280→12002。Nginx 通过这些 127.0.0.1 端口回源到 WSL 容器。
部署脚本
全部在 deploy/(PowerShell),配套沉淀了部署 Skill(.agents/skills/deploy-printing-factory/SKILL.md):
| 脚本 | 作用 |
|---|---|
deploy.ps1 |
统一入口,-Target all/frontend/backend/<svc>/nginx/frpc(可组合)+ -VerifyOnly;Run-HealthCheck 探 7 个端点 |
build-and-sync-wsl.ps1 |
后端编译 + 同步 WSL + docker compose up。pf-service:bun build --compile --target=bun-linux-x64;compose:bun run build + Bun 编译(含 public/);face(Rust):docker run rust:latest cargo build --release 后拷 target/release + models/ |
deploy-frontend.ps1 |
三前端 bun run build → tar|ssh 推到 ECS /usr/app/webpage(pf-manage 用 dist,pf-editor/pf-app 用 dist/spa) |
aliyun-nginx.conf / frpc.toml / minio-cors.json |
Nginx / FRP / MinIO CORS 配置 |
后端二进制在 WSL 内用容器交叉编译,镜像本身不含编译链——启动快、镜像小。
通用部署要点(自建其它拓扑时)
若不用 WSL+FRP,也可把后端三服务 + imgproxy 全放一台机器 + 反向代理:
- API 组 vs 合成 Worker:
pf-service可多实例并行(FOR UPDATE SKIP LOCKED天然支持);把jobs.composition.enabled=false的实例做纯 API 组、true的做调度组。渲染压力已在pf-compose-service,按需扩其副本/Worker 数。 - 合成服务:
ghcr.io/puppeteer/puppeteer基础镜像已带 Chromium 系统库;给它单独 cgroup 限内存,maxUsesPerWorker控制 Browser 重启防泄漏。 - 人脸服务:Rust 单二进制 +
libgomp1/libstdc++6,/health探活。 - 对象存储:生产建议多节点 MinIO(Erasure Code)或直接 S3/OSS/COS,改各服务
s3段。 - 数据库:PostgreSQL 主从(读多写少);
order_template_submission_items增长最快,按createdAt归档。备份pg_dump+ WAL 归档。 - Redis:Cluster / Sentinel,别用单点。
静态前端发布
1 | bun run --filter @wangijun/pf-app build # dist/spa |
注意 SPA base:pf-manage=/manage/、pf-editor=/editor/、pf-app=/app/,Nginx try_files $uri /index.html;后端地址通过各自 .env 注入。
监控与告警
| 指标 | 来源 | 参考阈值 |
|---|---|---|
| HTTP 5xx 率 | Nginx | > 1% 持续 5min |
| API P95 延迟 | pf-service | > 1s |
| 合成 pending 队列长度 | DB query | > 100 持续 10min |
| 合成失败率 / 单条耗时 P95 | DB / compose 日志 | > 5% / > 30s |
compose /health workers.waiting |
探针 | 持续 > 0 |
face /health |
探针 | 非 ok 任何时长 |
| MinIO 可用空间 | exporter | < 20% |
Nginx X-Cache-Status HIT 率 |
日志 | 缩略图应高命中 |
安全 checklist
- pf-service / compose / face 只暴露给内部代理(FRP/内网)
- imgproxy 生产开启签名(
IMGPROXY_KEY/SALT) - MinIO 私有桶 + 预签名 URL,禁止公开列举
- JWT/S3/FRP 密钥放 secret manager,不入仓(当前
config.json/frpc.toml有明文默认值,仅供开发) - CORS 白名单生产收紧
- 上传白名单:MIME + 文件头双重校验
- 合成服务只渲染受信内嵌页,不渲染外网任意 URL