Files
senmesh-zc/deployment/部署说明.md
KodouWZF d39b3b035d feat(部署): 新增 deployment/ 内外网两套按侧部署包
按 demo/ 的写法把交付包改成「角色由目录决定」:iw/ 拷内网机、ow/ 拷外网机,
各自含镜像 tar、SQL、配置与 install.sh,install.sh 不再收 iw/ow 参数。

前端不再打进镜像,改为静态包放在 <侧>/web/ 只读挂进 nginx:1.27-alpine,
现场临时改页面直接替换文件 + restart web 即可。数据仍全部挂宿主机,
子目录命名与 deploy/offline 一致(pgdata/redis/mq/upload/logs/gate),
默认根改为包内 ./volumes,放数据盘只改 .env 的 SFILE_DATA_ROOT。

后端镜像 sfile-app:1.0.1 含传输优化那一批(实测组包 11.4×、解包 9.9×)。
出包脚本 deployment/scripts/build-package.sh 负责 jar→镜像→前端双包→sql→tar→VERSION。
2026-11-04 21:44:56 +08:00

19 KiB
Raw Blame History

s-file 部署说明 —— deployment/ 内网机包 + 外网机包

本目录是按侧分开的两套部署包:iw/ 拷内网机,ow/ 拷外网机,各自自包含(含镜像 tar、前端静态包、SQL、配置、安装脚本)。 与仓库里另外两套部署的关系见 §1,别混用。


0. 三套部署目录的关系

目录 用途 一台机器几个栈 前端形态 数据落哪
deploy/docker-compose.yml 我方本机联调 一台机器同起内+外两套 打进 sfile-web 镜像 宿主 D:/zcodedata/sfile
deploy/offline/ 单一交付包 + 参数选角色 一台一个栈,两台共用同一份盘 打进 sfile-web 镜像(内含内/外两套站点) 宿主 /data/sfile
deployment/iw、deployment/ow 按侧分目录的正式包 一台一个栈,两份包各拷各的 静态包直接放 <侧>/web/,改页面替换文件即可 <侧>/volumes/(可改绝对路径)

deployment/ 相比 deploy/offline/ 的实质差别只有两点:前端不再打进镜像(便于现场临时改页面)、角色由目录决定而不是命令行参数(防止在内网机上跑 install.sh ow)。镜像、SQL、配置项名称与 deploy/offline/ 保持一致,运维口径不用重学。


1. 拓扑

        内网(客户办公网)                          外网(外部报送网)
   ┌─────────────────────────┐              ┌─────────────────────────┐
   │  内网服务器  跑 iw/       │              │  外网服务器  跑 ow/       │
   │  ┌───────────────────┐  │  光闸/单向   │  ┌───────────────────┐   │
   │  │ sfile-iw-web  :80  │◄─┼── 摆渡设备 ──►┼─ │ sfile-ow-web  :80 │   │
   │  │ sfile-iw-app :8080 │  │ (不开网络端口) │  │ sfile-ow-app      │   │
   │  │ pg/redis/mq(内部)  │  │              │  │ pg/redis/mq(内部) │   │
   │  └───────────────────┘  │              │  └───────────────────┘   │
   │  volumes/gate/out ───发──┼──────────────┼──▶ volumes/gate/in      │
   │  volumes/gate/in ◀──收──┼──────────────┼── volumes/gate/out ───发──┼─▶
   └───────────┬─────────────┘              └─────────────────────────┘
               │ http://host.docker.internal:<MeshSafe端口>
        ┌──────┴───────────┐
        │ MeshSafe 文件安全检测 │  同机容器 / 独立服务器 / 集群均可,只要求该侧能 HTTP 访问到
        └──────────────────┘
  • 两台服务器之间不开任何 TCP 端口,文件只经光闸在「本侧 gate/out → 对端 gate/in」单向搬运。
  • 每台机器一个独立 compose 栈(项目名 sfile-iw / sfile-ow)。
  • 应用只认容器内固定路径 /data/transfer/send、/data/transfer/receive,宿主机侧路径由 .env 的 GATE_SEND_DIR/GATE_RECV_DIR 决定。

2. 包内容

