From 93f085f95c22ecbcf626b6728273882cb5c84669 Mon Sep 17 00:00:00 2001 From: kdletters Date: Wed, 17 Jun 2026 23:21:56 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AE=B0=E5=BD=95=20Pingora=20realpath=20canar?= =?UTF-8?q?y=20=E5=8A=A0=E8=BD=BD=E9=A1=BA=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 补充 realpath canary 在 conf.d 中必须晚于 log_format 加载 记录 dev realpath canary current release 真实验收结果 新增生产运维护栏防止加载顺序说明丢失 --- deploy/nginx/README.md | 1 + docs/project-memory/shared-memory/pitfalls.md | 8 ++++++++ ...开发运维】Pingora独立网关试点-2026-06-11.md | 9 ++++++++- scripts/check-production-ops-guardrails.mjs | 18 ++++++++++++++++++ 4 files changed, 35 insertions(+), 1 deletion(-) diff --git a/deploy/nginx/README.md b/deploy/nginx/README.md index d5db0afec..ac9a3f253 100644 --- a/deploy/nginx/README.md +++ b/deploy/nginx/README.md @@ -64,6 +64,7 @@ - `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。它只能在 Nginx `http` 上下文人工 include,默认监听 `127.0.0.1:18083`,覆盖 `/api/creation-entry/config`、`/v1/identity`、`/v1/database//subscribe`、`/assets/app.js` 和拒绝入口,并写入独立 `/var/log/nginx/genarrative-pingora-realpath-canary.access.log`;不要把它 include 到生产 `443` server 内作为 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.d` server 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`。 diff --git a/docs/project-memory/shared-memory/pitfalls.md b/docs/project-memory/shared-memory/pitfalls.md index 55d6e0d82..b87df7a6a 100644 --- a/docs/project-memory/shared-memory/pitfalls.md +++ b/docs/project-memory/shared-memory/pitfalls.md @@ -125,6 +125,14 @@ - 验证:本机或 CI 执行 `node scripts/check-pingora-canary-docker.mjs --require-docker --pull` 时应同时完成临时 Nginx / Pingora access log 对账。目标机执行 `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`;本机执行 `npm run check:pingora-release-readiness-plan` 和 `npm run check:production-ops`,确认 live 门禁计划包含真实 access log 对账。 - 关联:`scripts/check-pingora-release-readiness.mjs`、`scripts/check-pingora-canary-access-log-parity.mjs`、`deploy/env/pingora-canary-live.env.example`、`docs/technical/【开发运维】Pingora独立网关试点-2026-06-11.md`。 +## Pingora realpath canary include 要晚于 log_format + +- 现象:目标机把 `genarrative-pingora-realpath-canary.conf` 放进 `/etc/nginx/conf.d/` 后,`nginx -t` 失败并报 `unknown log format "genarrative_upstream"`。 +- 原因:真实路径 canary 是独立 `server` 片段,并使用 `access_log /var/log/nginx/genarrative-pingora-realpath-canary.access.log genarrative_upstream;`。Nginx 会按文件名顺序加载 `conf.d`;如果 canary 文件名早于定义 `log_format genarrative_upstream` 的主站配置,access log 行会先被解析而找不到格式。 +- 处理:真实路径 canary 要么 include 在已经定义 `log_format genarrative_upstream` 之后,要么放入晚于主站配置加载的文件,例如 `/etc/nginx/conf.d/zz-genarrative-pingora-realpath-canary.conf`;另一种长期做法是把 `log_format` 放到所有 `conf.d` server 之前的全局 Nginx 配置。启用前必须先跑 `nginx -t`,失败时先移除临时 canary 文件再 reload,避免保留坏配置;检查配置时不要把 probe token 原文写入记录。 +- 验证:`nginx -t` 通过后 reload Nginx,再运行 `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 ...` 和 `node -- /opt/genarrative/current/scripts/check-pingora-release-readiness.mjs --release-runtime-only --require-realpath-live ...`。若只启用了真实路径 canary,不要同时传 `--require-live`,否则前缀 canary 未启用时会按正式 Nginx HTTP 入口返回 301。 +- 关联:`deploy/nginx/snippets/genarrative-pingora-realpath-canary.conf`、`deploy/nginx/README.md`、`docs/technical/【开发运维】Pingora独立网关试点-2026-06-11.md`、`scripts/check-pingora-release-readiness.mjs`。 + ## Pingora release readiness 脚本不能只存在于源码 checkout - 现象:本机 runbook 能生成,但目标机切换窗口执行启用前或启用后的 release readiness 复核时,可能命中 Jenkins workspace 或源码 checkout 的 `scripts/check-pingora-release-readiness.mjs`,而不是当前发布包里的脚本。 diff --git a/docs/technical/【开发运维】Pingora独立网关试点-2026-06-11.md b/docs/technical/【开发运维】Pingora独立网关试点-2026-06-11.md index c2404c9fa..fdecfb491 100644 --- a/docs/technical/【开发运维】Pingora独立网关试点-2026-06-11.md +++ b/docs/technical/【开发运维】Pingora独立网关试点-2026-06-11.md @@ -369,6 +369,13 @@ dev 根盘空间在安装后曾接近满盘;2026-06-17 进入 canary 前已清 - `node /tmp/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 ... --json` 对账 `6/6 matched`,`missingCount=0`,`mismatchCount=0`;除 healthz 探针映射到 `__genarrative_pingora/healthz` 外,API config、SpacetimeDB identity、WSS subscribe、静态代表路径和 generated 拒绝路径的 Nginx path 与 Pingora path 完全一致。 - 恢复后 `grep genarrative-pingora-realpath-canary /etc/nginx/conf.d/genarrative.conf` 无匹配,`ss -ltnp | grep :18083` 无监听,证明临时 realpath canary server 已移除;`genarrative-pingora-gateway.service` 仍为 `active` 且 `NRestarts=0`。 +同日下一阶段在 current release `/opt/genarrative/releases/dev-pingora-api-20260617230221` 上重新启用 loopback realpath canary,并用正式 Host `dev.genarrative.world` 验收。第一次把独立 server 写成 `/etc/nginx/conf.d/genarrative-pingora-realpath-canary.conf` 时,因它早于 `genarrative.conf` 加载,`nginx -t` 报 `unknown log format "genarrative_upstream"`;改为 `/etc/nginx/conf.d/zz-genarrative-pingora-realpath-canary.conf` 后 `nginx -t` 和 reload 均通过。随后: + +- `node -- /opt/genarrative/current/scripts/check-pingora-canary-live.mjs --realpath --base-url http://127.0.0.1:18083 --host dev.genarrative.world --json` 通过:healthz `200`、API config `200`、SpacetimeDB identity `405`、静态代表路径 `404`、generated 拒绝路径 `404`,全部带 `X-Genarrative-Nginx-Handoff: pingora-realpath-canary`。 +- `node -- /opt/genarrative/current/scripts/check-pingora-canary-access-log-parity.mjs --realpath ... --json` 对账 `12/12 matched`,`missingCount=0`,`mismatchCount=0`。 +- `node -- /opt/genarrative/current/scripts/check-pingora-release-readiness.mjs --release-runtime-only --require-realpath-live ...` 通过 current release 自包含自审、realpath live smoke 和 realpath access log 对账,最终对账 `18/18 matched`。 +- 本阶段没有启用前缀 canary,因此不能同时传 `--require-live`;否则正式 HTTP 入口会按 Nginx 策略返回 `301`,并缺少 `X-Genarrative-Nginx-Handoff: pingora-canary`。当前 public `80/443` 仍由 Nginx 承接,realpath canary 只保留在 `127.0.0.1:18083` 用于后续 loopback 复核。 + ## dev API release 正式路径验收记录 2026-06-17 已在 dev 机用正式 API release 路径部署包含 Pingora 的发布包 `dev-pingora-api-20260617140915`。发布包由分支 `codex/pingoranginx` 的 `68bf3b698a885ee2cd248dcba81e4b57460000f8` 构建,包含 `api-server`、`api-server.sha256`、`pingora-gateway`、`pingora-gateway.sha256`、`release-manifest.json` 和随包部署 / 自审脚本。部署命令从上传到 `/tmp/dev-pingora-api-20260617140915` 的发布包内执行 `scripts/deploy/production-api-deploy.sh`,目标 release root 为 `/opt/genarrative/releases`,current link 为 `/opt/genarrative/current`。 @@ -470,7 +477,7 @@ dev 根盘空间在安装后曾接近满盘;2026-06-17 进入 canary 前已清 2. 涉及 Nginx 模板、Pingora 路由、限流分组或路由文档时,同步更新 `deploy/pingora/nginx-route-parity.matrix.json`,并运行 `npm run check:pingora-route-parity` 与 `cargo test -p pingora-gateway --manifest-path server-rs/Cargo.toml matches_nginx_route_parity_matrix`。 3. 容器内使用同一份 Web 产物、同一组真实上游地址跑 Pingora smoke,并继续对照 `deploy/nginx/genarrative.conf` 扩展真实上游路由 parity 自动测试。 4. 使用 `deploy/nginx/snippets/genarrative-pingora-canary.conf` 做 Nginx 前缀 canary;启用前先跑 `npm run check:nginx-pingora-canary` 和 `npm run check:pingora-canary-docker`,有 Nginx 或 Docker 的目标环境分别强制跑 `node scripts/check-nginx-pingora-canary.mjs --require-nginx` 与 `node scripts/check-pingora-canary-docker.mjs --require-docker --pull`,其中 Docker handoff 会自动对账临时 Nginx 与 Pingora access log。启用后跑 `npm run check:pingora-canary-live`,再用 current release 随包 access log parity 脚本对账目标机 Nginx 与 Pingora access log。canary live 的 base URL、prefix、Host、额外 path 和 timeout 不能包含换行或 NUL;canary live timeout 和 access log `since-lines` 必须是正整数,非法值直接失败。 -5. 前缀 canary 通过后,再使用 `deploy/nginx/snippets/genarrative-pingora-realpath-canary.conf` 做真实路径 canary;它必须作为独立本机 `server` include 到 Nginx `http` 上下文,不能 include 到生产 `443` server 内。启用后跑 `node scripts/check-pingora-canary-live.mjs --realpath --base-url http://127.0.0.1:18083 --host <域名>`,再用 current release 随包 access log parity 脚本传 `--realpath --nginx-log-file /var/log/nginx/genarrative-pingora-realpath-canary.access.log` 对账 `/api/creation-entry/config`、`/v1/identity` 和 `/assets/app.js` 等真实路径。 +5. 前缀 canary 通过后,再使用 `deploy/nginx/snippets/genarrative-pingora-realpath-canary.conf` 做真实路径 canary;它必须作为独立本机 `server` include 到 Nginx `http` 上下文,不能 include 到生产 `443` server 内。该片段使用 `access_log ... genarrative_upstream`,如果放入 `/etc/nginx/conf.d/`,文件名必须保证晚于定义 `log_format genarrative_upstream` 的主站配置加载,例如 `/etc/nginx/conf.d/zz-genarrative-pingora-realpath-canary.conf`;否则 `nginx -t` 会报 `unknown log format "genarrative_upstream"`。启用后跑 `node scripts/check-pingora-canary-live.mjs --realpath --base-url http://127.0.0.1:18083 --host <域名>`,再用 current release 随包 access log parity 脚本传 `--realpath --nginx-log-file /var/log/nginx/genarrative-pingora-realpath-canary.access.log` 对账 `/api/creation-entry/config`、`/v1/identity` 和 `/assets/app.js` 等真实路径。 6. 目标机 canary include 后必须跑正式切换聚合门禁:源码 checkout / CI / 构建环境执行默认全量命令 `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`;目标机 current release 执行 `/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 时,两种命令都追加 `--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`。缺少 Host 会直接失败,避免 live canary 误测默认 vhost;live smoke 后还会按 `request_id` 对账 Nginx 与 Pingora access log,缺少同一请求的 Pingora 日志、状态码、方法或 path 漂移都会失败。 7. 如需评估 Pingora 直连公网入口,必须显式配置 `TLS_LISTEN`、证书、私钥和 `HTTP_REDIRECT_LISTEN`;Certbot 证书先用随包 `scripts/deploy/pingora-tls-cert-sync.mjs` 同步到 `/etc/genarrative/pingora-tls/<域名>/`,不要直接 chmod Let’s Encrypt live/archive 原路径;绑定 `80/443` 时还必须人工启用 `genarrative-pingora-gateway-direct-entry.conf` drop-in 授予 `CAP_NET_BIND_SERVICE`。随后用 `npm run check:pingora-gateway-smoke` 覆盖 TLS / HTTP/2 ALPN / redirect / WSS subscribe;目标机必须先跑 `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`,再跑 `npm run check:pingora-direct-live` 或 release readiness 的 `--require-direct`,且 `--require-direct` 必须带 direct HTTPS base URL、direct HTTP base URL、正式域名 Host/SNI、redirect Location host、Pingora access log 文件、health patrol env 文件、direct preflight env 文件、systemd drop-in 生效检查、service EnvironmentFile 一致性检查、当前用户和 systemd 服务用户证书可读检查、service 二进制可执行检查、端口释放检查和显式 SpacetimeDB 数据库名,并会拒绝 `--skip-wss`。高端口 rehearsal 使用 `https://127.0.0.1:<高端口>` 打入但期望 HTTP redirect Location 指向正式域名默认 HTTPS 入口时,额外传 `--direct-redirect-base-url https://<域名>`;`--direct-redirect-host` 仍必须保留,用于 runbook Host 一致性约束。direct live 会用生成的 `request_id` 反查 Pingora access log;缺少对应日志、method 漂移、path 漂移或 status 漂移都算直连门禁失败。direct preflight 会拒绝开启网关保护但未确认共享保护层的 `GENARRATIVE_PINGORA_GATEWAY_INSTANCE_COUNT>1` 配置;`--env-file`、`--systemd-service`、服务用户和 env 中的 listen / cert / key 值都不能包含换行或 NUL,执行 `systemctl cat` 或 `sudo -u test -r ` 前还会复核子命令参数,避免污染参数进入目标机预检命令;direct live 的 URL、Host、redirect base URL、probe token、额外 path、数据库名、access log 路径、timeout 和布尔 env 也不能包含换行或 NUL,且会在发起请求前失败;direct live timeout 必须是正整数,直连相关布尔 env 只接受 `true/false`、`1/0`、`yes/no`、`on/off` 或空值,非法值直接失败。证书申请与续期仍由 Certbot / 外部自动化承担,网关只读取现有文件。 8. 正式直连 runbook 的启用前基础门禁和启用后 `--require-direct` 复核必须调用 `/opt/genarrative/current/scripts/check-pingora-release-readiness.mjs --release-runtime-only`。该脚本、`scripts/check-pingora-canary-live.mjs` 和 `deploy/nginx/` 必须进入生产 API release、Jenkins API Build 归档、Jenkins API Deploy 复制清单和目标机 current release;缺失时部署应 fail-fast,切换窗口不能依赖源码 checkout 或 Jenkins workspace。 diff --git a/scripts/check-production-ops-guardrails.mjs b/scripts/check-production-ops-guardrails.mjs index 4a0c65f98..0206acc8b 100644 --- a/scripts/check-production-ops-guardrails.mjs +++ b/scripts/check-production-ops-guardrails.mjs @@ -832,6 +832,24 @@ const checks = [ reason: 'Pingora 真实路径 canary 必须覆盖代表性 API 真实路径。', }, + { + file: 'deploy/nginx/README.md', + includes: 'zz-genarrative-pingora-realpath-canary.conf', + reason: + 'Pingora 真实路径 canary 运维说明必须记录 conf.d 加载顺序,避免早于 log_format 定义加载。', + }, + { + file: 'docs/technical/【开发运维】Pingora独立网关试点-2026-06-11.md', + includes: 'unknown log format "genarrative_upstream"', + reason: + 'Pingora 试点文档必须记录 realpath canary 的 Nginx log_format 加载顺序踩坑。', + }, + { + file: 'docs/project-memory/shared-memory/pitfalls.md', + includes: 'Pingora realpath canary include 要晚于 log_format', + reason: + '团队共享踩坑必须记录 realpath canary 独立 server 在 conf.d 中的加载顺序要求。', + }, { file: 'scripts/jenkins-server-provision.sh', includes: 'genarrative-health-patrol.timer',