flnxi-Wu/docs/deployment/部署说明.md

30 KiB
Raw Blame History

FLNXI 素材库部署说明

本文档是当前主应用部署入口,合并了原来的快速部署、部署前检查清单和完整部署说明。

先说结论:

  • 客户或私有化交付默认按 NAS(网络存储) / Linux(服务器系统) 现场参数部署。
  • 固定服务器、固定端口、固定 Registry 地址只属于内部维护示例,不是客户默认交付口径。
  • 客户交付必须显式固定完整 APP_IMAGE,例如 registry.example.com/flnxi/material/flnxi-material:1.0.1;不要把 latest 当成客户部署依据。
  • deploy-registry.ps1 不再默认指向任何测试服务器,发布和远程部署必须显式传入 -RegistryHost,远程部署还必须显式传入 -Server。
  • 不同品牌 NAS 或客户自有 Linux 服务器交付前,先看 docs/deployment/多品牌 NAS 与 Linux 交付差异清单.md。

1. 当前部署形态

主应用生产编排文件:

docker-compose.prod.yml

当前会拉起:

  • postgres:PostgreSQL 数据库
  • minio:MinIO 对象存储
  • minio-init:初始化对象存储桶
  • app:FLNXI 素材库主应用

当前主应用镜像以 Registry 发布链路为准,实际部署时可通过 .env 中的 APP_IMAGE、POSTGRES_IMAGE、MINIO_IMAGE、MINIO_MC_IMAGE 覆盖。容器名、卷名、网络名和端口也支持用 .env 覆盖,下面命令中的 flnxi-material 和 4320 都只是默认值。

APP_IMAGE 是生产启动必填项。这样做是为了让客户现场明确运行哪个版本,避免安装或升级时误拉旧仓库、旧测试镜像或 latest。

根目录 docker-compose.yml 是本地/历史维护入口,不是客户正式安装入口。客户交付、NAS 和 Linux 私有化部署统一以 docker-compose.prod.yml + .env 为准。

当前不会由该 Compose 文件直接拉起:

  • Nginx / Traefik 等反向代理
  • 独立 license-server
  • Node 版或 Go 版安装 Agent

这些组件根据交付场景单独部署或由安装 Agent 编排。

独立许可证服务的默认编排在 license-server/docker-compose.yml。该编排会由 Nginx 对外暴露 9527,再反代到内部 license-server:3000;应用容器不再直接暴露宿主机端口。这样登录和 API 限流配置会进入默认链路,避免验证服务器在低配置机器上被暴力请求直接打到 Next 服务。

2. 部署前准备

2.1 机器条件

建议最低条件:

  • CPU:4 核及以上
  • 内存:8GB 及以上
  • 磁盘:100GB 可用空间
  • 系统:Ubuntu 22.04 LTS、群晖 DSM、飞牛 fnOS 或其他支持 Docker 的 Linux 环境
  • Docker:Docker 24+ 和 Docker Compose V2

当前主线安装命令按 Docker Compose 验证。Podman / 图形化容器套件属于目标兼容方向,但不同 NAS 的网络、卷权限和健康检查差异很大,客户交付前必须单独实机验证,不能直接把本文的 Docker 命令等同为 Podman 已完整支持。

2.2 必填环境变量

部署前至少准备:

POSTGRES_PASSWORD=<generate-a-strong-random-password>
MINIO_ROOT_USER=<generate-a-storage-admin-user>
MINIO_ROOT_PASSWORD=<generate-a-strong-random-password>
APP_IMAGE=registry.example.com/flnxi/material/flnxi-material:1.0.1
SERVER_IP=192.168.x.x
APP_PUBLIC_URL=http://192.168.x.x:4320
NEXTAUTH_URL=http://192.168.x.x:4320
NEXTAUTH_SECRET=<generate-a-strong-random-secret>
S3_BUCKET=flnxi-materials
LICENSE_SERVER_URL=https://your-license-server.example.com
LICENSE_PROJECT_CODE=flnxi-sucai
LICENSE_REQUEST_SIGNING_SECRET=<generate-a-shared-license-request-secret>
LICENSE_PUBLIC_KEY=<license-server-rsa-public-key>
LOCAL_ENCRYPTION_KEY=<generate-a-32-char-or-longer-local-key>
MAX_UPLOAD_FILE_SIZE_BYTES=10737418240
MAX_UPLOAD_TOTAL_FILE_SIZE_BYTES=10737418240
MAX_UPLOAD_FIELD_SIZE_BYTES=2097152

