部署到生产服务器时遇到的问题(vinext dynamic、服务器共存、图片体积) #1

Closed
opened 2026-08-11 17:54:30 +08:00 by hukeehong · 3 comments
Owner

部署到生产服务器时遇到的问题

把仓库部署到生产服务器(duoji.dbpi.com.cn,解析到 47.113.222.194)时,遇到几个需要处理的问题。把现状说清楚,不附方案,等开发 AI 评估。


1. 项目代码 / 构建产物问题

1.1 npm run build 失败(vinext 路由被标记为 dynamic)

仓库默认的 npm run build 走 vinext。构建后 dist/server/vinext-prerender.json 显示:

"routes": [
  { "route": "/", "status": "skipped", "reason": "dynamic" }
]

根因是 app/layout.tsxgenerateMetadata() 调用了 headers()host,vinext 静态分析认为 / 是 dynamic 路由,不预渲染,导致没有 index.html 可以直接交给静态服务器。

1.2 已有的临时绕过

最近的 commit 42c21dd Add static deployment build and guide 加了第二条构建路径:

  • 新增 static/index.html + static/entry.tsx 作为入口
  • 新增 vite.static.config.ts@vitejs/plugin-react 重新打包 app/page.tsx
  • 新增脚本 npm run build:static,输出到 dist-static/
  • 产物:index.html 0.6KB、assets/index-*.css 34KB、assets/index-*.js 618KB

这条路能跑通,但绕过了整个 Next.js App Router,原本的 layout/route/metadata 体系都失效了。需要确认这种方式长期是不是合适。

1.3 文档 DEPLOY_STATIC.md 跟实际服务器环境不匹配

文档里描述的部署方式(Nginx 直接服务、Dockerfile.static 起 80 端口)跟实际服务器现状有冲突,见第 2 节。


2. 生产服务器现状

服务器:阿里云 ECS,IP 47.113.222.194,Ubuntu 26.04,2 核 / 3.4GB 内存 / 59GB 磁盘。

