fix(deploy): refresh nginx gallery release config

This commit is contained in:
kdletters
2026-05-20 16:53:53 +08:00
parent 0eed942ce5
commit 9cd1bd6241
2 changed files with 53 additions and 5 deletions

View File

@@ -153,7 +153,7 @@ Windows Stdb module 构建流水线运行在 Jenkins `windows` 节点上。该
生产环境变量模板:`deploy/env/api-server.env.example`。真实密钥只放服务器,不提交 Git不写入文档示例。
`Genarrative-Server-Provision` 会安装基础构建依赖、systemd 模板和 Nginx 站点模板。Ubuntu / apt 目标机会额外安装 `libnginx-mod-http-brotli-filter``libnginx-mod-http-brotli-static`,随后由 `scripts/jenkins-server-provision.sh` 通过临时 `nginx -t` 配置探测 Brotli 指令是否可用;该临时配置必须先 `include /etc/nginx/modules-enabled/*.conf`,因为 apt 安装的 Brotli 是动态模块,不会出现在普通 `nginx -V` 编译参数里。探测成功才在渲染后的 `deploy/nginx/genarrative.conf` / `genarrative-dev-http.conf` 中启用 Brotli避免未安装模块的机器直接写入无效配置。
`Genarrative-Server-Provision` 会安装基础构建依赖、systemd 模板和 Nginx 站点模板。Ubuntu / apt 目标机会额外安装 `libnginx-mod-http-brotli-filter``libnginx-mod-http-brotli-static`,随后由 `scripts/jenkins-server-provision.sh` 通过临时 `nginx -t` 配置探测 Brotli 指令是否可用;该临时配置必须先 `include /etc/nginx/modules-enabled/*.conf`,因为 apt 安装的 Brotli 是动态模块,不会出现在普通 `nginx -V` 编译参数里。探测成功才在渲染后的 `deploy/nginx/genarrative.conf` / `genarrative-dev-http.conf` 中启用 Brotli避免未安装模块的机器直接写入无效配置。Provision 写入 Genarrative Nginx 站点时会把 `/etc/nginx/sites-enabled/default*` 移到 `/etc/nginx/sites-disabled/`,避免 Debian / Certbot 默认站点继续占用 `genarrative.world` / `www.genarrative.world` 并在 `nginx -T` 中出现 `conflicting server name ... ignored`。如果 `nginx -t` 失败,脚本会恢复写入前的 Genarrative 配置和被移动的默认站点。
50 HTTP req/s 首版压测优化口径:
@@ -164,7 +164,7 @@ Windows Stdb module 构建流水线运行在 Jenkins `windows` 节点上。该
- Windows 下载阶段如果出现 `curl: (18)` 或响应体截断,流水线会保留同名 `.download` 临时文件并用 `curl -C -` 断点续传;只有完整返回但 SHA256 digest 仍不匹配时才删除临时文件后重新下载。目标 Linux 节点仍只接收 `stash/unstash` 带过去的本地下载件,不回退外网下载。
- Windows 下载阶段如果走代理,在 `Genarrative-Server-Provision` 参数 `PROVISION_DOWNLOAD_PROXY` 填写 Windows Jenkins 节点可访问的 HTTP 代理,例如 `http://127.0.0.1:7890`;不要填写目标 release 机器视角的 `127.0.0.1`,除非代理确实运行在该 Windows 节点本机。Linux 目标机阶段会强制要求使用本地下载件,缺少文件直接失败,不再回退到外网下载。
- `otelcol-contrib.service` 作为可选系统服务加入 provision默认监听 `127.0.0.1:4317/4318` 并使用 `deploy/otelcol/genarrative-debug.yaml`。api-server 是否发送 OTLP 仍由 `GENARRATIVE_OTEL_ENABLED` 控制,服务 unit 见 `deploy/systemd/otelcol-contrib.service`
- Nginx `/api/``/admin/api/` 通过 `genarrative_api` upstream 代理到 `127.0.0.1:8082`upstream keepalive 为 64`limit_conn` 负责连接 / 并发保护,`limit_req` 负责入口 RPS 快拒绝。当前模板把公开 gallery list 单独放到 `genarrative_gallery_rps`,默认 `rate=5000r/s``burst=4096``limit_conn=320`;公开详情和普通 API 放到 `genarrative_api_rps`,后台 API 放到 `genarrative_admin_rps`。压测时看 `/var/log/nginx/genarrative.access.log` 中的 `request_time``upstream_connect_time``upstream_header_time``upstream_response_time``upstream_status``request_id`
- Nginx `/api/``/admin/api/` 通过 `genarrative_api` upstream 代理到 `127.0.0.1:8082`upstream keepalive 为 64`limit_conn` 负责连接 / 并发保护,`limit_req` 负责入口 RPS 快拒绝。当前模板把公开 gallery list 单独放到 `genarrative_gallery_rps`,默认 `rate=5000r/s``burst=4096``limit_conn=320`;公开详情和普通 API 放到 `genarrative_api_rps`,后台 API 放到 `genarrative_admin_rps``limit_conn_status 429``limit_req_status 429` 必须在 HTTP 与 HTTPS server 中同时生效;若线上压测看到 `limiting connections by zone "genarrative_api_conn"` 却返回 503优先检查 `nginx -T` 里 HTTPS server 是否缺少这些状态码,以及 `/api/runtime/puzzle/gallery` 是否误落到通用 `location ~ ^/api``limit_conn=64`压测时看 `/var/log/nginx/genarrative.access.log` 中的 `request_time``upstream_connect_time``upstream_header_time``upstream_response_time``upstream_status``request_id`
- 作品列表 K6 脚本一次 iteration 默认请求两个公开接口,因此约 50 HTTP req/s 的目标命令使用 `SCENARIO=spike START_RPS=5 PEAK_RPS=25 HOLD=60s END_RPS=5 DETAIL_RATIO=0 npm run loadtest:k6:works`
- 作品列表短期继续由 `api-server` / BFF 订阅 SpacetimeDB 公开 read model 后读本地 cache不让浏览器前端直接订阅完整列表未来如新增 `public_work_gallery_entry` 等专用公开作品列表 read model前端只可订阅稳定、低基数、公开的专用投影禁止订阅 `puzzle_work_profile``custom_world_profile` 等玩法源表后自行 join、聚合或判断权限。前端直订阅落地前必须先补齐权限、字段契约、排序 / 分页、埋点和 BFF 回退策略。
- 50 HTTP req/s 验收目标为 `http_req_failed < 1%``p95 < 2s``dropped_iterations = 0`,同时压测窗口内 Nginx 无新增 502。2026-05-19 容器 2C / 2G 连续 10 轮不重启 SpacetimeDB 压测:`PEAK_RPS=2500` 等价约 5000 HTTP req/s平均实际吞吐约 `4219 HTTP req/s`10 轮总计 `1,897,357` 个 200、`212,542` 个 429、`0` 个 5xx200 请求平均 `p95=123ms``p99=234ms`;该档会把 SpacetimeDB 容器内存从约 `366MiB` 推到约 `885MiB / 896MiB`,因此当前不要继续抬公开 gallery 入口并发,应优先处理 SpacetimeDB 侧连接 / 订阅 / tracking 写入后的内存高水位。

