71811f04c3
Nginx 按真实连接地址让内网绕过全站维护闸 Pingora 统一内网维护放行并拒绝 XFF 伪造 补齐公网与内网维护回归测试和运维文档
Genarrative Nginx compression policy
本配置片段由 scripts/jenkins-server-provision.sh 在安装 Nginx 站点配置时展开。
请求体大小
- 生产、开发服和容器模板都在通用
location ~ ^/api(?:/|$)内设置client_max_body_size 64m。 - 该值只用于让携带参考图 Data URL 的创作接口抵达
api-server;不要把它当作业务上传上限。Rust 路由仍通过DefaultBodyLimit和解码后字节校验限制具体接口,例如拼图参考图路由只放宽到 12 MiB 请求体,图片字节继续按业务规则拒绝。 - 若线上看到
413 Request Entity Too Large,并且 access log 里request_time=0.000 upstream_status=-,通常是 Nginx 没有加载该模板或未 reload;先执行nginx -T | grep client_max_body_size和nginx -t再检查api-server。
gzip
deploy/nginx/genarrative.conf与deploy/nginx/genarrative-dev-http.conf默认开启 gzip。- 覆盖
application/json,用于降低/api/runtime/*/gallery这类 JSON 列表接口的公网带宽占用。 - 当前推荐等级为
gzip_comp_level 5,兼顾 2C/2G 服务器 CPU 与压缩收益。 - Pingora 影子网关已通过
GENARRATIVE_PINGORA_GATEWAY_COMPRESSION_ALGORITHMS=gzip、GENARRATIVE_PINGORA_GATEWAY_GZIP_ENABLED=true、GENARRATIVE_PINGORA_GATEWAY_GZIP_LEVEL=5与GENARRATIVE_PINGORA_GATEWAY_GZIP_MIN_LENGTH_BYTES=1024承接 gzip parity;网关会把压缩算法收敛到 gzip allowlist,并按gzip_min_length 1024避免压缩小响应,同时沿用 Nginxgzip_types边界不压缩图片资源。改动后必须运行npm run check:pingora-gateway-smoke验证小响应不压缩、图片资源不压缩,以及Accept-Encoding: gzip和Accept-Encoding: br, gzip的大响应都返回 gzip。
Brotli
- Brotli 只在目标服务器 Nginx 接受 brotli 指令时开启。
- Pingora 当前正式化口径固定为 gzip-only,不承接 Brotli;做 Nginx -> Pingora canary 时,Brotli 仍由 Nginx 或更前置代理决定是否启用。直连 Pingora 时,不把 Brotli parity 作为切换门禁,避免使用 Pingora 0.8.1 里尚未通过可解压端到端验证的 Brotli compressor 路径。
- Ubuntu / apt 系统的
Genarrative-Server-Provision会安装libnginx-mod-http-brotli-filter与libnginx-mod-http-brotli-static;非 apt 系统暂不自动安装,仍按下面的能力探测结果决定是否启用。 - Provision 脚本通过临时配置执行
nginx -t做能力探测;探测配置会先include /etc/nginx/modules-enabled/*.conf,避免 Ubuntu 动态模块已安装但测试配置未加载模块导致误判。可用时把模板中的# __GENARRATIVE_BROTLI_DIRECTIVES__替换为 brotli 指令,不可用时保留注释说明。 - 不要直接在静态模板里无条件写
brotli on;,否则没有 brotli 模块的服务器会nginx -t失败并回滚。 - 不要用
nginx -V | grep brotli判断 brotli 是否可用;Ubuntu apt 安装的 brotli 是动态模块,不会出现在普通编译参数里。应检查包、/etc/nginx/modules-enabled/的load_module配置,或用包含include /etc/nginx/modules-enabled/*.conf的临时配置执行nginx -t。
Pingora upstream timeouts
- Pingora 影子网关已显式配置上游连接 / 读 / 写 timeout,避免正式 canary 时依赖 Pingora 默认值。
- 默认连接超时为
3000ms;没有 Nginx 显式长超时的代理路由读取超时为60s。 - 通用
/api、公开列表 / 详情和 SpacetimeDB subscribe 读取超时为3600s,写上游超时为3600s,对齐当前 Nginxproxy_read_timeout/proxy_send_timeout口径。 - 超时会返回 JSON
504 GATEWAY_UPSTREAM_TIMEOUT,由npm run check:pingora-gateway-smoke覆盖。
Pingora static cache policy
- Pingora 直连静态响应会显式写入
Cache-Control,避免公网入口切换后 HTML 入口页或 Vite 指纹资源依赖框架默认头。 - 默认
GENARRATIVE_PINGORA_GATEWAY_HTML_CACHE_CONTROL=no-cache,覆盖index.html、admin/index.html、目录 index 和 SPA fallback。 - 默认
GENARRATIVE_PINGORA_GATEWAY_ASSET_CACHE_CONTROL=public, max-age=31536000, immutable,只覆盖/assets/*与/admin/assets/*中带 Vite 指纹文件名的资源;非指纹静态和 ACME challenge 默认GENARRATIVE_PINGORA_GATEWAY_STATIC_CACHE_CONTROL=no-cache。 - Pingora 静态响应同时写入弱
ETag、Last-Modified与Accept-Ranges: bytes,并对If-None-Match/If-Modified-Since返回304;这层只负责浏览器协商缓存,不改变上面的Cache-Control分档。 - Pingora 静态文件支持单段
Range: bytes=:合法范围返回206与Content-Range,越界范围返回416与Content-Range: bytes */<len>,HEAD + Range只返回头。多段 range 暂按完整文件返回,不启用 multipart 响应。If-Range日期匹配时继续返回局部内容,日期旧于文件或弱 ETag 校验器时回完整200。206/304/416均不做 gzip 压缩,避免局部内容语义和Content-Range漂移。 - Pingora 静态路由只允许
GET/HEAD。非读取方法命中静态候选时返回405与Allow: GET, HEAD;缺失文件仍返回404,避免错误客户端把静态入口当作可写接口。 npm run check:pingora-gateway-smoke会用固定X-Request-Id对账静态304/405/206/416的 Pingora access log 行,并同时校验 method / path / status;该 smoke 还会让 direct live 自动发现首页里的普通静态资源和 Vite 指纹资源,确认指纹资源 GET / HEAD /If-None-Match304 /Range: bytes=0-0以及public, max-age=31536000, immutable缓存头,确保本地静态响应状态和指纹 chunk 长缓存都可进入直连切换证据链。direct live JSON 只给静态 GET / HEAD / 304 / Range 写入白名单headers,包括cache-control、etag、last-modified、accept-ranges、content-range、content-length和content-encoding,便于证据包归档后复盘静态缓存与 Range 响应头。
Pingora TLS / redirect
- Pingora 网关默认仍只监听本机 shadow 端口,不绑定公网
80/443。 - 需要评估直连入口时,显式配置
GENARRATIVE_PINGORA_GATEWAY_TLS_LISTEN、GENARRATIVE_PINGORA_GATEWAY_TLS_CERT_FILE和GENARRATIVE_PINGORA_GATEWAY_TLS_KEY_FILE;网关只读取现有证书文件,不负责 Certbot / ACME 自动续期。 - 需要 HTTP 到 HTTPS 跳转时,再显式配置
GENARRATIVE_PINGORA_GATEWAY_HTTP_REDIRECT_LISTEN;该入口除/.well-known/acme-challenge/*外统一 301 到 HTTPS,ACME challenge 仍从GENARRATIVE_PINGORA_GATEWAY_ACME_ROOT精确读取。 - 同一公网 IP 上还承载
git.genarrative.world时,直连前必须配置GENARRATIVE_PINGORA_GATEWAY_GITEA_HOSTS=git.genarrative.world与GENARRATIVE_PINGORA_GATEWAY_GITEA_UPSTREAM=127.0.0.1:3000,让命中 Gitea Host 的请求整站转发到 Gitea;该路由不走应用维护页、API body limit 或网关接流保护,避免影响 git clone / push。当前 Pingora TLS listener 只加载一组 cert/key;如果直连 443 同时服务dev.genarrative.world和git.genarrative.world,证书必须同时覆盖两个域名,不能使用只有单域名 SAN 的证书。 - API release 必须把
api-server.sha256、可选pingora-gateway.sha256、scripts/check-pingora-release-readiness.mjs、scripts/check-pingora-canary-live.mjs、scripts/check-pingora-canary-access-log-parity.mjs、scripts/ops/pingora-current-release-audit.mjs、scripts/ops/pingora-direct-rehearsal-status.mjs、scripts/ops/pingora-cutover-status-snapshot.mjs、scripts/ops/pingora-cutover-evidence-bundle.mjs、scripts/check-pingora-direct-preflight.mjs、scripts/check-pingora-direct-live.mjs、scripts/check-production-health-patrol-env.mjs、scripts/deploy/pingora-health-patrol-env-switch.mjs、scripts/deploy/pingora-gateway-env-shadow-switch.mjs、scripts/deploy/pingora-tls-cert-sync.mjs、deploy/systemd/、deploy/env/和deploy/pingora/一起复制到/opt/genarrative/current;current release 自审、直连彩排状态、直连启用、health patrol env 切换、Pingora gateway env shadow 切换、TLS 证书同步、canary live、access log 对账、cutover 状态快照和证据包脚本都从 current release 推导依赖路径,切换窗口不得依赖 Jenkins 工作区或目标机源码 checkout。 - 目标机如果使用 Certbot / Let’s Encrypt 证书,不要直接放宽
/etc/letsencrypt/live或archive的目录 / 私钥权限。先用随包脚本把 root 可读的 live symlink 解析并复制到 Pingora 私有目录,例如sudo -n node -- /opt/genarrative/current/scripts/deploy/pingora-tls-cert-sync.mjs --apply --source-cert-file /etc/letsencrypt/live/<域名>/fullchain.pem --source-key-file /etc/letsencrypt/live/<域名>/privkey.pem --target-dir /etc/genarrative/pingora-tls/<域名>;脚本默认 dry-run,--apply才原子写入fullchain.pem/privkey.pem,目标目录默认root:genarrative 0750,文件默认root:genarrative 0640,并复核genarrative服务用户可读。随后把GENARRATIVE_PINGORA_GATEWAY_TLS_CERT_FILE/TLS_KEY_FILE指向/etc/genarrative/pingora-tls/<域名>/下的副本。 - 默认 systemd service 以
genarrative非 root 用户运行,不具备绑定80/443的能力。Server-Provision 只安装/etc/genarrative/pingora/genarrative-pingora-gateway-direct-entry.conf作为人工启用模板;切直连低端口时必须先确认genarrative可读证书文件、Nginx 已释放80/443,再执行/opt/genarrative/current/scripts/deploy/pingora-direct-enable.sh --apply --preflight-env-file /etc/genarrative/pingora-gateway.env --preflight-check-cert-readable --preflight-check-service-env-file --preflight-check-service-user-cert-readable --preflight-check-service-binary-executable --preflight-check-ports-free --direct-https-base-url https://127.0.0.1 --direct-http-base-url http://127.0.0.1 --direct-host <域名> --direct-redirect-host <域名或host:port> --direct-spacetime-database <库名> --direct-pingora-access-log /var/log/genarrative/pingora-gateway.access.log;脚本会先执行随包 current release 自审,确认pingora-gateway可执行且 systemdExecStart指向 current release,失败时不会安装 drop-in。自审通过后才安装 drop-in、systemctl daemon-reload并重启 Pingora;脚本会用systemctl cat确认CAP_NET_BIND_SERVICEdrop-in 已进入 systemd 最终配置,用systemctl show ... ExecStart确认最终 service 指向随包主 service 模板中的 current releasepingora-gateway,用systemctl is-active确认 Pingora active,并执行 direct live smoke,强制覆盖 HTTPS / HTTP redirect / ACME / WSS 101 和 Pingora access log request_id 落盘。 npm run check:pingora-gateway-smoke会用临时自签证书覆盖 TLS 直连、HTTP redirect 和 WSS subscribe 行为;正式公网切换前仍必须先走 Nginx canary 和 release readiness 门禁。- 目标机直连入口启用前先运行
npm run check:pingora-direct-preflight -- --env-file /etc/genarrative/pingora-gateway.env --require-live-env --systemd-cat --check-cert-readable --check-service-env-file --check-service-user-cert-readable --check-service-binary-executable --check-ports-free,证明 TLS 文件、service env、current release 二进制和低端口释放都满足切流条件;启用后再运行npm run check:pingora-direct-live,检查 HTTPS 根路径、HTTP/2 ALPN、代表性 API、SpacetimeDB identity、WSS subscribe 握手、generated 拒绝、公开/healthz拒绝、可选 shadow probe,以及 HTTP 301 / ACME challenge;真实 SpacetimeDB 对GET /v1/identity返回405 Method Not Allowed属于可接受语义,direct live 只把它作为路径转发代表,不要求该 GET 创建 identity;release readiness 可用--require-direct --direct-https-base-url https://127.0.0.1 --direct-http-base-url http://127.0.0.1 --direct-host <域名> --direct-redirect-host <域名或host:port> --direct-spacetime-database <库名> --direct-pingora-access-log /var/log/genarrative/pingora-gateway.access.log --direct-health-patrol-env-file /etc/genarrative/health-patrol.env --direct-preflight-env-file /etc/genarrative/pingora-gateway.env --direct-preflight-systemd --direct-preflight-check-cert-readable --direct-preflight-check-service-env-file --direct-preflight-check-service-user-cert-readable --direct-preflight-check-service-binary-executable强制纳入同一条启用后门禁,漏掉 direct HTTP base URL、正式域名 Host/SNI、redirect Location host、目标 SpacetimeDB 库、Pingora access log request_id 落盘、health patrol env direct 模式复核、systemd drop-in 生效检查、service EnvironmentFile 一致性检查、当前用户 / systemd 服务用户证书可读检查或 service 二进制可执行检查都会直接失败;启用后--require-direct复核不再要求端口空闲,因为此时80/443应由 Pingora 占用。高端口 rehearsal 如果通过https://127.0.0.1:18443打入、但 Pingora 应返回正式域名默认 HTTPS Location,可额外传--direct-redirect-base-url https://<域名>;--direct-redirect-host仍必须保留,用于直连切换 Host 一致性约束。 - direct live 在 HTTPS 根路径返回
200且 HTML 中发现/assets/或/admin/assets/引用时,会自动请求该静态资源,确认Cache-Control、ETag、Last-Modified、Accept-Ranges: bytes、HEAD头响应、If-None-Match/If-Modified-Since的304响应和Range: bytes=0-0的206 + Content-Range响应,并纳入 Pingora access log method/path/status 对账;若首页引用 Vite 指纹资源,还会额外确认Cache-Control: public, max-age=31536000, immutable以及指纹资源 GET / HEAD / 304 / Range access log 证据。维护模式、非 HTML 或发布包首页没有资产引用时该项标记为 skipped,不阻断维护窗口。 - 直连启用前先 dry-run
/opt/genarrative/current/scripts/deploy/pingora-direct-enable.sh --no-status;验证失败时执行/opt/genarrative/current/scripts/deploy/pingora-direct-rollback.sh --apply --reload-nginx --nginx-smoke-url https://<域名>/ --nginx-smoke-expect-body '<!doctype html>',脚本会先运行nginx -t,通过后移除 direct-entry drop-in、重启 Pingora,用systemctl cat确认CAP_NET_BIND_SERVICE已从 systemd 最终配置中移除,用systemctl show ... ExecStart确认最终 service 仍指向随包主 service 模板中的 current releasepingora-gateway,并 reload Nginx、确认 Nginx service 仍为active,最后用 smoke URL 证明 Nginx 入口真实可访问;传入--nginx-smoke-expect-body时还会要求响应体包含该片段。回退 smoke 的 URL 与 body 必须来自切换前真实 Nginx 入口,不要继续用固定http://127.0.0.1/healthz与"ok":true;若 smoke URL 打本机地址,再显式追加--nginx-smoke-host <域名>避免命中默认 vhost。仓库工作区可用npm run deploy:pingora-direct-enable -- --apply --preflight-env-file /etc/genarrative/pingora-gateway.env --preflight-check-cert-readable --preflight-check-service-env-file --preflight-check-service-user-cert-readable --preflight-check-service-binary-executable --preflight-check-ports-free --direct-https-base-url https://127.0.0.1 --direct-http-base-url http://127.0.0.1 --direct-host <域名> --direct-redirect-host <域名或host:port> --direct-spacetime-database <库名> --direct-pingora-access-log /var/log/genarrative/pingora-gateway.access.log和npm run deploy:pingora-direct-rollback -- --apply --reload-nginx --nginx-smoke-url https://<域名>/ --nginx-smoke-expect-body '<!doctype html>'。两个脚本默认 dry-run;启用脚本--apply必须先通过 current release 自审,并带 env、证书可读、service EnvironmentFile 一致性、服务用户证书可读、service 二进制可执行、端口释放预检、direct live 参数和 Pingora access log 参数,回退脚本--apply必须带--reload-nginx和--nginx-smoke-url;如果 health patrol env 已预先切回 Nginx,也可追加--health-patrol-env-file /etc/genarrative/health-patrol.env --health-patrol-expected-public-base-url <切换前Nginx巡检入口> --health-patrol-require-empty-public-host让回退脚本在 Nginx smoke 后复核gateway mode=nginx且 public base URL / Host 已恢复,若切换前 Nginx 巡检本来需要 Host 覆盖,则把最后一项换成--health-patrol-expected-public-host <切换前Host>;如果还要同时证明 shadow 高端口仍健康,可追加--pingora-shadow-probe-url http://127.0.0.1:18081/__genarrative_pingora/healthz --pingora-shadow-probe-token <token>,脚本会隐藏 token 并要求响应包含gateway=pingora-shadow。本机npm run check:pingora-direct-enable/npm run check:pingora-direct-rollback会验证 dry-run 不会安装或删除临时 drop-in、current release 自审失败时启用脚本不会安装 drop-in,并展示启用 / 回退后的 systemd 最终配置核验、ExecStart 指向核验、Pingora active 核验、direct live smoke、Nginx 语法检查、reload 状态核验、Nginx smoke 及响应体片段核验、可选 health patrol env 复核和可选 shadow probe 复核。 - rollback apply 前必须先执行
node -- /opt/genarrative/current/scripts/deploy/pingora-gateway-env-shadow-switch.mjs --apply --env-file /etc/genarrative/pingora-gateway.env,把 active Pingora env 恢复为GENARRATIVE_PINGORA_GATEWAY_LISTEN=127.0.0.1:18081,并清空TLS_LISTEN、HTTP_REDIRECT_LISTEN、TLS_CERT_FILE和TLS_KEY_FILE;只移除 systemd direct-entry drop-in 而不恢复 env,会让回退后的 Pingora shadow 继续按 direct 低端口或半 TLS 配置启动。 npm run check:pingora-current-release-audit会烟测 current release 自审脚本;正式直连 runbook 会先执行/opt/genarrative/current/scripts/ops/pingora-current-release-audit.mjs --release-root /opt/genarrative/current --require-pingora-gateway --systemd-show,只读确认发布包自包含、api-server.sha256/pingora-gateway.sha256匹配、release-manifest.api-server.json已登记pingora-gateway、pingora-gateway可执行且 systemdExecStart指向 current release。npm run check:pingora-cutover-status-snapshot会烟测状态快照脚本,npm run check:pingora-cutover-evidence-bundle会烟测证据包脚本;正式切换窗口与 release readiness 使用/opt/genarrative/current/scripts/ops/pingora-cutover-evidence-bundle.mjs归档切换前 / 启用后 / 回退后三个阶段的manifest.json、snapshot.json、stdout / stderr 和命令记录。直连 runbook 的三个证据包阶段都会向状态快照透传--require-pingora-gateway,在checks.current-release-audit中保存 Pingora 二进制、sha256、release manifest 和 systemdExecStart自审结果;启用后证据包还会通过--run-direct-live保存direct-live.json、direct live stdout / stderr、命令记录、Pingora access logrequest_id反查结果,以及静态响应头白名单证据。post-enable证据包必须传--expected-pingora-env-mode direct,post-rollback证据包必须传--expected-pingora-env-mode shadow。manifest.summary.directLiveAccessLog会提升 checked / matched / missing / mismatch 对账摘要,manifest.summary.directLiveStaticHeaders会提升普通静态和 Vite 指纹静态的缓存头、校验头、RangeContent-Range和 304 状态摘要,manifest.summary.pingoraEnvShadow会提升listen、tlsListen、httpRedirectListen、tlsCertFile、tlsKeyFile、mode和shadowReady,便于值班人员不打开direct-live.json或snapshot.json也能先判断证据是否完整;如果 direct live 已输出静态资产结果但摘要缺少缓存头、校验头、Range206 + Content-Range、ETag 304 或 Last-Modified 304 证据,证据包会记为CRITICAL。最终总审计还会带--require-phase-direct-live-access-log post-enable --require-phase-direct-live-static-headers post-enable --require-phase-pingora-env-shadow post-rollback,防止缺少 access log 对账摘要、静态头摘要或回退后 shadow env 摘要的旧证据包混入正式归档。快照脚本只读采集状态,证据包脚本只写指定证据目录,不写/etc、不 reload systemd,也不修改 Nginx 或 Pingora。- 最终证据根目录总审计 JSON 先看
summary.status、summary.failedItems[]和summary.directLiveEvidence[]:失败时这里会直接指出阶段 / 命令 / 根目录问题,以及post-enableaccess log 或静态头摘要的短 reason;phases[]、commands[]和timeline继续作为完整机器归档。 - 最终证据根目录总审计必须同时带五条
--require-command-executable:enable-apply:pingora-direct-enable-apply:/opt/genarrative/current/scripts/deploy/pingora-direct-enable.sh、post-enable:pingora-health-patrol-direct-env-switch:/opt/genarrative/current/scripts/deploy/pingora-health-patrol-env-switch.mjs、rollback-prep:pingora-gateway-shadow-env-switch:/opt/genarrative/current/scripts/deploy/pingora-gateway-env-shadow-switch.mjs、rollback-prep:pingora-health-patrol-nginx-env-switch:/opt/genarrative/current/scripts/deploy/pingora-health-patrol-env-switch.mjs和rollback-apply:pingora-direct-rollback-apply:/opt/genarrative/current/scripts/deploy/pingora-direct-rollback.sh,避免只证明有命令证据,却没有证明真实执行的是 current release 随包切换脚本。 npm run check:production-api-release会动态生成 API release,并检查发布包 README 与随包scripts/check-pingora-release-readiness.mjs --dry-run-cutover输出的最终总审计步骤都保留上述五条--require-command-executable要求,避免源码文档正确但发布包操作口径漂移。- 直连接管后用
node -- /opt/genarrative/current/scripts/deploy/pingora-health-patrol-env-switch.mjs --apply --env-file /etc/genarrative/health-patrol.env --gateway-mode pingora-direct --public-base-url <直连HTTPS入口> --public-host <域名>切换 health patrol env;该脚本只改 gateway mode、public base URL 和 public Host,并先用临时目标 env 调用随包check-production-health-patrol-env.mjs复核,复核通过后才原子替换真实 env,复核失败不会落盘。回退到 Nginx 前用同一脚本改回--gateway-mode nginx --public-base-url <切换前Nginx巡检入口>,若切换前 Nginx 巡检不需要 Host 覆盖则传--clear-public-host,需要 Host 覆盖则传--public-host <切换前Host>;随后 rollback 脚本和独立 env 复核会再次阻断 public base URL / Host 漂移。plan:pingora-direct-cutover会在 JSON runbook 中列出 direct / nginx 两个方向的 env 切换脚本和 env 复核步骤,并先输出 Host 与回退巡检入口确认;正式切换 runbook 会拒绝--direct-redirect-host或--rollback-nginx-smoke-host与--direct-host使用不同 hostname,且必须提供--rollback-health-patrol-public-base-url <切换前Nginx巡检入口>,确认步骤会展示回退后要恢复的 public base URL / Host,避免回退计划把现场巡检入口覆盖成仓库默认值。若切换计划希望回退后同时证明 Pingora shadow 高端口仍健康,可给 runbook 追加--rollback-pingora-shadow-probe-url http://127.0.0.1:18081/__genarrative_pingora/healthz --rollback-pingora-shadow-probe-token <token>;JSON 输出会隐藏 token 原文,并把对应参数传给 rollback dry-run / apply 步骤。
Pingora canary
deploy/nginx/snippets/genarrative-pingora-canary.conf是默认不启用的人工前缀 canary 入口,只在需要验证 Nginx -> Pingora handoff 时临时 include。它使用/__genarrative_pingora_canary/前缀改写后转发到127.0.0.1:18081,并返回X-Genarrative-Nginx-Handoff: pingora-canary。deploy/nginx/snippets/genarrative-pingora-realpath-canary.conf是前缀 canary 之后、direct 直连之前的真实路径 canary。它只能在 Nginxhttp上下文人工 include,默认监听127.0.0.1:18083,覆盖/api/creation-entry/config、/v1/identity、/v1/database/<db>/subscribe、/assets/app.js和拒绝入口,并写入独立/var/log/nginx/genarrative-pingora-realpath-canary.access.log;不要把它 include 到生产443server 内作为 location 覆盖。- 真实路径 canary 片段使用
access_log ... genarrative_upstream,如果以独立文件放入/etc/nginx/conf.d/,文件加载顺序必须晚于定义log_format genarrative_upstream的主站配置。当前目标机可使用/etc/nginx/conf.d/zz-genarrative-pingora-realpath-canary.conf;若站点把log_format移到全局 Nginx 配置,则需确保它仍在所有conf.dserver include 之前加载。启用前必须先跑nginx -t,不要用会早于genarrative.conf加载的文件名。 - 启用 Nginx canary 前,目标机必须已经有持久
genarrative-pingora-gateway.service运行在127.0.0.1:18081,且带真实api-server、SpacetimeDB、静态目录和/var/log/genarrative/pingora-gateway.access.log完成本机 shadow 验收;不要从一次性/tmp网关进程直接切到 Nginx handoff。 - 启用前必须运行
npm run check:nginx-pingora-canary;目标 agent 有 Nginx 时运行node scripts/check-nginx-pingora-canary.mjs --require-nginx,同时做两个 snippet 的静态护栏和真实nginx -t。本机或 CI 可运行npm run check:pingora-canary-docker,用 Docker Nginx、真实 Pingora 和 mock 上游复现前缀 canary 与真实路径 canary 两条 handoff 链路。 - 前缀 canary reload 后运行
GENARRATIVE_PINGORA_CANARY_BASE_URL=http://127.0.0.1 GENARRATIVE_PINGORA_CANARY_HOST=<域名> npm run check:pingora-canary-live,再用scripts/check-pingora-canary-access-log-parity.mjs对账/var/log/nginx/genarrative.access.log与/var/log/genarrative/pingora-gateway.access.log。 - 真实路径 canary 不再手工编辑
/etc/nginx/conf.d/。目标机从 current release 执行/opt/genarrative/current/scripts/deploy/pingora-realpath-canary-enable.sh --apply --probe-token <token> --host <域名> --base-url http://127.0.0.1:18083;脚本会渲染到/etc/nginx/conf.d/zz-genarrative-pingora-realpath-canary.conf,保持晚于主站log_format genarrative_upstream加载,执行nginx -t、reload Nginx,并默认运行 realpath live smoke。nginx -t、reload 或 live smoke 任一失败都会恢复写入前配置。关闭时执行/opt/genarrative/current/scripts/deploy/pingora-realpath-canary-disable.sh --apply,脚本会删除该zz-...文件,执行nginx -t和 reload,失败时恢复删除前配置。 npm run check:pingora-release-readiness默认包含npm run check:pingora-realpath-canary-toggle,用临时 Nginx 目录和 fakenginx/systemctl/ live smoke 验证 realpath canary 启停脚本默认 dry-run、apply 写入、失败回滚和 disable 恢复逻辑;不再把这条脚本护栏留成单独可选项。- 真实路径 canary reload 后可再次运行
node -- /opt/genarrative/current/scripts/check-pingora-canary-live.mjs --realpath --base-url http://127.0.0.1:18083 --host <域名>,再运行node -- /opt/genarrative/current/scripts/check-pingora-canary-access-log-parity.mjs --realpath --nginx-log-file /var/log/nginx/genarrative-pingora-realpath-canary.access.log --pingora-log-file /var/log/genarrative/pingora-gateway.access.log --path /__genarrative_pingora_realpath_canary/healthz --path /api/creation-entry/config --path /v1/identity --path /assets/app.js。真实路径模式除 healthz 探针外要求 Nginx path 与 Pingora path 完全一致。 - 正式切换前在源码 checkout / CI / 构建环境跑聚合门禁,并按目标机已启用的 canary 入口选择参数:前缀 canary 已启用时使用
node scripts/check-pingora-release-readiness.mjs --require-docker --pull-docker --require-nginx --require-live --live-base-url http://127.0.0.1 --live-host <域名> --live-nginx-access-log /var/log/nginx/genarrative.access.log --live-pingora-access-log /var/log/genarrative/pingora-gateway.access.log;真实路径 canary 已启用时追加或单独使用--require-realpath-live --realpath-live-base-url http://127.0.0.1:18083 --realpath-live-host <域名> --realpath-live-nginx-access-log /var/log/nginx/genarrative-pingora-realpath-canary.access.log --realpath-live-pingora-access-log /var/log/genarrative/pingora-gateway.access.log。如果现场只启用了真实路径 canary,不要同时传--require-live。 - 目标机
/opt/genarrative/current只能跑随包 runtime-only 门禁,并同样按现场已启用入口选择:前缀 canary 用/opt/genarrative/current/scripts/check-pingora-release-readiness.mjs --release-runtime-only --require-live --live-base-url http://127.0.0.1 --live-host <域名> --live-nginx-access-log /var/log/nginx/genarrative.access.log --live-pingora-access-log /var/log/genarrative/pingora-gateway.access.log;真实路径 canary 用/opt/genarrative/current/scripts/check-pingora-release-readiness.mjs --release-runtime-only --require-realpath-live --realpath-live-base-url http://127.0.0.1:18083 --realpath-live-host <域名> --realpath-live-nginx-access-log /var/log/nginx/genarrative-pingora-realpath-canary.access.log --realpath-live-pingora-access-log /var/log/genarrative/pingora-gateway.access.log;验证 Pingora 直连入口时再追加--require-direct ...参数。未带--require-direct的 runtime-only 基础门禁会自动执行直连彩排状态复核,确认公网80/443仍由 Nginx 接流、Pingora shadow127.0.0.1:18081、realpath canary127.0.0.1:18083和 current release 自审均通过;启用后--require-direct复核不再要求 Nginx 接公网彩排状态。生成正式直连切换 runbook 时,--direct-redirect-host和回退 smoke Host 必须与--direct-host同 hostname,只允许端口不同。
验证
dpkg -l 'libnginx-mod-http-brotli-*'
ls -l /etc/nginx/modules-enabled/*brotli*
nginx -T 2>/dev/null | grep -Ei 'brotli|load_module'
curl -sSI -H 'Accept-Encoding: gzip' \
http://<host>/api/runtime/puzzle/gallery \
| grep -iE 'content-encoding|vary|content-type|content-length'
curl -sSI -H 'Accept-Encoding: br' \
http://<host>/api/runtime/puzzle/gallery \
| grep -iE 'content-encoding|vary|content-type|content-length'
预期:
- gzip 可用时返回
Content-Encoding: gzip。 - br 可用时返回
Content-Encoding: br。 - 响应头应包含
Vary: Accept-Encoding。