说明:

  • SERVER_IP 必须是用户浏览器能访问到的 NAS / 服务器地址。
  • APP_PUBLIC_URL 必须和最终访问入口一致,用于生成公开访问、分享和回调相关地址。
  • NEXTAUTH_URL 必须和最终访问入口一致。
  • APP_IMAGE 必须写完整镜像站地址和版本号,例如 registry.example.com:5000/flnxi/material/flnxi-material:1.0.1;不要只写 flnxi-material:1.0.1 或 latest。
  • POSTGRES_IMAGE、MINIO_IMAGE、MINIO_MC_IMAGE 建议同样固定版本;离线或受控网络交付时,应写成私有 Registry 里的固定版本镜像。
  • LICENSE_PROJECT_CODE 必须和 license-server 中项目表的 projects.code 一致。
  • LICENSE_REQUEST_SIGNING_SECRET 必须和 license-server 的同名变量一致,至少 32 字符,用于主应用到验证服务器的请求签名。
  • LICENSE_PUBLIC_KEY 是许可证服务签发许可证时使用的 RSA 公钥,主应用靠它确认许可证没有被伪造。
  • LOCAL_ENCRYPTION_KEY 用于主应用本地加密保存许可证缓存,至少 32 字符,不能使用示例值。
  • 付费模块核心本体走镜像站,不走验证服务器大文件下载;license-server 需要配置 PREMIUM_MODULE_REGISTRY_BASE 和 PREMIUM_MODULE_VERSION,用于给客户实例下发签名模块清单。
  • MAX_UPLOAD_FILE_SIZE_BYTES 是单个素材文件上限;MAX_UPLOAD_TOTAL_FILE_SIZE_BYTES 是一次上传请求总上限;MAX_UPLOAD_FIELD_SIZE_BYTES 是表单文字字段总上限。示例值分别是 10GB、10GB、2MB,低配 NAS 可按客户机器内存和磁盘调小。
  • 不要把真实密码、许可证地址、对象存储密钥写入 Git。

数据库有两种模式:

  • 内置 PostgreSQL:DATABASE_URL 留空或不写,由 docker-compose.prod.yml 按 POSTGRES_USER、POSTGRES_PASSWORD、POSTGRES_DB 自动拼接到 postgres 服务。
  • 外部 PostgreSQL:才填写真实 DATABASE_URL,例如 postgresql://flnxi:<password>@192.168.1.10:5432/flnxi?sslmode=disable。
  • 商业首版更推荐内置 PostgreSQL,升级脚本可以自动备份和回滚。若客户坚持使用外部 PostgreSQL,升级前必须先完成客户侧数据库备份;执行 deploy-registry.ps1 时需显式加 -ExternalDataBackupConfirmed,否则脚本会中止升级。

对象存储也有两种模式:

  • 内置 MinIO:S3_ENDPOINT=http://minio:9000,S3_ACCESS_KEY_ID / S3_SECRET_ACCESS_KEY 可不填,Compose 会使用 MINIO_ROOT_USER / MINIO_ROOT_PASSWORD。S3_PUBLIC_DOMAIN 必须写成用户浏览器能访问的地址,例如 http://192.168.1.20:9000/flnxi-materials。
  • 内置 MinIO 的 API 端口默认通过 MINIO_API_BIND_HOST=0.0.0.0 对浏览器可访问地址开放,用于素材预览和下载;管理控制台端口默认通过 MINIO_CONSOLE_BIND_HOST=127.0.0.1 只绑定本机。只有明确需要从局域网访问 MinIO 管理界面时,才把 MINIO_CONSOLE_BIND_HOST 改成服务器局域网 IP,并配合防火墙限制来源。
  • 外部 S3 / 外部 MinIO:需要填写真实 S3_ENDPOINT、S3_ACCESS_KEY_ID、S3_SECRET_ACCESS_KEY、S3_BUCKET、S3_PUBLIC_DOMAIN,并确认 bucket 已存在或当前账号有创建和读写权限。
  • 商业首版更推荐内置 MinIO,升级脚本可以自动备份素材对象。若使用外部 S3 / MinIO,升级前必须先完成客户侧 bucket / 对象备份;执行 deploy-registry.ps1 时同样需显式加 -ExternalDataBackupConfirmed。