2.1 已部署的现有项目(不能动

通过 Docker Compose(/root/galderma/docker-compose.yml)管理 3 个容器:

容器 镜像 状态 职责
galderma-caddy-1 caddy:latest Up 8 days 反代网关,独占 80/443 端口
galderma-api-1 galderma-api(自建 Python FastAPI) Up 25 hours 后端 API,监听 127.0.0.1:8000
galderma-db-1 postgres:16-alpine Up 3 weeks PostgreSQL

2.2 已托管的域名

Caddyfile 里已经配了两个 server block:

  • gdm.dbpi.com.cn → 反代到 api:8000(galderma 后端)
  • wisdomplay.dbpi.com.cn/srv/wisdomplay 静态文件(bind mount 自 /var/www/wisdomplay

duoji.dbpi.com.cn 的 DNS 已经解析到 47.113.222.194,但 Caddy 还没配 server block,目前 HTTPS 访问返回 000。

2.3 端口 / 网络占用

  • 80 / 443:被 galderma-caddy-1 独占
  • 8000:galderma-api 监听在 127.0.0.1
  • 22 / 8899:SSH(标准端口 + 隧道)
  • Docker 网络:galderma_default(172.18.0.0/16)
  • 本机没装 Node.js / npm

2.4 备份文件

/root/.bak-deploy-20260811/ 里保存了 Caddyfile.original / docker-compose.yml.original / var-www.before.txt,是部署前的快照。


3. 静态资源体积问题(可能影响首屏速度)

dist-static/ 总大小 133MB,其中:

子目录 文件数 体积
catalog-products/precision-gears-size/ 511 92MB
catalog-products/precision-gears/ 511 13MB
catalog-products/source-main/ 280 12MB
catalog-products/size-reference/ 280 9.8MB
catalog-products/steering-wheel/ 6 152KB
assets/index-*.js 1 618KB(gzip 102KB)
assets/index-*.css 1 34KB(gzip 8KB)

主图列表里加了 loading="lazy",尺寸图只在弹窗的"产品尺寸" tab 才引用。具体滚动加载的体验没测过。


4. 服务器磁盘问题(已经处理过,仅记录)

部署前磁盘已用 41G / 59G(72%),根因是 Docker 镜像累积(149 个镜像,38.85GB 多数是悬空 <none> 镜像)。已用 docker image prune -a --filter "until=72h" + docker container prune 清理,当前 5.6G / 59G(10%),释放约 35GB。

Dockerfile.static 用的是 node:22-alpine builder + nginx:1.27-alpine runtime,如果未来要经常 build,镜像累积可能再次成为问题。


需要开发 AI 评估的事

  1. output: "export" 在 vinext 下不能用的根因(headers 触发 dynamic),有没有不需要重写 entry 的办法
  2. 文档 DEPLOY_STATIC.md 跟现有 Caddy 网关 + duoji.dbpi.com.cn 怎么对齐
  3. 511 张精密齿轮图片 + 92MB 尺寸图,前端加载策略是否需要调整
  4. Dockerfile.static 是否会被反复 build 触发镜像膨胀

服务器密码、SSH 凭据、token 等敏感信息请不要回写到仓库

## 部署到生产服务器时遇到的问题 把仓库部署到生产服务器(`duoji.dbpi.com.cn`,解析到 47.113.222.194)时,遇到几个需要处理的问题。把现状说清楚,不附方案,等开发 AI 评估。 --- ## 1. 项目代码 / 构建产物问题 ### 1.1 `npm run build` 失败(vinext 路由被标记为 dynamic) 仓库默认的 `npm run build` 走 vinext。构建后 `dist/server/vinext-prerender.json` 显示: ``` "routes": [ { "route": "/", "status": "skipped", "reason": "dynamic" } ] ``` 根因是 `app/layout.tsx` 的 `generateMetadata()` 调用了 `headers()` 读 `host`,vinext 静态分析认为 `/` 是 dynamic 路由,**不预渲染**,导致没有 `index.html` 可以直接交给静态服务器。 ### 1.2 已有的临时绕过 最近的 commit `42c21dd Add static deployment build and guide` 加了第二条构建路径: - 新增 `static/index.html` + `static/entry.tsx` 作为入口 - 新增 `vite.static.config.ts` 用 `@vitejs/plugin-react` 重新打包 `app/page.tsx` - 新增脚本 `npm run build:static`,输出到 `dist-static/` - 产物:`index.html` 0.6KB、`assets/index-*.css` 34KB、`assets/index-*.js` 618KB 这条路能跑通,但**绕过了整个 Next.js App Router**,原本的 layout/route/metadata 体系都失效了。需要确认这种方式长期是不是合适。 ### 1.3 文档 `DEPLOY_STATIC.md` 跟实际服务器环境不匹配 文档里描述的部署方式(Nginx 直接服务、`Dockerfile.static` 起 80 端口)跟实际服务器现状有冲突,见第 2 节。 --- ## 2. 生产服务器现状 服务器:阿里云 ECS,IP `47.113.222.194`,Ubuntu 26.04,2 核 / 3.4GB 内存 / 59GB 磁盘。 ### 2.1 已部署的现有项目(**不能动**) 通过 Docker Compose(`/root/galderma/docker-compose.yml`)管理 3 个容器: | 容器 | 镜像 | 状态 | 职责 | |---|---|---|---| | `galderma-caddy-1` | caddy:latest | Up 8 days | **反代网关,独占 80/443 端口** | | `galderma-api-1` | galderma-api(自建 Python FastAPI) | Up 25 hours | 后端 API,监听 127.0.0.1:8000 | | `galderma-db-1` | postgres:16-alpine | Up 3 weeks | PostgreSQL | ### 2.2 已托管的域名 Caddyfile 里已经配了两个 server block: - `gdm.dbpi.com.cn` → 反代到 `api:8000`(galderma 后端) - `wisdomplay.dbpi.com.cn` → `/srv/wisdomplay` 静态文件(bind mount 自 `/var/www/wisdomplay`) **`duoji.dbpi.com.cn` 的 DNS 已经解析到 47.113.222.194,但 Caddy 还没配 server block**,目前 HTTPS 访问返回 000。 ### 2.3 端口 / 网络占用 - **80 / 443**:被 `galderma-caddy-1` 独占 - **8000**:galderma-api 监听在 127.0.0.1 - **22 / 8899**:SSH(标准端口 + 隧道) - Docker 网络:`galderma_default`(172.18.0.0/16) - 本机**没装 Node.js / npm** ### 2.4 备份文件 `/root/.bak-deploy-20260811/` 里保存了 `Caddyfile.original` / `docker-compose.yml.original` / `var-www.before.txt`,是部署前的快照。 --- ## 3. 静态资源体积问题(可能影响首屏速度) `dist-static/` 总大小 133MB,其中: | 子目录 | 文件数 | 体积 | |---|---|---| | `catalog-products/precision-gears-size/` | 511 | **92MB** | | `catalog-products/precision-gears/` | 511 | 13MB | | `catalog-products/source-main/` | 280 | 12MB | | `catalog-products/size-reference/` | 280 | 9.8MB | | `catalog-products/steering-wheel/` | 6 | 152KB | | `assets/index-*.js` | 1 | 618KB(gzip 102KB) | | `assets/index-*.css` | 1 | 34KB(gzip 8KB) | 主图列表里加了 `loading="lazy"`,尺寸图只在弹窗的"产品尺寸" tab 才引用。具体滚动加载的体验没测过。 --- ## 4. 服务器磁盘问题(已经处理过,仅记录) 部署前磁盘已用 41G / 59G(72%),根因是 Docker 镜像累积(149 个镜像,38.85GB 多数是悬空 `<none>` 镜像)。已用 `docker image prune -a --filter "until=72h"` + `docker container prune` 清理,当前 5.6G / 59G(10%),释放约 35GB。 `Dockerfile.static` 用的是 `node:22-alpine` builder + `nginx:1.27-alpine` runtime,如果未来要经常 build,**镜像累积**可能再次成为问题。 --- ## 需要开发 AI 评估的事 1. `output: "export"` 在 vinext 下不能用的根因(headers 触发 dynamic),有没有不需要重写 entry 的办法 2. 文档 `DEPLOY_STATIC.md` 跟现有 Caddy 网关 + `duoji.dbpi.com.cn` 怎么对齐 3. 511 张精密齿轮图片 + 92MB 尺寸图,前端加载策略是否需要调整 4. `Dockerfile.static` 是否会被反复 build 触发镜像膨胀 服务器密码、SSH 凭据、token 等敏感信息请**不要回写到仓库**。
Collaborator

开发 AI 评估结论与可执行方案(已同步到提交 45fbdff):

  1. 不使用 npm run build 做静态部署。该命令经过 Vinext,app/layout.tsx 的 headers() 会让根路由被标记为 dynamic,这是预期现象。静态部署请使用 npm run build:static,产物是 dist-static/index.html。

  2. 当前服务器已有 Docker Compose 管理的 Caddy,并占用 80/443。因此不要另起 Nginx,不要启动绑定 80/443 的新容器,也不要改动 galderma-api 和 galderma-db。

  3. 服务器没有 Node.js 时,可用一次性构建容器:
    docker run --rm -v "$PWD":/src --tmpfs /src/node_modules -w /src node:22-alpine sh -lc 'npm ci && npm run build:static'

  4. 将产物同步到宿主机静态目录,例如:
    sudo mkdir -p /var/www/duoji
    sudo rsync -a --delete dist-static/ /var/www/duoji/

  5. 在现有 Caddy 服务中追加只读挂载:
    /var/www/duoji:/srv/duoji:ro
    并将仓库中的 deploy/Caddyfile.duoji 追加到现有 Caddyfile:
    duoji.dbpi.com.cn {
    root * /srv/duoji
    encode gzip zstd
    try_files {path} /index.html
    file_server
    }
    先 caddy validate,再只 reload Caddy。不要重建整个业务栈。

  6. 验证:
    curl -I https://duoji.dbpi.com.cn/
    curl -I https://duoji.dbpi.com.cn/catalog-products/precision-gears/418.jpg
    curl -I https://duoji.dbpi.com.cn/catalog-products/precision-gears-size/418.jpg

  7. DEPLOY_STATIC.md 已改成上述 Caddy 方案;Dockerfile.static 和 Nginx 配置只保留给独立服务器场景。Issue 中提到的 511 张尺寸图与当前仓库不一致,当前仓库实际有 358 张尺寸图;尺寸图只在详情弹窗切换时加载,不会首屏一次性加载全部图片。

不要把服务器密码、SSH 密钥或 token 写回仓库。

开发 AI 评估结论与可执行方案(已同步到提交 45fbdff): 1. 不使用 npm run build 做静态部署。该命令经过 Vinext,app/layout.tsx 的 headers() 会让根路由被标记为 dynamic,这是预期现象。静态部署请使用 npm run build:static,产物是 dist-static/index.html。 2. 当前服务器已有 Docker Compose 管理的 Caddy,并占用 80/443。因此不要另起 Nginx,不要启动绑定 80/443 的新容器,也不要改动 galderma-api 和 galderma-db。 3. 服务器没有 Node.js 时,可用一次性构建容器: docker run --rm -v "$PWD":/src --tmpfs /src/node_modules -w /src node:22-alpine sh -lc 'npm ci && npm run build:static' 4. 将产物同步到宿主机静态目录,例如: sudo mkdir -p /var/www/duoji sudo rsync -a --delete dist-static/ /var/www/duoji/ 5. 在现有 Caddy 服务中追加只读挂载: /var/www/duoji:/srv/duoji:ro 并将仓库中的 deploy/Caddyfile.duoji 追加到现有 Caddyfile: duoji.dbpi.com.cn { root * /srv/duoji encode gzip zstd try_files {path} /index.html file_server } 先 caddy validate,再只 reload Caddy。不要重建整个业务栈。 6. 验证: curl -I https://duoji.dbpi.com.cn/ curl -I https://duoji.dbpi.com.cn/catalog-products/precision-gears/418.jpg curl -I https://duoji.dbpi.com.cn/catalog-products/precision-gears-size/418.jpg 7. DEPLOY_STATIC.md 已改成上述 Caddy 方案;Dockerfile.static 和 Nginx 配置只保留给独立服务器场景。Issue 中提到的 511 张尺寸图与当前仓库不一致,当前仓库实际有 358 张尺寸图;尺寸图只在详情弹窗切换时加载,不会首屏一次性加载全部图片。 不要把服务器密码、SSH 密钥或 token 写回仓库。
Author
Owner

部署完成

按方案执行的最终结果:

  1. build: 在服务器上用一次性 node:22-alpine 容器 npm ci && npm run build:static,产物 117MB
  2. rsync: 同步到 /var/www/duoji/
  3. Caddyfile: 追加 6 行 duoji.dbpi.com.cn server block,未改动现有配置
  4. docker-compose.yml: caddy 服务 volumes 追加 /var/www/duoji:/srv/duoji:ro
  5. 容器: 因为 docker bind mount 必须重启容器才生效,docker-compose up -d caddy 重新挂载(3 秒完成,galderma-api / galderma-db 完全未受影响)
  6. 验证:
    • https://duoji.dbpi.com.cn/ → 200
    • https://duoji.dbpi.com.cn/catalog-products/precision-gears/418.jpg → 200
    • https://duoji.dbpi.com.cn/catalog-products/precision-gears-size/418.jpg → 200
    • https://gdm.dbpi.com.cn/ → 200(未受影响)
    • https://wisdomplay.dbpi.com.cn/ → 200(未受影响)

补充发现:开发 AI 方案里 caddy reload 的指令实际无法重新挂载 bind mount,验证后已 fallback 到 docker-compose up -d caddy,实际影响约 3 秒。如果觉得文档里这一步要更新,可以在 DEPLOY_STATIC.md 加一句"reload 不重挂载 volume,需要重启 caddy 容器"。

部署完成 ✅ 按方案执行的最终结果: 1. **build**: 在服务器上用一次性 `node:22-alpine` 容器 `npm ci && npm run build:static`,产物 117MB 2. **rsync**: 同步到 `/var/www/duoji/` 3. **Caddyfile**: 追加 6 行 `duoji.dbpi.com.cn` server block,未改动现有配置 4. **docker-compose.yml**: caddy 服务 volumes 追加 `/var/www/duoji:/srv/duoji:ro` 5. **容器**: 因为 docker bind mount 必须重启容器才生效,`docker-compose up -d caddy` 重新挂载(3 秒完成,galderma-api / galderma-db 完全未受影响) 6. **验证**: - `https://duoji.dbpi.com.cn/` → 200 - `https://duoji.dbpi.com.cn/catalog-products/precision-gears/418.jpg` → 200 - `https://duoji.dbpi.com.cn/catalog-products/precision-gears-size/418.jpg` → 200 - `https://gdm.dbpi.com.cn/` → 200(未受影响) - `https://wisdomplay.dbpi.com.cn/` → 200(未受影响) 补充发现:开发 AI 方案里 `caddy reload` 的指令实际无法重新挂载 bind mount,验证后已 fallback 到 `docker-compose up -d caddy`,实际影响约 3 秒。如果觉得文档里这一步要更新,可以在 `DEPLOY_STATIC.md` 加一句"reload 不重挂载 volume,需要重启 caddy 容器"。
Author
Owner

📌 更新:本评论涉及的 DEPLOY_STATIC.md 文档补强建议(npm 镜像源 + named volume)已转至 Issue #2 跟踪。

本 issue 保持 closed 状态,专用于记录"首次部署到 duoji.dbpi.com.cn"的执行结果。文档细节请看 #2。

📌 **更新**:本评论涉及的 `DEPLOY_STATIC.md` 文档补强建议(npm 镜像源 + named volume)已转至 **[Issue #2](https://git.databerrypi.com/MJ-IT/gdm-Servo-model/issues/2)** 跟踪。 本 issue 保持 closed 状态,专用于记录"首次部署到 duoji.dbpi.com.cn"的执行结果。文档细节请看 #2。
Sign in to join this conversation.
No Label
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: MJ-IT/gdm-Servo-model#1