View File

@@ -437,11 +437,55 @@ validate_nginx_tls() {
fi
}
disable_nginx_default_sites_enabled() {
local moves_file="$1"
local sites_enabled="/etc/nginx/sites-enabled"
local sites_disabled="/etc/nginx/sites-disabled"
local stamp source target base
local candidates=("${sites_enabled}/default" "${sites_enabled}/default."*)
stamp="$(date +%Y%m%d%H%M%S)"
for source in "${candidates[@]}"; do
if [[ ! -e "${source}" && ! -L "${source}" ]]; then
continue
fi
base="$(basename "${source}")"
target="${sites_disabled}/${base}.disabled-${stamp}"
echo "[server-provision] 禁用 Debian 默认 Nginx 站点,避免与 Genarrative server_name 冲突: ${source} -> ${target}"
mkdir -p "${sites_disabled}"
mv "${source}" "${target}"
printf "%s\t%s\n" "${target}" "${source}" >>"${moves_file}"
done
}
restore_nginx_default_sites_enabled() {
local moves_file="$1"
local target source
if [[ ! -f "${moves_file}" ]]; then
return
fi
while IFS=$'\t' read -r target source || [[ -n "${target:-}" ]]; do
if [[ -z "${target:-}" || -z "${source:-}" ]]; then
continue
fi
if [[ -e "${target}" || -L "${target}" ]]; then
mkdir -p "$(dirname "${source}")"
if [[ ! -e "${source}" && ! -L "${source}" ]]; then
echo "[server-provision] 恢复 Debian 默认 Nginx 站点: ${target} -> ${source}"
mv "${target}" "${source}"
fi
fi
done <"${moves_file}"
}
install_nginx_config_with_rollback() {
local config_target="/etc/nginx/conf.d/genarrative.conf"
local snippet_target="/etc/nginx/snippets/genarrative-maintenance.conf"
local config_source
local rendered_config rendered_snippet config_backup snippet_backup
local rendered_config rendered_snippet config_backup snippet_backup disabled_sites
local had_config="false"
local had_snippet="false"
@@ -459,6 +503,7 @@ install_nginx_config_with_rollback() {
echo "+ install -m 0644 deploy/nginx/snippets/genarrative-maintenance.conf ${snippet_target}"
if [[ "${DRY_RUN}" == "true" ]]; then
echo "+ disable /etc/nginx/sites-enabled/default* if present"
echo "+ nginx -t"
echo "+ nginx -s reload"
return
@@ -468,6 +513,7 @@ install_nginx_config_with_rollback() {
rendered_snippet="$(mktemp)"
config_backup="$(mktemp)"
snippet_backup="$(mktemp)"
disabled_sites="$(mktemp)"
if [[ "${NGINX_CONFIG_MODE}" == "production-https" ]]; then
validate_nginx_tls
render_nginx_https_config >"${rendered_config}"
@@ -487,6 +533,7 @@ install_nginx_config_with_rollback() {
install -m 0644 "${rendered_config}" "${config_target}"
install -m 0644 "${rendered_snippet}" "${snippet_target}"
disable_nginx_default_sites_enabled "${disabled_sites}"
if ! nginx -t; then
echo "[server-provision] nginx -t 失败,恢复写入前的 Nginx 配置。" >&2
@@ -500,13 +547,14 @@ install_nginx_config_with_rollback() {
else
rm -f "${snippet_target}"
fi
rm -f "${rendered_config}" "${rendered_snippet}" "${config_backup}" "${snippet_backup}"
restore_nginx_default_sites_enabled "${disabled_sites}"
rm -f "${rendered_config}" "${rendered_snippet}" "${config_backup}" "${snippet_backup}" "${disabled_sites}"
exit 1
fi
echo "+ nginx -s reload"
nginx -s reload
rm -f "${rendered_config}" "${rendered_snippet}" "${config_backup}" "${snippet_backup}"
rm -f "${rendered_config}" "${rendered_snippet}" "${config_backup}" "${snippet_backup}" "${disabled_sites}"
}
cleanup_placeholder_nginx_config() {