NAS 或 Linux 服务器上建议提前准备挂载目录:

mkdir -p uploads data
chown -R 1001:1001 uploads data
chmod 775 uploads data

如果客户系统启用了更严格的用户权限,需让运行主应用容器的用户可以写入这两个目录;当前镜像内应用用户通常按 UID 1001 理解。飞牛 / NAS 场景里,第一次手工创建目录如果是 root 所有权,单纯 chmod 775 可能还是不够,优先把目录属主改成 1001:1001。最简单的验收标准是:容器启动后能上传素材,uploads/ 和 data/ 没有持续出现权限拒绝日志。

2.3 HTTP Registry 前置检查

如果 Registry 使用 http://,本机 Docker 和目标机器 Docker 都要先允许这个 Registry。否则会出现 HTTP response to HTTPS client,表现为登录、推送或拉取镜像失败。

Linux 目标机常见配置:

{
  "insecure-registries": ["registry.example.com:5000"]
}

配置位置通常是 /etc/docker/daemon.json。改完后重启 Docker:

sudo systemctl restart docker
docker info | grep -A 5 "Insecure Registries"

Docker Desktop 需要在设置里的 Docker Engine 配置同样的 insecure-registries,保存并重启 Docker。

3. 标准部署步骤

3.1 区分首装和升级

当前交付链路分三段看,不能混成一个命令:

  • 发布方构建和推送镜像:使用 deploy-registry.ps1 的发布动作,产出固定版本镜像。
  • 客户首次安装:在目标机器准备部署目录、docker-compose.prod.yml、.env、数据卷和固定版本镜像,然后启动 Compose。
  • 已有客户升级:使用 deploy-registry.ps1 -Action DeployApp 写入新 APP_IMAGE,并执行备份、拉取、迁移、健康检查和失败回滚。

deploy-registry.ps1 -Action DeployApp 默认假设远端已经有部署目录、生产 Compose 和 .env,它是升级/远程更新入口,不是单独完整的首装向导。

3.2 准备环境文件

在目标机器部署目录准备 .env:

cp .env.example .env

再按实际环境补齐数据库、MinIO、NextAuth、许可证和访问地址。首装使用内置 PostgreSQL 时,不要给 DATABASE_URL 填占位连接串;外部 PostgreSQL 才填写真实连接串。

3.3 启动服务

正式客户交付先通过 deploy-registry.ps1 完成版本化镜像发布,并把远端 .env 里的 APP_IMAGE 写到目标版本,再执行下面的启动命令。

docker compose -f docker-compose.prod.yml up -d

生产 Compose 只读取 APP_IMAGE,不包含 build:。如果需要重新构建镜像,应在发布方机器使用 deploy-registry.ps1 或内部构建脚本先生成版本镜像,再更新 .env。

离线或受控网络交付时,启动前还要确认 APP_IMAGE、POSTGRES_IMAGE、MINIO_IMAGE、MINIO_MC_IMAGE 都已经在目标机器 Docker 或客户私有 Registry 中存在。否则 docker compose up -d 会因为无法拉取镜像而失败。

3.4 初始化数据库

主应用容器入口脚本 scripts/docker-entrypoint.sh 会在启动时自动执行:

npx prisma migrate deploy

所以常规部署不需要手动再跑一次数据库迁移。

如果历史模板数据需要补齐,或自动迁移失败后要人工补救,再按实际容器名执行:

docker exec <APP_CONTAINER_NAME> npm run migrate:template-data
docker exec <APP_CONTAINER_NAME> npm run verify:template-data

默认容器名是 flnxi-material;如果 .env 配了 APP_CONTAINER_NAME,以实际值为准。

如是全新环境,继续按 docs/初次部署与出厂设置指南.md 走系统初始化流程。

3.5 验证服务

docker compose -f docker-compose.prod.yml ps
curl http://<SERVER_IP>:4320/api/health
docker logs flnxi-material --tail 100

