Files
Genarrative/deploy/container
lhk229 27f84fc6a7
Project CI / Frontend tests (pull_request) Successful in 1m43s
Project CI / Backend tests (pull_request) Successful in 7m28s
Project CI / Repository checks (pull_request) Failing after 2m26s
Project CI / Native shell tests (pull_request) Successful in 6m19s
完善 BgFilter 失败审计与权威文档
补充已发出 provider 请求的失败审计判定,并由 shutdown tracker 排空异步审计任务。

统一父流程、BgFilter worker、重试、超时、fallback 与结果持久化的架构和编辑器文档口径。

修正本地 dev 进程角色、live smoke 端口、OTEL identity、journal 与端口路由 skill。

明确当前容器拓扑不包含 BgFilter worker,仅覆盖非 BgFilter 路径和 unsupported queue smoke。
2026-07-22 04:24:11 +00:00
..

Genarrative 容器化压测与隔离部署方案

本目录只服务本机或预发的容器化模拟压测,不替换当前生产 systemd + Nginx + Jenkins 发布路径。当前 compose 不包含独立 bgfilter-worker,因此不是完整 BgFilter 预发拓扑,也不覆盖会触发 BgFilter 的现役任务;这里只验证非 BgFilter 路径,或使用 unsupported job 检查队列 claim / fail 回写和 API / worker 进程隔离。生产服务器仍以 deploy/systemd/deploy/nginx/scripts/jenkins-*.shscripts/deploy/production-api-deploy.sh 为准。

拓扑

Docker Compose
├─ spacetimedb  :3101,独立数据卷,供 api-server 连接
├─ nginx        :80 -> api-server:8082,负责静态站点、/admin/、/api/ 反代、upstream timing log、连接限制
├─ api-server   :8082Linux release 构建,连接 compose 内 SpacetimeDB
├─ external-generation-worker,独立 worker 进程,消费 external_generation_job 队列
└─ otelcol      :4317/4318debug exporter,接收 traces / metrics / logs

当前容器模拟参数按 genarrative-release 服务器采样值收口为 2 vCPU / 2 GiB RAM / 4096 soft nofile / 768 worker_connections,并已在 compose 里落实到 spacetimedb cpus=1.0 mem_limit=896mapi-server cpus=2.0 mem_limit=1gexternal-generation-worker cpus=2.0 mem_limit=1gnginx cpus=0.5 mem_limit=128motelcol cpus=0.25 mem_limit=128m。SpacetimeDB 同时设置 --page_pool_max_size=402653184,给 reducer、订阅与运行时保留更多非 page pool 内存。 容器 api-server 默认 GENARRATIVE_API_WORKER_THREADS=4,用于让 Tokio 在 2 vCPU 配额内有更多 I/O 调度 worker;该值不会突破 compose 里的 cpus=2.0 CPU 上限。 容器默认 GENARRATIVE_EXTERNAL_GENERATION_MODE=queue,用于验证不经过 BgFilter 的 api-server -> external_generation_job -> external-generation-worker 链路;会触发 BgFilter 的任务不属于当前 compose 验收范围。如只想本地同步排查非 BgFilter provider / OSS / SpacetimeDB 写回,可在本机 env 临时改为 inline,但该模式不会覆盖 worker 动态扩缩容验证。 Collector 镜像使用 otel/opentelemetry-collector-contrib:0.151.0。 生产服务器若启用 Collector,则由 deploy/systemd/otelcol-contrib.servicedeploy/otelcol/genarrative-debug.yaml 托管,不走容器镜像。

默认 host 端口:

  • http://127.0.0.1:13101:容器 SpacetimeDB。
  • http://127.0.0.1:18080:容器 Nginx。
  • 127.0.0.1:4317 / 127.0.0.1:4318:容器 Collector OTLP gRPC / HTTP。

如端口冲突,可设置:

$env:GENARRATIVE_CONTAINER_SPACETIME_PORT="13102"
$env:GENARRATIVE_CONTAINER_HTTP_PORT="18081"
$env:GENARRATIVE_CONTAINER_OTLP_HTTP_PORT="14318"
$env:GENARRATIVE_CONTAINER_OTLP_GRPC_PORT="14317"

初始化

npm run container:init

该命令会从 deploy/container/api-server.env.example 生成本地 deploy/container/api-server.env。真实 token、库名和外部服务密钥只写本地 env 文件,不提交 Git。

