From fce546403f988e0c590b7eb3fb98b8150174dc89 Mon Sep 17 00:00:00 2001 From: kdletters Date: Thu, 17 Sep 2026 20:23:55 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=87=E6=A1=A3=E8=AE=B0=E5=BD=95=20Jenkins?= =?UTF-8?q?=E3=80=81=E9=A2=84=E8=A7=88=E6=8E=A7=E5=88=B6=E9=9D=A2=E4=B8=8E?= =?UTF-8?q?=E9=A2=84=E8=A7=88=E5=AE=9E=E4=BE=8B=E7=9A=84=E5=85=AC=E7=BD=91?= =?UTF-8?q?=E5=85=A5=E5=8F=A3=E5=8F=A3=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在【开发运维】本地开发验证与生产运维补充 jenkins.genarrative.world 与 build.genarrative.world 的公网入口、反向隧道端口、白名单依赖与排障顺序 - 在 shared-memory/decision-log.md 记录 Jenkins 公网入口、预览部署控制面公网入口、预览控制面公网预览地址口径三组决策及各自验证证据 --- .../shared-memory/decision-log.md | 27 +++++++++++++++++++ ...发运维】本地开发验证与生产运维-2026-05-15.md | 2 ++ 2 files changed, 29 insertions(+) diff --git a/docs/project-memory/shared-memory/decision-log.md b/docs/project-memory/shared-memory/decision-log.md index f1edf3f71..1b57435a0 100644 --- a/docs/project-memory/shared-memory/decision-log.md +++ b/docs/project-memory/shared-memory/decision-log.md @@ -8864,3 +8864,30 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在 - 边界:Deploy 阶段在远端 dev / release agent 执行,不受该上限约束。调整只动这两处:`systemctl set-property / revert jenkins.service`、`docker update --cpus= gitea-runner` 加同步 compose(备份 `/opt/gitea-stack/compose.yml.bak-<时间戳>`)。 - 验证:限速后 `Genarrative-Full-Build-And-Deploy` #289 / #290 SUCCESS;采样期 Jenkins 峰值 10.2~10.5 核、限流不足 2s(可忽略),runner 峰值 12.07 核且持续出现 throttling,整机回落到 2.6%~19.8%。 - 关联文档:[开发运维](../../【开发运维】本地开发验证与生产运维-2026-05-15.md)。 + +## 2026-09-17 Jenkins 公网入口 jenkins.genarrative.world 复用 router 反向隧道口径 + +- 背景:Jenkins controller 实际与 Gitea 同机运行在 `genarrative-station`(`jenkins.service`,`--httpPort=8080 --prefix=/jenkins`,`JENKINS_HOME=/var/lib/jenkins`),此前只有内网入口 `http://192.168.35.82:8080/jenkins/`;`router.genarrative.world` 已有「dev Nginx → dev loopback → station 反向隧道」的成熟口径。 +- 决策:沿用 router 口径,不新增网关组件。`genarrative-station` 的 `gitea-reverse-tunnel.service` 增加 `-R 127.0.0.1:18085:127.0.0.1:8080`(dev loopback `18085` → station Jenkins `127.0.0.1:8080`);dev 新增 `/etc/nginx/conf.d/jenkins.genarrative.world.conf`:`80` 只做 ACME webroot 与 `301`,`443` 用 Certbot 证书反代 `http://127.0.0.1:18085` 并保留 `Upgrade` / `X-Forwarded-*`;证书按 router 口径用 `certbot certonly --webroot -w /var/www/html -d jenkins.genarrative.world --renew-hook 'systemctl reload nginx'` 申请。 +- 路径口径:Jenkins 固定 `--prefix=/jenkins`,域名根路径 `302` 到 `https://jenkins.genarrative.world/jenkins/login`,`/jenkins` 补斜杠,其余未带前缀路径 `302` 到 `/jenkins$request_uri`;证书不复制到 Pingora 私有目录,公网 `80/443` 仍由 dev Nginx 监听。 +- 边界:本次只暴露 HTTP/HTTPS UI,`slaveAgentPort` 保持 `-1`(agent 继续由 Jenkins 用 SSH launcher 连 dev / release),不改 Jenkins `jenkinsUrl` 与鉴权策略;Jenkins 登录页因此进入公网可达面,访问控制继续依赖 Jenkins 自身账号体系。 +- 验证:dev `nginx -t` 与 `systemctl reload nginx` 通过;`curl -sI https://jenkins.genarrative.world/` 返回 `302 /jenkins/login`、`/jenkins/login` 返回 `200`、登录页静态资源 `200`、`http://` 入口 `301`;Let's Encrypt 证书 `CN=jenkins.genarrative.world` 到期 `2026-12-16`;公网探测 `82.157.175.59` 仍只开放 `80/443/22`。 +- 关联文档:[开发运维](../../【开发运维】本地开发验证与生产运维-2026-05-15.md)。 + +## 2026-09-17 预览部署控制面公网入口 build.genarrative.world + +- 背景:多人内网预览控制面(`preview-deployer-server` + `shared/Genarrative-Preview-Deployer` Job)此前只在内网 `http://192.168.35.82/build/` 提供,2026-08-15 决策明确「不配置公网域名」;本次要求给它加公网入口。 +- 决策:沿用 router / Jenkins 同一口径新增 `build.genarrative.world` 作为控制面公网入口,并把 `preview.genarrative.world` 作为 `*.preview.genarrative.world` 规划的父域名先落一张落地页(实例本身仍只在内网)。station `gitea-reverse-tunnel.service` 增加 `-R 127.0.0.1:18086:127.0.0.1:8410`;dev 新增 `/etc/nginx/conf.d/build.genarrative.world.conf`(`80` ACME+`301`,`443` 把 `/build/`、`/api/preview-deployer/` 反代到 `127.0.0.1:18086`,`/` 跳 `/build/`,公网侧 `proxy_cookie_flags ~ secure`)与 `/etc/nginx/conf.d/preview.genarrative.world.conf`(单域名证书 + 落地页)。 +- 白名单:控制面按 `Host` 精确匹配、对非 GET 的 `/api/*` 精确匹配 `Origin`,因此同步改为 `GENARRATIVE_PREVIEW_DEPLOYER_ALLOWED_HOSTS=192.168.35.82,build.genarrative.world`、`GENARRATIVE_PREVIEW_DEPLOYER_ALLOWED_ORIGINS=http://192.168.35.82,https://build.genarrative.world`;`GENARRATIVE_PREVIEW_DEPLOYER_SECURE_COOKIE` 保持 `false`(内网 HTTP 入口继续可用),公网 cookie 的 `Secure` 由 dev nginx 强制。重启 `genarrative-preview-deployer.service` 会清空内存会话,内网用户需重新输入口令。 +- 边界:本次只暴露控制面(触发/查看构建、卸载),预览实例不暴露;`preview.genarrative.world` 没有通配记录,实例地址仍是内网 `http://192.168.35.82:84xx`,控制面页面展示的 `webUrl` 也仍是内网地址。要变成公网实例地址,需要 `*.preview.genarrative.world` 通配证书(只能 DNS-01)、station 侧按 Host 分发和页面 URL 口径改造。 +- 验证:`nginx -t` 与 reload 通过;`https://build.genarrative.world/` `302 → /build/`、`/build/` `200`、SPA 资源 `200`、`/api/preview-deployer/session` 返回 `{"authenticated":false}`;错误或缺失 `Origin` 的 POST `403`、错误口令 `401`、字段名不符 `422`;两个新域名证书到期 `2026-12-16`;内网 `Host: 192.168.35.82` 仍 `200`、未知 Host `403`;jenkins / dev / git 入口回归正常。 +- 关联文档:[开发运维](../../【开发运维】本地开发验证与生产运维-2026-05-15.md)、[Jenkins容器预览部署控制面技术方案](../../technical/【开发运维】Jenkins容器预览部署控制面技术方案-2026-08-15.md)。 + +## 2026-09-17 预览控制面增加公网预览地址口径(代码已实现,待随控制面发布) + +- 背景:控制面页面此前只展示内网地址 `http://192.168.35.82:`;公网通配域名 `*.preview.genarrative.world` 已解析到 dev,需要页面能显示对应的公网入口。 +- 决策:公网地址由控制面自己派生,不接受 Jenkins 产物或状态文件提供的任意地址。`preview-deployer-server` 新增可选配置 `GENARRATIVE_PREVIEW_DEPLOYER_WEB_DOMAIN`(如 `preview.genarrative.world`,只接受小写字母、数字、短横线和点号,不带协议与端口),并在公开 DTO 新增 `webPublicUrl`:仅当记录已有内网 `webUrl` 时取 `https://.`(实例 ID 仍由分支派生,形如 `preview-<16位hex>`),卸载时与 `webUrl` / `webPort` 一起清空;状态文件里的旧值在加载时被重新派生覆盖。 +- 前端:`apps/preview-deployer-web` 在存在公网地址时把「打开公网预览」作为主入口,内网地址降级为次级链接;未配置时行为与之前一致。 +- 上线依赖(本次未完成):`*.preview.genarrative.world` 通配证书(Let's Encrypt 通配只能走 DNS-01,域名在 DNSPod,certbot 无官方插件,需要 DNSPod API Token 配合 acme.sh)、station 侧按 Host 分发到 `84xx` 端口、dev 通配 vhost 与隧道;控制面本体需在 station 用 `scripts/deploy/preview-deployer-install.sh` 重建发布。 +- 验证:`cargo test -p preview-deployer-server`(13 项)、`apps/preview-deployer-web` vitest(13 项,含新增公网地址用例)、`npx tsc --noEmit`、`npm run preview-deployer:web:build`(`PREVIEW_DEPLOYER_WEB_BASE=/build/`)、`npm run check:preview-deployer`、`npm run check:encoding`、`git diff --check` 全部通过。 +- 关联文档:[开发运维](../../【开发运维】本地开发验证与生产运维-2026-05-15.md)、[Jenkins容器预览部署控制面技术方案](../../technical/【开发运维】Jenkins容器预览部署控制面技术方案-2026-08-15.md)。 diff --git a/docs/【开发运维】本地开发验证与生产运维-2026-05-15.md b/docs/【开发运维】本地开发验证与生产运维-2026-05-15.md index 0b5d624d2..f13c743d5 100644 --- a/docs/【开发运维】本地开发验证与生产运维-2026-05-15.md +++ b/docs/【开发运维】本地开发验证与生产运维-2026-05-15.md @@ -783,6 +783,8 @@ npm run container:down `npm run container:config` 默认只做 quiet 校验,避免把本地 env 中的 token 展开到终端;确需排查完整 compose 时再传 `-- --print`。 多人内网预览入口固定为 `http://192.168.35.82/build/`,不配置公网域名。该独立 Jenkins 容器预览部署控制面不让浏览器直接操作 Docker 或持有 Jenkins Token;SPA 通过同源代理触发固定 `shared/Genarrative-Preview-Deployer` Job。分支和 commit 输入框通过受认证的控制服务搜索固定内网 Git 仓库并展示下拉结果;提交构建前控制服务重新确认分支存在、可选 commit 存在且属于目标分支,失败时不触发 Jenkins,Jenkins checkout 仍保留最终复核。每个分支使用稳定的内部 `deploymentId` 和独立 Compose project,Web 端口从 `8400..8499` 在文件锁内分配,同一分支换 commit 优先复用端口,卸载后释放;页面记录 ID 使用 Jenkins 构建编号。Jenkins 用 `preview-result.json` 向页面提供 resolved commit、发布结果和内网 Web URL,页面刷新时由控制服务实时复核 Web 健康;构建详情链接固定使用局域网 Jenkins 地址,不暴露 loopback 地址。失败/取消且不可卸载的记录保留 7 天,停止记录保留 30 天,仍可卸载的失败记录不会自动清理。安装资产为 `deploy/systemd/genarrative-preview-deployer.service`、`deploy/env/preview-deployer.env.example` 和 `deploy/nginx/genarrative-preview-deployer-lan.conf`;完整合同见 `docs/technical/【开发运维】Jenkins容器预览部署控制面技术方案-2026-08-15.md`。 +Jenkins controller 与 Gitea 同机运行在 `genarrative-station`,除内网入口 `http://192.168.35.82:8080/jenkins/` 外,公网入口为 `https://jenkins.genarrative.world/jenkins/`:dev 的 `/etc/nginx/conf.d/jenkins.genarrative.world.conf` 把 `443` 反代到 `http://127.0.0.1:18085`,该 loopback 端口由同机 `gitea-reverse-tunnel.service` 新增的 `-R 127.0.0.1:18085:127.0.0.1:8080` 落到 station 的 `jenkins.service`(`--prefix=/jenkins`,因此域名根路径 `302` 到 `/jenkins/login`)。排障顺序:dev `ss -tlnp | grep 18085` 必须有 `sshd` 监听,`curl -sI https://jenkins.genarrative.world/jenkins/login` 必须 `200`,`systemctl status gitea-reverse-tunnel.service` 必须 `active`,且公网 `80/443` 仍由 Nginx 监听;`slaveAgentPort=-1` 保持关闭,agent 仍走 SSH launcher,不新增入库端口。证书按 router 口径用 Certbot webroot(`/var/www/html`)维护,续期 hook 为 `systemctl reload nginx`。 +预览部署控制面的公网入口为 `https://build.genarrative.world/build/`(`/` 与 `/build` 分别 `302`/`301` 到 `/build/`,API 走同源 `/api/preview-deployer/`):dev 的 `/etc/nginx/conf.d/build.genarrative.world.conf` 反代到 `127.0.0.1:18086`,该 loopback 端口由 station `gitea-reverse-tunnel.service` 的 `-R 127.0.0.1:18086:127.0.0.1:8410` 落到控制面 `preview-deployer-server`。控制面按 `Host` 精确匹配白名单、对非 GET 的 `/api/*` 精确匹配 `Origin`,公网域名必须同时出现在 `GENARRATIVE_PREVIEW_DEPLOYER_ALLOWED_HOSTS` 和 `GENARRATIVE_PREVIEW_DEPLOYER_ALLOWED_ORIGINS` 中;公网 cookie 的 `Secure` 由 dev nginx 的 `proxy_cookie_flags ~ secure` 强制,因此 `GENARRATIVE_PREVIEW_DEPLOYER_SECURE_COOKIE` 保持 `false`,内网 `http://192.168.35.82/build/` 的登录态不受影响。排障顺序:dev `ss -tlnp | grep 18086` 有 `sshd` 监听;`curl -s https://build.genarrative.world/api/preview-deployer/session` 返回 `{"authenticated":false}`;缺失或错误 `Origin` 的 POST 必须 `403`,错误口令必须 `401`。`preview.genarrative.world` 当前只是通配规划下的落地页,预览实例仍只在内网 `http://192.168.35.82:84xx`;要暴露实例需要 `*.preview.genarrative.world` 通配证书(只能 DNS-01)、station 侧按 Host 分发和页面 `webUrl` 口径改造。 隔离验证 worker 队列和 API-only 更新时使用 `npm run container:worker-smoke -- smoke`。该命令不复用 `deploy/container/api-server.env`,会在 `deploy/container/worker-smoke/` 生成本机专用 env 与端口 state,并且只使用 unsupported job 验证 worker claim / fail 回写,不覆盖 BgFilter 成功、失败或 fallback 链路,也不需要真实外部生成密钥;本机 crates.io 网络不稳时使用 `--local-binary`,由容器内 Cargo 复用本机 Cargo 缓存构建,并把产物放进 Debian bookworm smoke runtime。 独立 BgFilter worker 的本机全进程验证先运行 `cargo build -p api-server --manifest-path server-rs/Cargo.toml`,再依次运行 `npm run bgfilter-worker:smoke-test`、`npm run bgfilter-worker:load-smoke` 和 `npm run bgfilter-worker:fault-smoke`。三条命令只使用动态 loopback 端口、假 OSS 签名配置和本地 mock provider;不会读取仓库 `.env*` 或请求真实 BgFilter / OSS。自定义或 WSL binary 通过 `GENARRATIVE_BGFILTER_SMOKE_BINARY` 指定。当前 fault 范围包含 overload、queue deadline、两类 HTTP 状态顺序重试结果,以及 provider 成功响应 body 中途 reset 后第二次 attempt 串行成功;慢读、大响应、父侧客户端断连与 SIGTERM 排空另行验证。