/api/health 返回 200 表示应用启动检查已通过;如果返回 503,先查看 docker logs flnxi-material --tail 100,通常是数据库连接、迁移或内置模板初始化未完成。

如果 .env 覆盖了 APP_PORT 或 APP_CONTAINER_NAME,验证命令也要同步替换成实际端口和容器名。

页面验证:

  • 登录页:http://<SERVER_IP>:4320/login
  • 首页:http://<SERVER_IP>:4320
  • 管理后台:http://<SERVER_IP>:4320/admin
  • 模板管理:在 http://<SERVER_IP>:4320/admin 后台内打开;当前没有独立的 /admin/templates 路由

4. 版本化 Registry 发布

当前仓库的主发布脚本是 deploy-registry.ps1,用于构建、推送和远程更新主应用、Agent 以及运行时镜像。它默认会把版本镜像写入远端 .env,客户升级不要再回退到默认 latest。

DeployApp 远程部署现在默认包含第一轮升级保护:

  • 升级前在远端创建 backups/deploy-时间戳/,保存升级前 .env。
  • 发现已有 app 容器运行时,会先短暂停止 app,让系统进入升级维护窗口,减少备份期间继续上传、编辑或删除素材造成的数据库和对象存储不一致。
  • 如果远端 PostgreSQL 是当前 Compose 管理的 postgres 服务,会先执行 pg_dump 备份;如果使用外部数据库,会提示需要确认外部备份。
  • 如果远端 MinIO 是当前 Compose 管理的 minio 服务,会先把 MINIO_VOLUME_NAME 对应的数据卷打包备份;如果使用外部 S3 / 外部 MinIO,会在备份目录写入提示文件,要求按客户现场方案单独备份对象存储。
  • 备份目录会生成 RESTORE_PLAN.txt,把本次 .env、PostgreSQL、MinIO 备份文件和 L1/L2/L3 恢复命令列出来,便于升级失败时按影响范围恢复。
  • 部署失败时默认尝试恢复升级前 .env,并按旧运行镜像或旧 APP_IMAGE 重建应用容器。
  • 部署后会在远端校验应用容器实际运行镜像、镜像内 VERSION.txt 和 Prisma 迁移状态,然后再做外部 /api/health 检查。

只有在已有其它备份和回滚体系时,才考虑使用 -NoRemoteDbBackup、-NoRemoteObjectStorageBackup 或 -NoRollback。离线客户现场如果没有 busybox:1.36,需要先把该 helper 镜像放到目标机,或通过 -RemoteBackupHelperImage 指定现场已有的小型 Linux 镜像。

必须注意:脚本的自动回滚只恢复应用镜像和 .env,不自动恢复数据库和对象存储。只要本次升级包含数据库迁移或素材文件结构变化,升级前的数据库备份和对象存储备份就必须保留,并按 RESTORE_PLAN.txt 的 L2/L3 步骤由人工确认后恢复。

典型用法:

$registryPassword = Read-Host 'Registry密码' -AsSecureString
$sshPassword = Read-Host 'SSH密码' -AsSecureString
.\deploy-registry.ps1 -Action DeployApp -DeployChannel release -Version 1.0.1 -RegistryHost registry.example.com:5000 -Server 192.168.1.20 -RegistryPassword $registryPassword -Password $sshPassword
.\deploy-registry.ps1 -Action PublishRelease -Version 1.0.1 -RegistryHost registry.example.com:5000 -RegistryPassword $registryPassword
.\deploy-registry.ps1 -Action PublishAgent -Version 1.0.1 -RegistryHost registry.example.com:5000 -RegistryPassword $registryPassword
.\deploy-registry.ps1 -Action SyncRuntime -RegistryHost registry.example.com:5000 -RegistryPassword $registryPassword

