fix(deploy): 部署脚本内嵌 nginx 配置,运行时覆盖容器内 upstream #1647

Merged
auto-approve-bot merged 1 commits from fix/nginx-volume-in-deploy into develop 2026-09-03 15:53:28 +08:00
Owner

问题

PR #1645 修改了 compose.yml 添加 nginx volume mount,但实际部署并未生效。

根因ci_staging_deploy.sh 使用 docker run 直接启动容器,不走 docker compose。所以 compose.yml 的 volume mount 完全不生效。

加上 CI path filter 将 infra/ 改动判为「纯后端」→ skip_frontend=true → Build Staging Web Image 被 skip → retag 旧镜像 → 镜像内 nginx 配置指向 xiaoxia-api-production → staging web 容器 upstream DNS 失败 → 502。

修复

在部署脚本中内嵌 nginx 配置,部署时写入服务器本地文件,docker run 时 volume mount 到容器内 /etc/nginx/conf.d/default.conf

  • ci_staging_deploy.sh: 内嵌 staging nginx 配置(upstream → xiaoxia-api-staging:8000
  • ci_production_deploy.sh: 内嵌 production nginx 配置(upstream → xiaoxia-api-production:8000
  • 回滚逻辑同步修改,确保回滚时也使用正确配置

与 PR #1645 的关系

PR #1645 修改 compose.yml 的思路仍然有效(给使用 docker compose 的场景提供保障),但当前部署流程不走 compose,所以需要本 PR 补充。两层保护:

  1. 构建时:web.Dockerfile ARG NGINX_CONF 烘焙正确配置(主路径)
  2. 运行时:部署脚本内嵌配置 + volume mount(安全网)
## 问题 PR #1645 修改了 `compose.yml` 添加 nginx volume mount,但实际部署并未生效。 **根因**:`ci_staging_deploy.sh` 使用 `docker run` 直接启动容器,**不走 docker compose**。所以 compose.yml 的 volume mount 完全不生效。 加上 CI path filter 将 `infra/` 改动判为「纯后端」→ `skip_frontend=true` → Build Staging Web Image 被 skip → retag 旧镜像 → 镜像内 nginx 配置指向 `xiaoxia-api-production` → staging web 容器 upstream DNS 失败 → 502。 ## 修复 在部署脚本中**内嵌 nginx 配置**,部署时写入服务器本地文件,`docker run` 时 volume mount 到容器内 `/etc/nginx/conf.d/default.conf`。 - `ci_staging_deploy.sh`: 内嵌 staging nginx 配置(upstream → `xiaoxia-api-staging:8000`) - `ci_production_deploy.sh`: 内嵌 production nginx 配置(upstream → `xiaoxia-api-production:8000`) - 回滚逻辑同步修改,确保回滚时也使用正确配置 ## 与 PR #1645 的关系 PR #1645 修改 compose.yml 的思路仍然有效(给使用 docker compose 的场景提供保障),但当前部署流程不走 compose,所以需要本 PR 补充。两层保护: 1. 构建时:`web.Dockerfile` ARG `NGINX_CONF` 烘焙正确配置(主路径) 2. 运行时:部署脚本内嵌配置 + volume mount(安全网)
xiaoxia added 1 commit 2026-09-03 15:41:53 +08:00
fix(deploy): 部署脚本内嵌 nginx 配置,运行时覆盖容器内 upstream
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 2s
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 3s
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m16s
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 29s
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 23s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 2m16s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 2m28s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 2m46s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m12s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Validate - Style (pull_request) Successful in 3m25s
AI Code Review / AI Code Review (pull_request) Failing after 5m45s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 7m19s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 10m1s
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / CI Gate (pull_request) Successful in 1s
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 8s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 27s
9ad0164348
根因:compose.yml 的 volume mount 不生效,因为 ci_staging_deploy.sh
使用 docker run 直接启动容器,不走 docker compose。
Build Staging Web Image 被 skip 后 retag 旧镜像,镜像内 nginx 配置
指向 production upstream,导致 staging web 容器 502。

修复:在部署脚本中内嵌 nginx 配置(staging/production 各一份),
部署时写入服务器本地文件,docker run 时 volume mount 到容器内
/etc/nginx/conf.d/default.conf,作为运行时安全网覆盖镜像内的配置。

- ci_staging_deploy.sh: 写入 nginx-staging.conf + volume mount
- ci_production_deploy.sh: 写入 nginx-production.conf + volume mount
- 回滚逻辑同步修改,确保回滚时也使用正确配置

🚀 预览环境已部署

项目 详情
PR号 #1647
预览链接 https://pr-1647.preview.xiaoxiajianji.com
API环境 staging

💡 预览环境使用 staging API 数据,请勿在预览环境中操作重要数据。

🔄 每次提交新代码后预览环境会自动更新。

🗑️ PR 关闭或合并后,预览环境会自动清理。

🚀 **预览环境已部署** | 项目 | 详情 | |------|------| | PR号 | #1647 | | 预览链接 | [https://pr-1647.preview.xiaoxiajianji.com](https://pr-1647.preview.xiaoxiajianji.com) | | API环境 | staging | > 💡 预览环境使用 staging API 数据,请勿在预览环境中操作重要数据。 > > 🔄 每次提交新代码后预览环境会自动更新。 > > 🗑️ PR 关闭或合并后,预览环境会自动清理。
Collaborator

【阻塞级判定】

  • 是否存在阻塞级问题:是
  • 阻塞级问题数量:1 个

📊 审查概览

  • 整体评价:需修改
  • 建议级问题数量:3 个

🔴 阻塞级问题(必须修复)

  1. [scripts/ci_production_deploy.sh: 98-107 & scripts/ci_staging_deploy.sh: 97-106] Nginx Location 优先级冲突导致 404
    • 问题类型:逻辑bug
    • 问题描述:Nginx 配置中,正则匹配(location ~* \.(js|css...)$)的优先级高于普通前缀匹配(location /generated-files/)。当访问 /generated-files/report.jpg 这类路径时,请求会被正则规则拦截,导致 Nginx 去 /usr/share/nginx/html/generated-files/ 寻找文件,而不是预期的 /app/generated/,从而导致文件无法访问(404)。
    • 修改建议:将 location /generated-files/ 修改为 location ^~ /generated-files/。使用 ^~ 修饰符可以停止后续的正则表达式匹配,确保该路径下的请求(包括图片等静态资源)能正确命中 alias 规则。

💡 改进建议(不阻塞合并)

  1. [scripts/ci_production_deploy.sh: 76 & scripts/ci_staging_deploy.sh: 69] 缺少父目录创建检查

    • 具体内容:脚本直接写入 $NGINX_CONF_FILE,但未确保其父目录存在。如果 $NGINX_CONF_FILE 被自定义为一个不存在的目录路径,脚本执行会报错失败。建议在写入前添加 mkdir -p "$(dirname "$NGINX_CONF_FILE")"
  2. [scripts/ci_production_deploy.sh: 88 & scripts/ci_staging_deploy.sh: 87] 冗余的 resolver 配置

    • 具体内容:配置中添加了 resolver 127.0.0.11,但 proxy_pass 使用的是静态域名(如 http://xiaoxia-api-production:8000)。Nginx 会在启动时解析一次该域名,resolver 指令仅在 proxy_pass 中使用变量(如 http://$upstream)时才会生效。当前配置下该指令不会产生实际效果,若非为后续变更预留,建议删除以保持配置整洁。
  3. [scripts/ci_production_deploy.sh: 85 & scripts/ci_staging_deploy.sh: 84] 上传大小限制过大

    • 具体内容:client_max_body_size 800m 允许上传非常大的文件。请确认后端服务是否支持处理如此大的请求体,否则可能导致后端崩溃或超时。建议根据实际业务需求调整(例如 50m 或 100m),并确保后端限制一致。

良好实践

  • 使用 << 'NGINX_EOF' 引号包裹 Heredoc 结束符,有效防止了 Shell 变量在配置文件写入时被意外展开,安全性良好。
  • Docker 挂载使用了 :ro(只读)模式,遵循最小权限原则。
  • 静态资源缓存策略配置合理(expires 1y + immutable)。

🤖 由 AI 代码审查机器人自动生成 | 2026-09-03 07:47:41 | 模型:

### 【阻塞级判定】 - 是否存在阻塞级问题:是 - 阻塞级问题数量:1 个 ### 📊 审查概览 - 整体评价:需修改 - 建议级问题数量:3 个 ### 🔴 阻塞级问题(必须修复) 1. **[scripts/ci_production_deploy.sh: 98-107 & scripts/ci_staging_deploy.sh: 97-106] Nginx Location 优先级冲突导致 404** - 问题类型:逻辑bug - 问题描述:Nginx 配置中,正则匹配(`location ~* \.(js|css...)$`)的优先级高于普通前缀匹配(`location /generated-files/`)。当访问 `/generated-files/report.jpg` 这类路径时,请求会被正则规则拦截,导致 Nginx 去 `/usr/share/nginx/html/generated-files/` 寻找文件,而不是预期的 `/app/generated/`,从而导致文件无法访问(404)。 - 修改建议:将 `location /generated-files/` 修改为 `location ^~ /generated-files/`。使用 `^~` 修饰符可以停止后续的正则表达式匹配,确保该路径下的请求(包括图片等静态资源)能正确命中 alias 规则。 ### 💡 改进建议(不阻塞合并) 1. **[scripts/ci_production_deploy.sh: 76 & scripts/ci_staging_deploy.sh: 69] 缺少父目录创建检查** - 具体内容:脚本直接写入 `$NGINX_CONF_FILE`,但未确保其父目录存在。如果 `$NGINX_CONF_FILE` 被自定义为一个不存在的目录路径,脚本执行会报错失败。建议在写入前添加 `mkdir -p "$(dirname "$NGINX_CONF_FILE")"`。 2. **[scripts/ci_production_deploy.sh: 88 & scripts/ci_staging_deploy.sh: 87] 冗余的 resolver 配置** - 具体内容:配置中添加了 `resolver 127.0.0.11`,但 `proxy_pass` 使用的是静态域名(如 `http://xiaoxia-api-production:8000`)。Nginx 会在启动时解析一次该域名,`resolver` 指令仅在 `proxy_pass` 中使用变量(如 `http://$upstream`)时才会生效。当前配置下该指令不会产生实际效果,若非为后续变更预留,建议删除以保持配置整洁。 3. **[scripts/ci_production_deploy.sh: 85 & scripts/ci_staging_deploy.sh: 84] 上传大小限制过大** - 具体内容:`client_max_body_size 800m` 允许上传非常大的文件。请确认后端服务是否支持处理如此大的请求体,否则可能导致后端崩溃或超时。建议根据实际业务需求调整(例如 50m 或 100m),并确保后端限制一致。 ### ✅ 良好实践 - 使用 `<< 'NGINX_EOF'` 引号包裹 Heredoc 结束符,有效防止了 Shell 变量在配置文件写入时被意外展开,安全性良好。 - Docker 挂载使用了 `:ro`(只读)模式,遵循最小权限原则。 - 静态资源缓存策略配置合理(`expires 1y` + `immutable`)。 --- <sub>🤖 由 AI 代码审查机器人自动生成 | 2026-09-03 07:47:41 | 模型: </sub> <!-- AI_CODE_REVIEW_AUTO_COMMENT -->
auto-approve-bot approved these changes 2026-09-03 15:52:57 +08:00
auto-approve-bot left a comment
Collaborator

CI全绿,自动审批通过。

CI全绿,自动审批通过。
auto-approve-bot approved these changes 2026-09-03 15:52:57 +08:00
auto-approve-bot left a comment
Collaborator

CI全绿,自动审批通过。

CI全绿,自动审批通过。
auto-approve-bot merged commit 5474812fab into develop 2026-09-03 15:53:28 +08:00
auto-approve-bot deleted branch fix/nginx-volume-in-deploy 2026-09-03 15:53:29 +08:00

🗑️ 预览环境已清理

PR #1647 已关闭或合并,对应的预览环境已被清理。

如有需要,可以重新打开 PR 来重新生成预览环境。

🗑️ **预览环境已清理** PR #1647 已关闭或合并,对应的预览环境已被清理。 > 如有需要,可以重新打开 PR 来重新生成预览环境。
Sign in to join this conversation.