Docker Desktop 下默认通过 http://spacetimedb:3101 连接 compose 内 SpacetimeDB;宿主机只负责用 CLI 发布模块:

GENARRATIVE_SPACETIME_SERVER_URL=http://spacetimedb:3101
GENARRATIVE_SPACETIME_DATABASE=genarrative-loadtest
GENARRATIVE_SPACETIME_TOKEN=

宿主机发布模块时,先用 CLI 向 http://127.0.0.1:13101 发布到 genarrative-loadtest,再启动 npm run container:up

Linux Docker Engine 若要从宿主机 CLI 连到容器内服务,直接用 http://127.0.0.1:13101;容器内部服务之间统一走 http://spacetimedb:3101

构建工具链

api-server 容器镜像只构建 Linux release API 二进制,不构建 spacetime-module。当前 api-server -> spacetime-client -> spacetimedb-sdk 2.6.1 依赖链要求 Rust 1.93,因此 deploy/container/api-server.Dockerfile 的 Rust builder 固定为 rust:1.93-bookworm。镜像构建阶段会同时复制 public/,用于满足 API 二进制里 include_bytes! 引用的内置素材;不要把 public/generated-* 放入镜像上下文。如果本机 Docker Hub 拉取失败,可以先在本机准备同名本地 builder 镜像,但不要把临时 bootstrap 容器或私有 registry 凭据写入仓库。

启动与验证

npm run container:config
npm run container:build
npm run container:up -- spacetimedb
spacetime publish genarrative-loadtest --server http://127.0.0.1:13101 --module-path server-rs/crates/spacetime-module --yes --build-options="--debug"
npm run container:up
npm run container:ps
curl -sS -H 'Authorization: Bearer <access-token>' \
  'http://127.0.0.1:18080/api/assets/history?kind=character_visual'

查看日志:

npm run container:logs -- nginx
npm run container:logs -- api-server
npm run container:logs -- external-generation-worker
npm run container:logs -- otelcol

npm run container:config 默认只校验配置,不打印完整 env。排查 compose 展开结果时可临时使用:

npm run container:config -- --print

如果 deploy/container/api-server.env 已写入真实 token,不要把完整展开结果贴到公开渠道。

动态扩缩容外部生成 worker 时,只调整 external-generation-worker service

npm run container:up -- --scale external-generation-worker=3 external-generation-worker
npm run container:up -- --scale external-generation-worker=1 external-generation-worker

动态扩缩容验证必须保持 GENARRATIVE_EXTERNAL_GENERATION_MODE=queueinline 模式下生成请求由 api-server 同步执行,不会被这些 worker 实例消费。

外部生成 Worker 隔离 Smoke

如果只想在本机隔离验证 worker 模式,不复用 deploy/container/api-server.env,使用专用脚本:

npm run container:worker-smoke -- smoke

该脚本会生成 gitignored 的 deploy/container/worker-smoke/api-server.env 与端口 state,使用独立 compose project、独立 SpacetimeDB 数据卷和独立 host 端口,完成 build -> up-spacetime -> publish -> up -> enqueue -> api-update -> enqueue。测试 job 使用 worker_smoke_unsupported 类型,不访问真实 VectorEngine、LLM 或 OSS;预期结果是 worker 领取队列任务后按“不支持的任务类型”执行失败分支,从而验证队列 claim、lease、失败回写路径和 API / worker 进程隔离。external_generation_job 是 private table,脚本通过 worker 日志里的 job_id 和 unsupported 记录确认消费,不通过 CLI SQL 绕过权限。smoke 默认只启动 api-serverexternal-generation-worker,避免无关前端 / Nginx 镜像构建;需要同时验证 Nginx 时可分步执行 up --with-nginx

分步排查时可执行:

npm run container:worker-smoke -- init --force
npm run container:worker-smoke -- build
npm run container:worker-smoke -- up-spacetime
npm run container:worker-smoke -- publish
npm run container:worker-smoke -- up
npm run container:worker-smoke -- enqueue before-update
npm run container:worker-smoke -- api-update
npm run container:worker-smoke -- enqueue after-update
npm run container:worker-smoke -- status

如果隔离端口或库数据需要重置:

npm run container:worker-smoke -- smoke --force