说明:

  • 客户升级和内部发布优先按版本 tag 走,不要把 latest 当成客户部署依据。
  • 客户现场部署使用 -DeployChannel release -Version x.x.x;测试通道只用于内部验证,必须显式传 -DeployChannel test。
  • 客户 SSH 端口、登录用户和部署目录必须按现场显式确认;脚本默认端口是常见的 22,默认目录是 /opt/flnxi,内部测试机或自定义环境要显式传 -SshPort、-Username、-RemotePath。
  • DeployApp 远程部署时默认会更新远端 .env 的 APP_IMAGE、访问地址和运行时镜像;只有临时验证时才使用 -NoUpdateRemoteEnv。
  • DeployApp 默认会做远端维护窗口停写入、.env 备份、Compose PostgreSQL 备份、Compose MinIO 备份、恢复分级说明、失败自动回滚、运行镜像校验、版本文件校验和 Prisma 迁移状态校验。
  • 旧的 deploy-new-fixed.ps1 仍可作为内部维护参考,但不再作为当前主线部署入口。
  • 如果要做远程更新,先确认 Registry、SSH、.env 和目标机器都属于当前维护环境。

4.1 镜像体积基线

发布前可以对主应用镜像做一次体积和分层记录:

.\scripts\measure-image-size.ps1 `
  -Image "registry.example.com/flnxi/material/flnxi-material:1.0.1" `
  -OutputPath ".\reports\image-size-1.0.1.md" `
  -MaxImageSizeMB 650 `
  -MaxLayerCount 80 `
  -MaxLargestLayerMB 250

这个脚本不会改镜像,只会读取 Docker 本地镜像信息,列出总大小、层数和最大的若干层。传入 -MaxImageSizeMB、-MaxLayerCount 或 -MaxLargestLayerMB 后,超出阈值会以非 0 退出,适合发布前挡住明显变胖的镜像。后续做镜像瘦身时,先用这个报告确认是 ffmpeg、字体、canvas、node_modules 还是 Prisma 迁移工具占用更大,再决定是否拆成可选能力或独立迁移镜像。

5. 部署前检查清单

  • 已确认目标机器品牌、系统和 Docker Compose 支持情况;如使用 Podman,已单独实机验证
  • 已确认端口、域名、证书、反向代理方案
  • 已确认数据目录、对象存储目录和备份位置
  • 已确认升级失败时使用 L1 应用回滚、L2 数据库恢复还是 L3 数据库+素材恢复
  • .env 已填写真实环境变量;内置 PostgreSQL 模式下 DATABASE_URL 没有错误占位值
  • APP_IMAGE 已固定到完整版本镜像,不使用 latest
  • 如果 Registry 是 HTTP,本机和目标机 Docker 都已配置 insecure-registries
  • NEXTAUTH_URL 与最终访问地址一致
  • S3_BUCKET、S3_PUBLIC_DOMAIN 口径正确
  • LICENSE_SERVER_URL 和 LICENSE_PROJECT_CODE 已确认
  • LICENSE_REQUEST_SIGNING_SECRET 已配置且与许可证服务一致
  • LICENSE_PUBLIC_KEY、LOCAL_ENCRYPTION_KEY 已配置为真实值
  • uploads/、data/ 目录对应用容器可写
  • 已执行与本次改动相关的测试或命令
  • 涉及数据库变更时已有备份和回滚方案

6. 部署后冒烟测试

NAS / Linux 客户现场优先使用 .sh 脚本。如果当前账号没有 Docker 权限,就用 sudo bash ... 执行。

启动前可以先做一轮不启动容器的安装前检查:

bash scripts/smoke-install.sh --project-dir /opt/flnxi --preflight-only

如果希望给实施人员一个更像向导的入口,可以先只看步骤:

bash scripts/customer-install.sh --project-dir /opt/flnxi --plan-only

确认 .env、镜像和数据目录都准备好后,让脚本按步骤预检、启动并验收:

sudo bash scripts/customer-install.sh --project-dir /opt/flnxi --prepare-directories --start --expect-version 1.0.1

customer-install.sh 只是对 smoke-install.sh 的分步骤包装,不替代 docker-compose.prod.yml,也不把 Agent 当成唯一安装入口。--prepare-directories 在 root / sudo 运行时会把 uploads/ 和 data/ 调整为应用容器 UID 1001 可写,能提前拦截飞牛 / NAS 常见目录权限问题。

Windows 实施机或已安装 PowerShell 的现场,可以使用对应 .ps1 脚本:

.\scripts\smoke-install.ps1 -ProjectDir "D:\deploy\flnxi" -PreflightOnly

如果希望给实施人员一个更像向导的入口,可以先只看步骤:

.\scripts\customer-install.ps1 -ProjectDir "D:\deploy\flnxi" -PlanOnly

确认 .env、镜像和数据目录都准备好后,让脚本按步骤预检、启动并验收:

.\scripts\customer-install.ps1 -ProjectDir "D:\deploy\flnxi" -PrepareDirectories -Start -ExpectedVersion 1.0.1

customer-install.ps1 只是对 smoke-install.ps1 的分步骤包装,不替代 docker-compose.prod.yml,也不把 Agent 当成唯一安装入口。

如果是新目录,还希望脚本顺手创建 uploads/ 和 data/,可以加:

.\scripts\smoke-install.ps1 -ProjectDir "D:\deploy\flnxi" -PreflightOnly -PrepareDirectories

这一步会提前拦截 .env 示例值、latest 镜像、缺失数据目录和明显错误的访问地址,避免等容器启动失败后再排查。

首装后优先使用脚本做基础验收:

.\scripts\smoke-install.ps1 -ProjectDir "D:\deploy\flnxi" -Start -ExpectedVersion 1.0.1

如果服务已经由 NAS 图形界面或其他编排入口启动,可以去掉 -Start,只做检查:

.\scripts\smoke-install.ps1 -ProjectDir "D:\deploy\flnxi"

这些脚本会先做安装前检查,再检查 Compose 配置、容器状态、应用运行镜像、镜像内版本文件、Prisma 迁移状态、MinIO bucket 和 /api/health。建议每次首装或升级验收都带上期望版本,.sh 使用 --expect-version x.x.x,.ps1 使用 -ExpectedVersion x.x.x,脚本会同时确认容器内 VERSION.txt 和浏览器访问入口 /api/health 返回的版本,避免现场访问地址指到旧服务。如果客户使用外部对象存储,.sh 脚本加 --skip-object-storage-check,.ps1 脚本加 -SkipObjectStorageCheck,对象存储验收按客户现场方案单独执行。

人工验收命令仍保留如下:

  • /api/health 正常
  • 登录 / 退出正常
  • 首次初始化或激活流程正常
  • 首页素材列表可加载
  • 素材上传、预览、下载正常
  • 后台模板管理可打开
  • 系统设置可读取和保存
  • 许可证状态显示正确
  • 容器日志没有持续错误

如果需要一条命令做客户业务验收,可在实施机或部署机上运行:

npm run smoke:customer -- \
  --base-url http://<SERVER_IP>:4320 \
  --expect-version 1.0.1

提供临时验收账号后,可以继续验登录、后台权限和模板接口:

npm run smoke:customer -- \
  --base-url http://<SERVER_IP>:4320 \
  --expect-version 1.0.1 \
  --email <smoke-admin-email> \
  --password <smoke-admin-password>

需要真实验证数据库和 MinIO 写入时,加 --write。该动作会创建一条小素材验收记录,建议只在测试环境或客户允许的验收窗口执行:

npm run smoke:customer -- \
  --base-url http://<SERVER_IP>:4320 \
  --expect-version 1.0.1 \
  --email <smoke-admin-email> \
  --password <smoke-admin-password> \
  --write

不要把验收账号密码写入文档、Git 或聊天记录;如果使用 .env.smoke,该文件也必须留在本机,不进入仓库。

7. 数据备份与回滚

deploy-registry.ps1 -Action DeployApp 会默认做一轮升级前备份和失败回滚。下面命令主要用于人工兜底、外部数据库场景或单独恢复演练。

7.1 一键备份

首选脚本:

NAS / Linux:

sudo bash scripts/backup-restore.sh --project-dir /opt/flnxi --action backup

Windows / PowerShell:

.\scripts\backup-restore.ps1 -ProjectDir "D:\deploy\flnxi" -Action Backup

默认备份内容:

  • .env
  • docker-compose.prod.yml
  • VERSION.txt,如果部署目录里存在
  • compose-config.txt、compose-ps.txt、docker-images.txt
  • Compose 管理的 PostgreSQL:postgres.sql
  • Compose 管理的 MinIO 数据卷:minio-data.tar.gz
  • 本地挂载目录:data.zip、uploads.zip,用于保留许可证实例数据和历史本地上传残留
  • manifest.json:记录备份时间、应用镜像、访问地址、服务名等关键信息

备份脚本默认也会在备份期间短暂停止 app,并在备份结束后恢复启动,目的是让数据库、MinIO、data/、uploads/ 尽量处在同一个时间点。只有客户现场已经有其它停写入机制时,才考虑加 -SkipStopAppForBackup。

备份包里会包含 .env、数据库内容和客户素材文件,通常也就包含访问密钥、许可证缓存和企业内部资料。备份目录必须放在受控位置,按客户现场要求加密或限权保存,不能提交到 Git,也不能随意发到聊天工具。

DeployApp 升级时生成的 backups/deploy-时间戳/RESTORE_PLAN.txt 是升级恢复指引;backup-restore.ps1 -Action Backup 生成的 flnxi-backup-时间戳/manifest.json 是完整备份包记录。两者用途不同,客户升级失败时优先看当次升级目录里的 RESTORE_PLAN.txt。

如果 DATABASE_URL 指向外部 PostgreSQL,脚本不会假装自动备份,会在备份目录写入 EXTERNAL_DATABASE_BACKUP_REQUIRED.txt,客户现场必须按外部数据库方案单独备份。

如果对象存储是外部 S3 / 外部 MinIO,或现场已有独立对象存储快照,可加:

sudo bash scripts/backup-restore.sh --project-dir /opt/flnxi --action backup --skip-object-storage-backup
.\scripts\backup-restore.ps1 -ProjectDir "D:\deploy\flnxi" -Action Backup -SkipObjectStorageBackup

内置 MinIO 备份/恢复默认使用 busybox:1.36 作为 helper 镜像。离线客户现场需要提前把该镜像放进 Docker,或通过 -HelperImage 指定现场已有的 Linux 工具镜像。

7.2 一键验收

NAS / Linux:

bash scripts/backup-restore.sh --project-dir /opt/flnxi --action verify

Windows / PowerShell:

.\scripts\backup-restore.ps1 -ProjectDir "D:\deploy\flnxi" -Action Verify

该动作会检查 Compose 配置、容器状态和 /api/health,适合备份前、恢复后、升级后快速确认系统是否可用。

7.3 一键恢复

恢复会覆盖客户数据,必须停写入并确认备份来源正确。脚本要求输入固定确认短语,避免误操作:

NAS / Linux:

sudo bash scripts/backup-restore.sh \
  --project-dir /opt/flnxi \
  --action restore \
  --backup-path /opt/flnxi/backups/flnxi-backup-YYYYMMDD-HHMMSS \
  --confirm-restore RESTORE_FLNXI_DATA

Windows / PowerShell:

.\scripts\backup-restore.ps1 `
  -ProjectDir "D:\deploy\flnxi" `
  -Action Restore `
  -BackupPath "D:\deploy\flnxi\backups\flnxi-backup-YYYYMMDD-HHMMSS" `
  -ConfirmRestore RESTORE_FLNXI_DATA

说明:

  • 默认恢复 postgres.sql、minio-data.tar.gz、data.zip 和 uploads.zip,然后启动服务并执行健康验收。
  • 如果只恢复数据库、不恢复对象存储,加 -SkipObjectStorageRestore。
  • 如果需要把备份包中的 .env 一起恢复,加 -RestoreEnv;脚本会先把当前 .env 另存为 .env.before-restore-时间戳。
  • 外部数据库和外部对象存储不受该脚本自动恢复,必须按客户现场备份方案操作。

7.4 手动备份数据库

docker exec flnxi-postgres pg_dump \
  -U postgres \
  -d flnxi \
  -F c \
  -f /tmp/flnxi-backup-$(date +%Y%m%d-%H%M%S).dump

再把备份文件复制到宿主机或外部备份位置。外部数据库不受 Compose 脚本管理,必须按客户现场数据库方案单独备份。

7.5 手动回滚应用

如使用镜像部署,回滚到上一镜像版本时,直接改完整 APP_IMAGE。不要只改 IMAGE_TAG,因为完整 APP_IMAGE 的优先级更高。

docker compose -f docker-compose.prod.yml down
cp .env .env.rollback-$(date +%Y%m%d-%H%M%S)
sed -i 's#^APP_IMAGE=.*#APP_IMAGE=registry.example.com/flnxi/material/flnxi-material:<previous-version>#' .env
docker compose -f docker-compose.prod.yml pull app
docker compose -f docker-compose.prod.yml up -d app
curl http://<SERVER_IP>:4320/api/health

回滚后 /api/health 仍必须返回 200 才算应用恢复可用。

回滚前先确认数据库是否需要同时回滚。只回滚应用镜像不等于回滚数据库结构;如果本次升级包含数据库迁移,必须先确认备份和恢复路径。

7.6 手动恢复数据库

恢复前必须确认已停止会写入数据库的应用容器,并确认备份文件正确。下面给两种常见方式,二选一即可。

docker cp ./flnxi-backup-YYYYMMDD-HHMMSS.dump flnxi-postgres:/tmp/flnxi-restore.dump
docker exec flnxi-postgres pg_restore \
  -U postgres \
  -d flnxi \
  -c \
  /tmp/flnxi-restore.dump

或者直接从宿主机标准输入恢复:

docker exec -i flnxi-postgres pg_restore \
  -U postgres \
  -d flnxi \
  -c \
  < ./flnxi-backup-YYYYMMDD-HHMMSS.dump

8. 常见问题

应用访问不到

优先检查:

  • docker compose -f docker-compose.prod.yml ps
  • APP_PORT 是否和访问端口一致
  • SERVER_IP 和 NEXTAUTH_URL 是否填写成浏览器可访问地址
  • 防火墙是否放通端口

旧恢复接口被拒绝

/api/restore-data 是历史一次性数据恢复入口,不属于客户日常后台功能。客户环境默认应保持:

RESTORE_DATA_ENABLED=0

只有在受控恢复窗口才临时启用,并且必须同时满足:

  • RESTORE_DATA_TOKEN 至少 32 字符,不能使用示例值或短口令。
  • RESTORE_DATA_ALLOWED_IPS 只写维护机来源 IP。
  • 通过可信反向代理传递来源 IP 时,才设置 RESTORE_DATA_TRUST_PROXY=1;否则接口默认不信任 x-forwarded-for。
  • 调用时必须同时带 x-restore-data-token 和 x-restore-data-confirm: RESTORE_DATA。

恢复完成后必须立刻把 RESTORE_DATA_ENABLED 改回 0,并重启应用容器。

MinIO 预览或下载异常

优先检查:

  • S3_BUCKET
  • MINIO_ROOT_USER
  • MINIO_ROOT_PASSWORD
  • S3_PUBLIC_DOMAIN
  • MinIO 控制台是否能看到桶和对象
  • S3_SET_ACL 是否仍为默认 0。私有化交付默认不公开素材桶;只有兼容旧公开对象模式时才允许显式改成 1。
  • 当前生产 Compose 的 minio-init 会默认执行 mc anonymous set none,用于关闭 MinIO 桶匿名下载。如果客户历史环境曾经公开过桶,升级后应重跑 minio-init 或手动执行同等命令确认桶已恢复私有。
  • 外部 S3 或历史环境可能存在旧 bucket policy、旧对象级 ACL 或历史公开链接,这些不一定会被当前 minio-init 自动收回,升级前后要单独做只读审计。

许可证异常

优先检查:

  • LICENSE_SERVER_URL
  • LICENSE_PROJECT_CODE
  • LICENSE_REQUEST_SIGNING_SECRET 是否与许可证服务一致
  • 许可证服务是否存在对应项目编码
  • 主应用 /api/license/check、/api/license/verify 响应
  • 如果独立部署 license-server/docker-compose.yml,确认对外访问的是 Nginx 暴露的 9527,不是容器内部 3000。
  • 如果请求返回 429,优先检查 Nginx 限流是否被触发,再看是否存在脚本重试、暴力激活或心跳频率异常。

数据迁移失败

优先查看:

docker logs flnxi-material --tail 200
docker exec flnxi-material npx prisma validate
docker exec flnxi-material npm run verify:template-data

9. 相关文档

  • docs/初次部署与出厂设置指南.md
  • docs/deployment/多品牌 NAS 与 Linux 交付差异清单.md
  • docs/许可证服务接入说明.md
  • scripts/README.md