deployment/
├── 部署说明.md                  本文件
├── scripts/build-package.sh     我方出包脚本(现场用不到)
├── iw/                          ← 拷内网机
│   ├── docker-compose.yml       5 个服务(web/app/pg/redis/mq),现场不改
│   ├── .env                     本机全部可变项(角色/端口/口令/目录/MeshSafe)← 只改这个
│   ├── config/application.yml   后端静态默认配置,不放任何口令
│   ├── nginx/web.conf           前端站点 + /api 反代(站点根 = ./web)
│   ├── web/                     前端静态包(内网包),只读挂进 nginx
│   ├── sql/01-init-table.sql    全量建表(92K,含 x_file_security 与 md5 索引)
│   ├── sql/02-init-data.sql     初始数据(admin、机构、平台编码)
│   ├── sql/upgrade/             已上线老库的增量脚本,新装不执行
│   ├── images/*.tar             5 个镜像 + SHA256SUMS.txt(不进气仓,走交付盘)
│   ├── scripts/install.sh       一键安装(无参数,角色看 .env)
│   ├── README.md                现场速查(装机/改页面/运维)
│   ├── VERSION.txt              本次出包的版本/平台/时间/镜像清单
│   └── volumes/                 运行时数据,install.sh 自动建(不进气仓)
└── ow/                          ← 拷外网机,结构与 iw 完全一致

iw/ 与 ow/ 只有三处不同:.env(角色/方向/网段/机器号/口令/MeshSafe 通道)、web/(前端内网包 vs 外网包)、README.md 里的角色字样。其余文件出包脚本保证逐字节一致。

2.1 镜像清单

镜像 tag tar 说明
sfile-app 1.0.1 ~313M JDK8 运行时 eclipse-temurin:8-jre-jammy,内含 s-file.jar
nginx 1.27-alpine ~21M 只做静态站点 + 反代,前端在宿主机 web/ 里
postgres 18 161M 验证过 18.3
redis 8 53M 验证过 8.6.3
rabbitmq 3.13-management-alpine 84M 验证过 3.13.7

sfile-app:1.0.1(对应 git tag v1.0.1)相比 1.0.0 是传输链路性能优化那一批(监听周期 60s→5s、读写缓冲 64KB、已压缩格式不再二次压缩、md5_code 加索引、单轮时间预算)。对照实测:端到端 123.1s → 9.9s,组包 11.4×、解包 9.9×,包体 +3.63%;详见 backend/docs/传输链路性能优化方案.md §10。 版本对应关系:git tag v1.0.1 = 镜像 sfile-app:1.0.1 = .env 的 APP_IMAGE,出包脚本按版本号一次对齐。

2.2 数据挂载对照(命名/位置与之前一致)

数据 容器内路径 deployment/(本包) deploy/offline/
PostgreSQL /var/lib/postgresql volumes/pgdata /data/sfile/pgdata
Redis /data volumes/redis /data/sfile/redis
RabbitMQ /var/lib/rabbitmq volumes/mq /data/sfile/mq
上传文件本体 /data/upload volumes/upload /data/sfile/upload
后端日志 /app/logs volumes/logs /data/sfile/logs
摆渡发出/收到 /data/transfer/send、/receive volumes/gate/out、in /data/sfile/gate/out、in

子目录名与 compose 里的写法完全沿用,只是根目录默认从 /data/sfile 改成包内 ./volumes(整目录自包含、拷走就能跑)。放独立数据盘就把 .env 的 SFILE_DATA_ROOT、GATE_SEND_DIR、GATE_RECV_DIR 改成绝对路径,重跑 install.sh 即可,compose 不用动。


3. 现场要先定的 6 件事(都在 .env)

项 变量 建议
服务器 CPU 架构 — uname -m:x86_64→amd64,aarch64(鲲鹏/飞腾)→arm64。出包前必须确认,架构不符装不上
前端对外端口 WEB_PORT 出厂 80;被占用改 8080/9080,同步改客户访问地址
数据盘根目录 SFILE_DATA_ROOT 正式环境改绝对路径指到独立数据盘。上传文件+库+日志都在这,按 500MB/文件上限估:平均文件大小 × 日均份数 × 保留天数,一般 ≥200G
光闸摆渡目录 GATE_SEND_DIR / GATE_RECV_DIR 与光闸厂商确认挂载点,发出与收到是两个不同目录,两侧机器各自配自己的
MeshSafe MESHSAFE_ENABLED / _BASE_URL / _CLIENT_ID / _SECRET 没接检测 → MESHSAFE_ENABLED=false(前后端自动隐藏「安全检测」列,摆渡不受影响);接了 → secret 必须与 MeshSafe 侧 MESHSAFE_API_CLIENTS 白名单里的 client_id:secret 逐字一致
各类口令 POSTGRES_PASSWORD 等 出厂已给随机值可直接跑,正式交付按客户安全要求轮换(§4.1)

机器规格建议:8 核 / 16G 起(应用堆 -Xmx 3G,PG + MQ + Redis 还要约 2-3G,留足页缓存),系统盘 100G + 数据盘独立。


4. 出包与装机

4.1 出包(我方构建机,需要联网一次)

# 前置:仓库源码就位,docker 可用
./deployment/scripts/build-package.sh 1.0.1                          # amd64
TARGET_PLATFORM=linux/arm64 ./deployment/scripts/build-package.sh 1.0.1   # 鲲鹏/飞腾

# 只想重出镜像 tar / 只刷前端:SKIP_JAR=1 / SKIP_WEB=1 / SKIP_SAVE=1

脚本会依次:建 jar → 建 sfile-app:<版本> → 前端内/外网包导出到 iw/web、ow/web → 从 backend/db 刷 sql/ → docker save 5 个镜像到两侧 images/ 并写 SHA256SUMS.txt → 写两侧 VERSION.txt 并对齐 .env 的 APP_IMAGE。 不动两侧 volumes/(现场运行时数据)与 .env 里的口令。

带盘就是带 iw/ 和 ow/ 两个目录(或各自压缩后分发),必须整目录拷——images/*.tar 不进气仓。

4.2 装机(每台服务器各执行一次)

# 内网机
cd /opt/sfile-iw
vi .env                    # 至少确认 MESHSAFE_ENABLED/SECRET
./scripts/install.sh

# 外网机
cd /opt/sfile-ow
vi .env
./scripts/install.sh

install.sh 做六件事:环境自检 → 校验交付物(5 个 tar + sha256 + 架构 + 前端包完整 + 端口占用探测)→ docker load → 建数据/摆渡目录(含 pgdata 属主 999、SELinux chcon)→ 起栈 → 逐个等健康,最后打印访问地址。 幂等:重复执行只重建有差异的容器,不动已初始化好的数据库。

首次登录后立刻改掉 admin 的初始口令。

4.3 出厂口令(交付单抄一份,建议现场轮换)

变量 内网机 iw 外网机 ow 现场是否必须改
POSTGRES_PASSWORD Sfile-iw-Pg-N1tOz1MxgdaS Sfile-ow-Pg-pT-673--i1rk 建议(只在本机容器网络内生效,不对网暴露)
MQ_PASSWORD Sfile-iw-Mq-UTqVSnCDZ0LB Sfile-ow-Mq-sfW5Cv-Nk-cA 建议(同上)
JWT_SECRET 86a6b015…7d70(64 位) 9eeeea03…6988(64 位) 必须保密;泄露等于能伪造登录态,两侧已不同
USER_DEFAULT_PASSWORD Sfile-iw-Usr-FBLS-0fA Sfile-ow-Usr-WqDJ7PDS 必须改:新建用户的初始密码,业务员要知道
admin 登录口令 由 sql/02-init-data.sql 灌入 同左 必须改:装完第一次登录就改
MESHSAFE_SECRET CHANGE_ME_meshsafe_secret 同左 故意留占位:以 MeshSafe 侧白名单为准;装了且 ENABLED=true 时 install.sh 会拦住

本包的 .env 是随包提交的出厂配置(一台机器一份),与 deploy/offline/ 用 .env.iw/.env.ow 再拷成 .env 的做法等价,只是省掉了拷贝步骤。现场改过的 .env 属于该台机器的实际配置,不要提交回仓库。

4.4 内网机 / 外网机配置差异

变量 iw ow
SFILE_ROLE iw ow
SFILE_TRANSFER_LOCATION 1(内网) 2(外网)
web/ 前端包 内网包(页面显示「内网」标识) 外网包
GATE_SEND_DIR / GATE_RECV_DIR 本侧 out=内→外发出,in=外→内投递 本侧 out=外→内发出,in=内→外投递
MESHSAFE_CLIENT_ID / MESHSAFE_CHANNEL sfile-iw sfile-ow
APP_SUBNET / APP_NODE_IP / APP_WORK_ID 172.30.18.0/24 / .10 / 1 172.30.28.0/24 / .10 / 2
JWT_SECRET / POSTGRES_PASSWORD / MQ_PASSWORD 一套 另一套(已确认不同)
WEB_PORT 80 80(两台机器各占自己的 80,不冲突)

两侧网段/机器号故意错开:客户先把两台合并成一台机器做演示时,两份包同起也不冲突。 SFILE_TRANSFER_LOCATION 写反是最容易踩的坑:值对调后两侧都以为自己是发送端,摆渡目录里的包永远不会被解包。


5. 手工验收(不依赖任何测试脚本)

# 动作 期望
1 docker compose ps 两台机各 5 个 healthy,无 Restarting/unhealthy
2 浏览器开 http://<内网机IP>:<端口> 左上角有「内网」标识,能登录;外网机同地址无该标识
3 内网上传一个文件 → 传输到外网 内网 volumes/gate/out 出现 <n>_<id>.zip;一分钟内「我的接收」出现 <原名>_<时间戳> 文件夹
4 接了 MeshSafe 时看「安全检测」列 几秒内从检测中变检测通过;未接检测则该列不显示
5 docker logs --tail 100 sfile-iw-app 无 ERROR,能看到监听任务每 5 秒一轮的日志

6. 运维

docker compose ps                          # 状态
docker logs -f sfile-iw-app                # 后端日志(含送检/摆渡全过程)
tail -f volumes/logs/*.log                 # 宿主机侧日志(已外置,不用进容器)
docker compose restart app                 # 改完 config/application.yml 后重启后端
docker compose up -d                       # 改完 .env(端口/口令/网段)后重建容器
docker compose restart web                 # 替换 web/ 里的前端文件后刷新站点
docker compose down                        # 停止(不动数据)

改前端页面(本包的主要便利点):web/ 就是站点根,只读挂载进 nginx。 换整包 rm -rf web && cp -r <新 dist> web && docker compose restart web; 临时改文案可以直接编辑 web/ 里的文件,浏览器强刷一次生效。

备份只需三样:volumes/pgdata(库)、volumes/upload(文件本体)、.env(口令)。 恢复:换机器后按原路径放回 + install.sh(它会把镜像重新 load 并起栈)。

老库升级:sql/01、sql/02 只在空库首次启动执行,已有数据卷会被忽略; 已上线环境要补的增量在 sql/upgrade/(都是「第 1 节 MySQL/达梦、第 2 节 PostgreSQL」双库混排,只能跑第 2 节):

awk '/^-- 2\. PostgreSQL/{f=1} f' sql/upgrade/upgrade-20261103-transfer-perf.sql \
  | docker exec -i sfile-iw-pg psql -U sfile -d s_file

7. 已知坑与取舍

  1. 交付盘必须整目录拷:install.sh 会核 5 个 tar 的个数、sha256、架构,以及 .env 里 5 个镜像 tag 是否都能 load 到,还会核 web/index.html 与 web/assets 非空。只拷脚本不拷 images/ 或 web/ 是最常见的现场翻车。
  2. 架构不符装不上:sfile-app 与前端包是在构建机上按平台产出的,信创 ARM 机必须用 TARGET_PLATFORM=linux/arm64 重新出包(install.sh 会拦你)。
  3. 前端改了不生效:web/ 是只读挂载,容器内看不到宿主机改动是正常的(宿主机改完 restart 就见效);另外 nginx 对 js/css/字体/图片 设了 expires 7d,直接替换同名文件时用户要强刷。构建产物文件名带 hash,正规换包不受这个影响。
  4. PostgreSQL 18 的数据目录挂载点是 /var/lib/postgresql(15 及以前是 /var/lib/postgresql/data)。自己换库版本时改 .env 的 PG_DATA_DIR,否则容器会报「检测到未挂载的数据」并反复重启。
  5. nginx 反代必须透传带端口的 Host(nginx/web.conf 里用的是 $http_host)。改成 $host 会让后端 RequestUtils.getRootDomain 对无端口地址抛 IllegalStateException,症状就是登录 500。
  6. 发号器 workerId 必须能取到,否则应用起不来。 后端按 code.snowflake.workid.<app容器IP> 取机器号,取不到回退默认 99,而 99 超出雪花 5 位上限,启动即 worker Id can't be greater than 31。本方案把 app 的容器 IP 锁死(APP_NODE_IP)并由 compose 自动写进 JVM 参数,.env 里 APP_SUBNET / APP_GATEWAY / APP_NODE_IP 三个值必须同网段。
  7. MeshSafe 凭证两边要逐字对上:调用方 .env 的 MESHSAFE_CLIENT_ID/MESHSAFE_SECRET 必须存在于 MeshSafe 侧 MESHSAFE_API_CLIENTS(格式 client_id:secret,client_id2:secret2),否则送检全部 401;改白名单要重启 MeshSafe 的 api 容器。MESHSAFE_CHANNEL 只是日志追溯标签,服务端不校验,不需要登记。MESHSAFE_FAIL_OPEN=true 时检测服务挂了只记失败不阻断,等保从严请置 false。
  8. host.docker.internal 需要 Docker ≥ 20.10;MeshSafe 在独立服务器时不需要它,可删掉 compose 里那两行 extra_hosts。
  9. PDF 盖章依赖 spire.pdf 是桩实现:本机因拿不到商业包做了替代 jar,运行时调用会抛 UnsupportedOperationException。交付前若客户要电子签章,必须换回 e-iceblue:spire.pdf:3.7.4 正式包重新打 jar。
  10. 端到端延迟里有一半是监听周期带来的空等。收发两侧各轮询一次,默认 5 秒一轮 → 平均空等 5 秒、最坏 10 秒(早期默认 1 分钟时是平均 60 秒、最坏 120 秒,这是「一页文件也要 2 分钟」的主因)。客户嫌慢就调 .env 的 FILE_TRANSFER_MONITOR_CRON;改成秒级是安全的:空目录一轮只有两次 Files.list + 过滤,且 Spring 的 cron 任务要等上一轮跑完才排下一轮,同实例内不会重叠。副作用是损坏的包会被更频繁重试(失败包标 .error 后停止重试,但仍在目录里)。改周期要同步看 config/application.yml 的 round-budget-seconds(留 1 秒余量)。
  11. 两侧数据库完全独立,同一份 02-init-data.sql 灌出来 admin 初始口令一致,交付时两侧都要改。
  12. 网段/端口与机器上已有容器冲突:install.sh 会探测 WEB_PORT 占用并提示;APP_SUBNET 冲突时报 invalid pool request: Pool overlaps with other one on this address space,整段换掉 .env 的三个网络值即可。
  13. 检测结论按文件内容复用:MeshSafe 的 fileCode 是内容寻址,同一份字节重复上传会直接沿用上次结论不重新扫。等保评审若问「改判是否会被复用绕过」,现场演示请用每次内容都不同的测试文件。
  14. 相对路径依赖包目录:SFILE_DATA_ROOT=./volumes 是相对 docker-compose.yml 解析的。若把包挪位置或从别处 docker compose -f 指过来,数据目录会跟着变——正式环境建议一律改成绝对路径。
  15. 随包文本一律 LF:.gitattributes 已把 *.sh / *.yml / *.sql / *.conf / .env 锁成 LF,build-package.sh 出包时再兜一遍。若有人用 Windows 记事本改过 install.sh 再拷去现场,先 tr -d '\r' < install.sh > i && mv i install.sh,否则 bash 会在第一行报 bad interpreter 或 set: pipefail\r: invalid option。