container:worker-smoke 默认会把本机 spacetime 2.6.1 CLI 打成轻量 SpacetimeDB 镜像,避免首次 smoke 必须拉取官方大镜像;普通 npm run container:* 压测默认使用 clockworklabs/spacetime:v2.6.1。如果 Docker build 阶段在容器内拉取 crates.io 依赖不稳定,可让容器内 Cargo 复用本机 Cargo 缓存构建当前二进制,再打入临时 smoke 镜像。该模式默认使用 rust:1.93-bookworm 作为 builder、Debian bookworm smoke runtime 承载构建产物;需要换 builder 镜像时设置 GENARRATIVE_WORKER_SMOKE_CARGO_IMAGE,需要换运行时基础镜像时设置 GENARRATIVE_WORKER_SMOKE_LOCAL_BASE_IMAGE

npm run container:worker-smoke -- smoke --local-binary

api-update 只会 --force-recreate api-server,并校验 external-generation-worker 容器 ID 不变;如要同时重建 API 镜像,使用:

npm run container:worker-smoke -- api-update --build

验证 worker 动态扩缩容:

npm run container:worker-smoke -- scale 3
npm run container:worker-smoke -- ps
npm run container:worker-smoke -- enqueue scaled-workers
npm run container:worker-smoke -- scale 1

查看或清理隔离环境:

npm run container:worker-smoke -- logs external-generation-worker
npm run container:worker-smoke -- down -v

停止:

npm run container:down

如需同时清理容器卷:

npm run container:down -- -v

历史压测

旧作品列表 / gallery 的 compose k6 profile 与 container:k6 命令已经随模板业务退役。scripts/loadtest/ 仅保留历史脚本和脱敏样例,不进入当前运行入口;新的容量验收必须针对现役编辑器、项目或素材接口单独设计,不能复用旧作品接口结论。

内存采样

排查 API 容器内存时,优先对比压测前后的 /proc/$pid/smaps_rollup 和 cgroup 当前/峰值,不把 Windows 任务管理器总占用当成单进程结论:

docker exec genarrative-container-loadtest-api-server-1 sh -c 'pid=$(pidof api-server); grep VmRSS /proc/$pid/status; grep RssAnon /proc/$pid/status; cat /proc/$pid/smaps_rollup | grep Anonymous; echo cgroup_current=$(cat /sys/fs/cgroup/memory.current); echo cgroup_peak=$(cat /sys/fs/cgroup/memory.peak)'

/healthz 也能复现的内存尖峰应先按连接层、service clone 或 allocator 高水位排查,不要直接归因到 SpacetimeDB procedure、作品列表 cache 或业务 DTO。2026-05-18 验证:AppState 改为 Arc<AppStateInner> 浅拷贝后,容器内直连 api-server:8082/healthz 的 500 HTTP req/s、PREALLOCATED_VUS=100、30 秒压测完成 15001 次请求,http_req_failed=0dropped_iterations=0API 进程 RSS 从约 18 MiB 升至约 52 MiBcgroup 峰值约 47 MiB,未再出现 1 GiB 级尖峰。

OTLP

容器内 otelcol 默认使用 debug exporter。开启 api-server OTEL

GENARRATIVE_OTEL_ENABLED=true
OTEL_EXPORTER_OTLP_ENDPOINT=http://otelcol:4318

然后重建或重启容器:

npm run container:up
npm run container:logs -- otelcol

Collector 日志会输出 traces / metrics / logs。接 Rider、Jaeger、Tempo、Prometheus、Grafana 或托管平台时,另建独立 Collector 配置,不直接改生产 systemd 或 Nginx 模板。

容器内需要临时转发到 Grafana Cloud 时,切换 Collector 配置并从当前 shell 传入 Grafana Cloud 凭据;真实 token 不写入仓库文件:

$env:GENARRATIVE_CONTAINER_OTELCOL_CONFIG="./otelcol.grafana.yaml"
$env:GRAFANA_CLOUD_OTLP_ENDPOINT="https://..."
$env:GRAFANA_CLOUD_BASIC_AUTH_HEADER="Basic ..."
npm run container:up
npm run container:logs -- otelcol

deploy/container/otelcol.grafana.yaml 会同时保留本地 debug exporter,并通过 otlphttp/grafana 把 traces / metrics / logs 发到 Grafana Cloud。

隔离边界

  • 不改生产 systemd 单元。
  • 不改 Jenkins 发布主流程。
  • 不要求真实 HTTPS 证书。
  • 不把真实 .env.env.local.env.secrets.localdeploy/container/api-server.env 放入 Docker build context。
  • 不在容器镜像里内置 SpacetimeDB 数据或 token。