记录客户首装升级坑位排查
This commit is contained in:
parent
487be0bf02
commit
28ae9a6462
@ -214,6 +214,7 @@ docker compose -f docker-compose.prod.yml up -d
|
||||
- `docs/deployment/部署说明.md`
|
||||
- `docs/deployment/多品牌 NAS 与 Linux 交付差异清单.md`
|
||||
- `docs/初次部署与出厂设置指南.md`
|
||||
- `docs/客户首装升级坑位排查_20260530.md`
|
||||
- `docs/许可证服务接入说明.md`
|
||||
- `docs/商业化地基8项收口记录_20260530.md`
|
||||
- `docs/参考源码功能吸收整理_20260513.md`
|
||||
|
||||
@ -19,6 +19,8 @@
|
||||
面向不同 NAS / Linux 客户现场的部署前采集清单。
|
||||
- `初次部署与出厂设置指南.md`
|
||||
从零安装、系统初始化和恢复出厂流程。
|
||||
- `客户首装升级坑位排查_20260530.md`
|
||||
客户真实首装、升级、回滚、许可证服务和对象存储的当前坑位清单。
|
||||
- `新手用户上手指引.md`
|
||||
给普通使用者看的基础操作指引。
|
||||
|
||||
|
||||
362
docs/客户首装升级坑位排查_20260530.md
Normal file
362
docs/客户首装升级坑位排查_20260530.md
Normal file
@ -0,0 +1,362 @@
|
||||
# 客户首装升级坑位排查
|
||||
|
||||
更新时间:2026-05-30
|
||||
|
||||
本文件记录本轮针对客户真实首装、升级、回滚、许可证服务、对象存储和文档真相的只读排查结果。它不是整改完成记录,后续修复时要以当前代码、配置和实际验证结果为准。
|
||||
|
||||
## 1. 结论
|
||||
|
||||
当前项目已经有比较清楚的主线:`docker-compose.prod.yml + .env` 做客户首装,`deploy-registry.ps1` 做版本化发布和已有客户升级。但离“客户能按步骤稳定安装和升级”还有几个硬坑:
|
||||
|
||||
- 首装照 `.env.example` 复制后,主应用可能直接连到错误数据库地址。
|
||||
- 许可证服务的 `docker compose config` 目前用示例环境文件会直接失败。
|
||||
- 主应用许可证激活依赖的关键变量没有进入部署必填清单。
|
||||
- Go Agent 的一键升级链路存在失败后仍像成功、失败后不恢复旧容器的问题。
|
||||
- `deploy-registry.ps1` 的自动回滚只退应用镜像和 `.env`,不退数据库;涉及迁移时不能把它当完整回滚。
|
||||
- 外部 S3 / MinIO、NAS 目录权限、历史对象 ACL 还需要专项演练和审计。
|
||||
|
||||
## 2. P0:会直接卡首装、升级或回滚
|
||||
|
||||
### P0-01 主应用首装可能连错数据库
|
||||
|
||||
客户影响:
|
||||
|
||||
客户按文档执行 `cp .env.example .env` 后,如果没有特别处理 `DATABASE_URL`,生产 Compose 会优先使用占位地址 `postgresql://username:password@host:5432/database`,而不是自动回退到内置 PostgreSQL。结果就是容器启动后数据库连接超时。
|
||||
|
||||
证据:
|
||||
|
||||
- `.env.example:41` 写入了占位 `DATABASE_URL=postgresql://username:password@host:5432/database`。
|
||||
- `docker-compose.prod.yml:113` 会优先使用 `.env` 里的 `DATABASE_URL`。
|
||||
- `docs/deployment/部署说明.md:60` 到 `75` 的必填变量清单没有提醒要删除占位 `DATABASE_URL` 或改成 Compose 内部地址。
|
||||
- 本轮只读验证:`docker compose -f docker-compose.prod.yml --env-file .env.example config` 输出的 `DATABASE_URL` 仍是占位地址。
|
||||
|
||||
建议:
|
||||
|
||||
- 首装用内置 PostgreSQL 时,`.env.example` 不应给一个会被直接使用的错误 `DATABASE_URL`。
|
||||
- 部署说明必须明确两种模式:内置 PostgreSQL 不填 `DATABASE_URL`;外部 PostgreSQL 才填写真实连接串。
|
||||
|
||||
### P0-02 主应用许可证激活缺少真正必填变量
|
||||
|
||||
客户影响:
|
||||
|
||||
服务可能能启动,但首次激活许可证时失败。客户会以为“系统装好了但激活不了”,排查成本高。
|
||||
|
||||
证据:
|
||||
|
||||
- `.env.example:81` 到 `85` 有 `LICENSE_PUBLIC_KEY` 和 `LOCAL_ENCRYPTION_KEY`。
|
||||
- `docs/deployment/部署说明.md:60` 到 `75` 的必填变量没有列这两个变量。
|
||||
- `lib/license/crypto-service.ts` 会检查许可证公钥配置。
|
||||
- `lib/license/license-service.ts` 保存本地许可证时依赖本地加密能力。
|
||||
|
||||
建议:
|
||||
|
||||
- 把 `LICENSE_PUBLIC_KEY`、`LOCAL_ENCRYPTION_KEY` 加入部署必填清单。
|
||||
- 首装脚本或检查脚本应在启动前发现缺失,而不是让客户到激活页才失败。
|
||||
|
||||
### P0-03 许可证服务示例环境文件无法启动 Compose
|
||||
|
||||
客户影响:
|
||||
|
||||
如果客户或实施人员单独部署 `license-server`,按示例 `.env.example` 走,`docker compose config` 就会失败,服务起不来。
|
||||
|
||||
证据:
|
||||
|
||||
- `license-server/docker-compose.yml:29` 强制要求 `LICENSE_REQUEST_SIGNING_SECRET`。
|
||||
- `license-server/.env.example:1` 到 `27` 没有这个变量。
|
||||
- 本轮只读验证:`docker compose -f license-server/docker-compose.yml --env-file license-server/.env.example config` 直接报缺少 `LICENSE_REQUEST_SIGNING_SECRET`。
|
||||
|
||||
建议:
|
||||
|
||||
- 补齐 `license-server/.env.example`。
|
||||
- `license-server/scripts/generate-keys.ts` 也要同步生成或提示这个变量。
|
||||
|
||||
### P0-04 许可证服务示例数据库地址不适配容器
|
||||
|
||||
客户影响:
|
||||
|
||||
即使补上签名密钥,许可证服务容器内访问 `localhost:5432` 也会连到自己,不会连到 Compose 里的 PostgreSQL,仍然启动失败或运行异常。
|
||||
|
||||
证据:
|
||||
|
||||
- `license-server/.env.example:8` 和 `13` 使用 `localhost:5432`。
|
||||
- `license-server/docker-compose.yml:38` 的数据库服务名是 `postgres`。
|
||||
|
||||
建议:
|
||||
|
||||
- Compose 场景下示例地址应使用 `postgres:5432`。
|
||||
- 本地裸机开发和 Compose 部署要分开给示例。
|
||||
|
||||
### P0-05 Go Agent 健康检查超时后仍返回成功
|
||||
|
||||
客户影响:
|
||||
|
||||
Agent 升级时,新容器如果一直不健康,代码仍可能把结果当成功。前端显示“更新完成”,客户实际打不开系统。
|
||||
|
||||
证据:
|
||||
|
||||
- `flnxi-agent-go/internal/docker/client.go:195` 开始等待健康状态。
|
||||
- `flnxi-agent-go/internal/docker/client.go:216` 超时后仍 `return nil`。
|
||||
|
||||
建议:
|
||||
|
||||
- 超时必须返回错误。
|
||||
- 前端和接口要把失败状态明确展示出来。
|
||||
|
||||
### P0-06 Go Agent 升级失败没有恢复旧容器
|
||||
|
||||
客户影响:
|
||||
|
||||
当前更新流程先停旧容器、删旧容器,再创建新容器。新容器失败时没有恢复旧容器,也没有数据库备份/恢复。客户现场可能直接断服务。
|
||||
|
||||
证据:
|
||||
|
||||
- `flnxi-agent-go/internal/api/handlers.go:502` 开始停止当前容器。
|
||||
- `flnxi-agent-go/internal/api/handlers.go:511` 到 `514` 删除旧容器。
|
||||
- `flnxi-agent-go/internal/api/handlers.go:537` 到 `540` 健康失败只记录错误,没有恢复旧容器。
|
||||
|
||||
建议:
|
||||
|
||||
- Agent 升级前要保存旧镜像、旧容器配置和数据备份状态。
|
||||
- 新容器失败时至少自动恢复旧容器;涉及数据库迁移时必须进入人工确认流程。
|
||||
|
||||
### P0-07 发布脚本自动回滚不等于数据库回滚
|
||||
|
||||
客户影响:
|
||||
|
||||
如果新版本已经执行了数据库迁移,之后健康检查失败,`deploy-registry.ps1` 只恢复 `.env` 和旧应用镜像,不恢复数据库。旧应用可能不兼容新库,客户会以为“已回滚”,但系统仍可能不可用。
|
||||
|
||||
证据:
|
||||
|
||||
- `scripts/docker-entrypoint.sh:39` 容器启动时自动执行 `npx prisma migrate deploy`。
|
||||
- `deploy-registry.ps1:621` 到 `637` 会做 `pg_dump` 备份。
|
||||
- `deploy-registry.ps1:591` 到 `618` 的 `rollback()` 只恢复 `.env` 和旧应用镜像。
|
||||
|
||||
建议:
|
||||
|
||||
- 文档和脚本输出必须明确:自动回滚只是应用层回滚,不是完整数据回滚。
|
||||
- 涉及数据库结构变更时,要有单独的恢复演练和人工确认步骤。
|
||||
|
||||
## 3. P1:会造成交付不稳或现场误操作
|
||||
|
||||
### P1-01 远端 `.env` 写入失败时可能不会自动恢复
|
||||
|
||||
证据:
|
||||
|
||||
- `deploy-registry.ps1:688` 开始写远端 `.env`。
|
||||
- `deploy-registry.ps1:698` 才挂载 `trap rollback ERR`。
|
||||
|
||||
影响:
|
||||
|
||||
如果 `.env` 写入中途失败,可能留下半新半旧配置,下次手工 `docker compose up` 会继续踩坑。
|
||||
|
||||
建议:
|
||||
|
||||
- 在任何远端 `.env` 写入前就启用回滚保护。
|
||||
|
||||
### P1-02 手动数据库恢复文档不可直接照做
|
||||
|
||||
证据:
|
||||
|
||||
- `docs/deployment/部署说明.md:250` 到 `254` 把备份写在容器内 `/tmp`。
|
||||
- `docs/deployment/部署说明.md:257` 说要复制到宿主机或外部位置。
|
||||
- `docs/deployment/部署说明.md:281` 到 `285` 恢复时又直接让容器读 `/tmp/flnxi-backup-...dump`,没有给 `docker cp` 或标准输入恢复路径。
|
||||
|
||||
影响:
|
||||
|
||||
真出事故时,照文档恢复大概率找不到备份文件。
|
||||
|
||||
建议:
|
||||
|
||||
- 改成一条可执行路径:要么 `docker cp` 到容器后恢复,要么用宿主机备份文件通过 stdin 管道恢复。
|
||||
|
||||
### P1-03 示例密钥非空,Compose 不会拦截弱默认
|
||||
|
||||
证据:
|
||||
|
||||
- `.env.example:8`、`12`、`43`、`57` 到 `58` 都是 `replace-with...` 占位值。
|
||||
- `docker-compose.prod.yml:21`、`43` 到 `44`、`132` 到 `135` 只检查非空。
|
||||
|
||||
影响:
|
||||
|
||||
客户漏改时系统可能照样启动,带着可猜密钥上线。
|
||||
|
||||
建议:
|
||||
|
||||
- 增加启动前环境检查,发现 `replace-with`、`your-`、空公钥等占位值时直接阻断。
|
||||
|
||||
### P1-04 NAS 挂载目录权限容易卡上传和激活
|
||||
|
||||
证据:
|
||||
|
||||
- `Dockerfile:292` 到 `293` 使用非 root 用户 `nextjs`。
|
||||
- `docker-compose.prod.yml:140` 到 `142` 直接挂载 `./uploads`、`./data`。
|
||||
|
||||
影响:
|
||||
|
||||
NAS 上目录常由宿主机 root 或图形界面用户创建,容器内 `nextjs` 可能没有写权限。客户会在上传素材或保存许可证实例时才发现失败。
|
||||
|
||||
建议:
|
||||
|
||||
- 部署文档给出创建目录和授权步骤。
|
||||
- 更稳的方式是安装脚本自动处理目录权限。
|
||||
|
||||
### P1-05 外部 S3 / MinIO 口径容易误导
|
||||
|
||||
证据:
|
||||
|
||||
- `.env.example:48` 到 `53` 给了外部 S3 写法。
|
||||
- `docker-compose.prod.yml:116` 到 `123` 在应用环境中强制覆盖为内置 `minio:9000` 和 `SERVER_IP:9000`。
|
||||
|
||||
影响:
|
||||
|
||||
客户以为填 `.env` 就能接外部对象存储,实际仍会走内置 MinIO。
|
||||
|
||||
建议:
|
||||
|
||||
- 明确区分“内置 MinIO 模式”和“外部对象存储模式”。
|
||||
- 外部对象存储模式需要单独 compose 覆盖文件或明确删改环境覆盖。
|
||||
|
||||
### P1-06 历史对象权限还没有审计闭环
|
||||
|
||||
证据:
|
||||
|
||||
- `docker-compose.prod.yml:78` 到 `86` 当前 MinIO 初始化会默认关闭匿名下载。
|
||||
- `lib/s3.ts:137` 到 `139`、`184` 到 `186` 只有显式开启时才写 `public-read`。
|
||||
- `docs/商业化地基排查问题清单_20260524.md:121` 已记录外部 S3 或历史对象级 ACL 需要单独审计。
|
||||
|
||||
影响:
|
||||
|
||||
新上传默认更安全,但旧 bucket、外部 S3、历史对象 ACL 可能仍然公开。
|
||||
|
||||
建议:
|
||||
|
||||
- 做一份只读审计脚本或 SOP,检查 bucket policy、对象 ACL、历史公开链接。
|
||||
|
||||
### P1-07 上传预览图和 PSD 展示图仍有大图内存风险
|
||||
|
||||
证据:
|
||||
|
||||
- `app/api/materials/upload/route.ts:574` 预览图使用 `readBuffer()`。
|
||||
- `app/api/materials/upload/route.ts:629` PSD 展示图使用 `readBuffer()`。
|
||||
- `app/api/materials/upload/route.ts:633` 会继续切割展示图。
|
||||
|
||||
影响:
|
||||
|
||||
大图或异常尺寸图片可能拖垮低配 NAS 内存。原始文件已经走流式上传,但图片处理链路还不是完全流式。
|
||||
|
||||
建议:
|
||||
|
||||
- 增加图片尺寸、像素数和单张展示图大小限制。
|
||||
- 对 PSD 展示图切割做更明确的上限和错误提示。
|
||||
|
||||
### P1-08 许可证对外接入文档过期
|
||||
|
||||
证据:
|
||||
|
||||
- `docs/其他项目接入许可证系统说明.md` 仍把自己描述为可单独对外发送的接入说明。
|
||||
- 当前 `docs/许可证服务接入说明.md` 已把 `LICENSE_REQUEST_SIGNING_SECRET`、HMAC 签名、`timestamp/nonce/signature` 作为当前真相。
|
||||
|
||||
影响:
|
||||
|
||||
其他项目照旧文档接入,会缺签名参数,联调失败。
|
||||
|
||||
建议:
|
||||
|
||||
- 更新对外接入文档,补齐签名密钥和签名示例。
|
||||
- 修完前不要单独对外发送旧文档。
|
||||
|
||||
## 4. P2:容易误导、但不一定马上阻断
|
||||
|
||||
### P2-01 仍有不存在或不明确的一键安装口径
|
||||
|
||||
证据:
|
||||
|
||||
- `docker-compose.prod.yml:109` 到 `110` 提到 `install.sh` 会生成 `.env.production`。
|
||||
- 根目录没有客户首装 `install.sh`;存在的是 `flnxi-agent-go/install.sh`。
|
||||
|
||||
影响:
|
||||
|
||||
实施人员容易分不清 Compose 首装和 Agent 安装。
|
||||
|
||||
建议:
|
||||
|
||||
- 删除或改写这段注释。
|
||||
|
||||
### P2-02 Podman 只写目标,没有可执行路径
|
||||
|
||||
证据:
|
||||
|
||||
- `docs/deployment/部署说明.md:53` 到 `54` 写支持 Docker / Podman。
|
||||
- 实际命令主要是 `docker compose`,例如 `docs/deployment/部署说明.md:136`。
|
||||
|
||||
影响:
|
||||
|
||||
只提供 Podman 或图形容器入口的 NAS,客户仍不知道该怎么装。
|
||||
|
||||
建议:
|
||||
|
||||
- 明确当前客户主线是 Docker Compose。
|
||||
- Podman 另开适配文档或验证清单,不要混在主线步骤里。
|
||||
|
||||
### P2-03 历史安全评估仍在讲已修复问题
|
||||
|
||||
证据:
|
||||
|
||||
- `docs/许可证链路安全评估_20260312.md` 仍保留旧结论,内容与当前签名校验实现不一致。
|
||||
- `docs/README.md:45` 到 `50` 虽然标注历史材料,但仍放在活跃索引里。
|
||||
|
||||
影响:
|
||||
|
||||
后续排查会被旧结论带偏。
|
||||
|
||||
建议:
|
||||
|
||||
- 移入归档目录,或在顶部明确“历史快照,不作为当前事实来源”。
|
||||
|
||||
### P2-04 商业化长清单仍混合旧状态和当前状态
|
||||
|
||||
证据:
|
||||
|
||||
- `docs/商业化地基排查问题清单_20260524.md:5` 说自己是事实入口,但又混有已完成、待整改和审计痕迹。
|
||||
- `docs/商业化地基8项收口记录_20260530.md:5` 把阶段性记录抬得过高。
|
||||
|
||||
影响:
|
||||
|
||||
后续接手时容易把旧状态当当前真相。
|
||||
|
||||
建议:
|
||||
|
||||
- 新增“当前开放项”短文档。
|
||||
- 长清单改成内部审计流水,不作为默认事实入口。
|
||||
|
||||
### P2-05 旧构建脚本仍保留 `latest` 口径
|
||||
|
||||
证据:
|
||||
|
||||
- `scripts/build-material-image.ps1:121` 仍会打 `latest`。
|
||||
- `scripts/README.md:39` 已说明它是历史/内部维护备用入口。
|
||||
|
||||
影响:
|
||||
|
||||
内部人员可能误拿 `latest` 当客户升级锚点。
|
||||
|
||||
建议:
|
||||
|
||||
- 继续把客户升级主线限定在 `deploy-registry.ps1` 的固定版本镜像。
|
||||
- 旧脚本仅保留内部兼容用途,必要时移动到 legacy 目录。
|
||||
|
||||
## 5. 已做验证
|
||||
|
||||
- 本轮使用 multi-agent 做只读并行排查,分别覆盖首装入口、升级回滚、文档真相。
|
||||
- 本地执行 `docker compose -f docker-compose.prod.yml --env-file .env.example config`,确认 `DATABASE_URL` 占位值会进入最终配置。
|
||||
- 本地执行 `docker compose -f license-server/docker-compose.yml --env-file license-server/.env.example config`,确认因缺少 `LICENSE_REQUEST_SIGNING_SECRET` 失败。
|
||||
- 子 agent 执行 `deploy-registry.ps1` PowerShell 语法解析,并运行 `npm test -- --runTestsByPath scripts/__tests__/deploy-registry-script.test.ts scripts/__tests__/docker-entrypoint.test.ts --runInBand`,测试通过。
|
||||
- 本轮没有启动客户环境容器,没有写入服务器,没有修改旧参考源码目录。
|
||||
|
||||
## 6. 建议修复顺序
|
||||
|
||||
1. 先修首装会失败的环境变量问题:主应用 `DATABASE_URL`、许可证必填变量、许可证服务 `.env.example`。
|
||||
2. 再修升级会假成功的问题:Go Agent 健康检查、Agent 更新失败恢复、禁止直接传 `:latest` 镜像。
|
||||
3. 再补回滚闭环:发布脚本 trap 顺序、数据库恢复文档、数据库迁移后的回滚口径。
|
||||
4. 再做 NAS/对象存储专项:目录权限、外部 S3 模式、历史 bucket/ACL 审计。
|
||||
5. 最后清文档入口:许可证对外接入文档、历史安全评估、商业化长清单和阶段性记录口径。
|
||||
Loading…
x
Reference in New Issue
Block a user