Files
senmesh-zc/deployment/iw/docker-compose.yml
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

193 lines
8.2 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# =============================================================================
# s-file 信创文件安全传输系统 —— 单台服务器一套(内网机 iw / 外网机 ow 通用同一份)
#
# 两台机器之间只靠光闸摆渡目录互通,彼此不开任何网络端口。
# 随角色变化的取值全在同目录 .env 里,本文件现场不用改。
#
# ./scripts/install.sh 自检 -> 校验并载入 images/*.tar -> 建目录 -> 起栈 -> 等健康
#
# 数据一律落在宿主机,不建 docker 卷(默认在本目录 volumes/ 下,见 .env 的 SFILE_DATA_ROOT):
# volumes/pgdata PostgreSQL 数据 volumes/redis Redis AOF
# volumes/mq RabbitMQ 数据 volumes/upload 用户上传的文件本体
# volumes/logs 后端日志(+logs/nginx) volumes/gate/out|in 光闸发出/收到的传输包
#
# 前端是已构建好的静态包,直接放在 web/ 目录挂进 nginx:
# 改页面 = 替换 web/ 下的文件,docker compose restart web 即可,不用重新构建镜像。
#
# 只发布前端端口;后端 / PostgreSQL / Redis / RabbitMQ 仅在容器网络内可达。
# =============================================================================
name: sfile-${SFILE_ROLE:?缺少 SFILE_ROLE(内网机 iw / 外网机 ow)}
services:
web:
image: ${NGINX_IMAGE:?缺少 NGINX_IMAGE}
container_name: sfile-${SFILE_ROLE}-web
restart: unless-stopped
ports:
- "${WEB_HOST:-0.0.0.0}:${WEB_PORT:-80}:80"
environment:
TZ: ${TZ:-Asia/Shanghai}
volumes:
- ./nginx/web.conf:/etc/nginx/conf.d/default.conf:ro
# 前端静态包:宿主机 web/ 目录即站点根,替换文件即换页面
- ./web:/usr/share/nginx/html:ro
- ${SFILE_DATA_ROOT:?缺少 SFILE_DATA_ROOT}/logs/nginx:/var/log/nginx
depends_on:
app:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "curl -sf -o /dev/null http://127.0.0.1:80/ || exit 1"]
interval: 30s
timeout: 5s
retries: 3
start_period: 20s
networks: [sfile]
app:
image: ${APP_IMAGE:?缺少 APP_IMAGE}
container_name: sfile-${SFILE_ROLE}-app
restart: unless-stopped
stop_grace_period: 30s
environment:
TZ: ${TZ:-Asia/Shanghai}
# 发号器 workerId 按「容器 IP」取键,取不到会回退 99 而 99 > 31 直接启动失败,
# 因此这里锁死 app 的容器 IP,并把同一个键写进 JVM 参数,两处永远一致
JAVA_OPTS: "${APP_JAVA_OPTS:--Xms1g -Xmx3g -Dfile.encoding=UTF-8} -Dcode.snowflake.workid.${APP_NODE_IP:?缺少 APP_NODE_IP}=${APP_WORK_ID:?缺少 APP_WORK_ID}"
SPRING_CONFIG_ADDITIONAL_LOCATION: file:/config/application.yml
SPRING_DATASOURCE_URL: jdbc:postgresql://pg:5432/${POSTGRES_DB:?缺少 POSTGRES_DB}
SPRING_DATASOURCE_USERNAME: ${POSTGRES_USER:?缺少 POSTGRES_USER}
SPRING_DATASOURCE_PASSWORD: ${POSTGRES_PASSWORD:?缺少 POSTGRES_PASSWORD}
SPRING_REDIS_HOST: redis
SPRING_REDIS_PORT: 6379
SPRING_RABBITMQ_HOST: mq
SPRING_RABBITMQ_PORT: 5672
SPRING_RABBITMQ_USERNAME: ${MQ_USER:?缺少 MQ_USER}
SPRING_RABBITMQ_PASSWORD: ${MQ_PASSWORD:?缺少 MQ_PASSWORD}
# 两侧交换机/广播 ID 必须不同,防止有人把两套栈并到一台机器时串消息
RABBITMQ_CUSTOM_TOPIC_EXCHANGE_NAME: s-file-${SFILE_ROLE}-topic
RABBITMQ_CUSTOM_DELAYED_EXCHANGE_NAME: s-file-${SFILE_ROLE}-delayed
RABBITMQ_CUSTOM_BROADCAST_ID: s-file-${SFILE_ROLE}-broadcast
SECURITY_JWT_SECRET: ${JWT_SECRET:?缺少 JWT_SECRET}
USER_DEFAULT_PASSWORD: ${USER_DEFAULT_PASSWORD:?缺少 USER_DEFAULT_PASSWORD}
# 内网机 1 / 外网机 2,写反了摆渡方向就反了
FILE_TRANSFER_LOCATION: "${SFILE_TRANSFER_LOCATION:?缺少 SFILE_TRANSFER_LOCATION}"
FILE_TRANSFER_SEND_DIR: /data/transfer/send
FILE_TRANSFER_RECEIVE_DIR: /data/transfer/receive
FILE_TRANSFER_MONITOR_CRON: "${FILE_TRANSFER_MONITOR_CRON:-0/5 * * * * ?}"
MESHSAFE_ENABLED: ${MESHSAFE_ENABLED:-true}
MESHSAFE_BASE_URL: ${MESHSAFE_BASE_URL:-}
MESHSAFE_CLIENT_ID: ${MESHSAFE_CLIENT_ID:-}
MESHSAFE_SECRET: ${MESHSAFE_SECRET:-}
MESHSAFE_CHANNEL: ${MESHSAFE_CHANNEL:-sfile-${SFILE_ROLE}}
MESHSAFE_FAIL_OPEN: ${MESHSAFE_FAIL_OPEN:-true}
MESHSAFE_TIMEOUT_SECONDS: ${MESHSAFE_TIMEOUT_SECONDS:-120}
MESHSAFE_POLL_TIMEOUT_MINUTES: ${MESHSAFE_POLL_TIMEOUT_MINUTES:-60}
MESHSAFE_POLL_CRON: ${MESHSAFE_POLL_CRON:-0/10 * * * * ?}
volumes:
- ./config/application.yml:/config/application.yml:ro
- ${SFILE_DATA_ROOT}/upload:/data/upload
- ${SFILE_DATA_ROOT}/logs:/app/logs
# 摆渡目录:容器内固定路径,宿主机侧由光闸设备负责单向搬运
- ${GATE_SEND_DIR:?缺少 GATE_SEND_DIR}:/data/transfer/send
- ${GATE_RECV_DIR:?缺少 GATE_RECV_DIR}:/data/transfer/receive
# MeshSafe 与应用同机部署时靠它从容器内访问宿主机端口;
# Docker < 20.10 不支持 host-gateway,或 MeshSafe 在独立服务器时,删掉这两行即可
extra_hosts:
- "host.docker.internal:host-gateway"
depends_on:
pg:
condition: service_healthy
redis:
condition: service_healthy
mq:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "curl -s --max-time 5 -o /dev/null http://127.0.0.1:8080/ || exit 1"]
interval: 30s
timeout: 8s
retries: 5
# 首次建库要跑全量 DDL,冷启动给足时间
start_period: 180s
networks:
sfile:
ipv4_address: ${APP_NODE_IP}
pg:
image: ${PG_IMAGE:?缺少 PG_IMAGE}
container_name: sfile-${SFILE_ROLE}-pg
restart: unless-stopped
stop_grace_period: 60s
environment:
TZ: ${TZ:-Asia/Shanghai}
POSTGRES_DB: ${POSTGRES_DB:?缺少 POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER:?缺少 POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?缺少 POSTGRES_PASSWORD}
# 空库首次启动才执行 initdb.d,已有数据卷时会被忽略
POSTGRES_INITDB_ARGS: "--encoding=UTF8 --lc-collate=C --lc-ctype=C"
volumes:
# postgres 18+ 数据目录挂载点是 /var/lib/postgresql,15- 是 .../data,见 .env 的 PG_DATA_DIR
- ${SFILE_DATA_ROOT}/pgdata:${PG_DATA_DIR:-/var/lib/postgresql}
- ./sql/01-init-table.sql:/docker-entrypoint-initdb.d/01-init-table.sql:ro
- ./sql/02-init-data.sql:/docker-entrypoint-initdb.d/02-init-data.sql:ro
healthcheck:
# 必须强制走 TCP(-h 127.0.0.1):首次 initdb 期间镜像会临时起一个只听 unix socket
# 的实例,此时用默认 socket 探测也会返回成功,导致 app 抢跑连库失败
test: ["CMD-SHELL", "pg_isready -h 127.0.0.1 -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 10
start_period: 60s
networks: [sfile]
redis:
image: ${REDIS_IMAGE:?缺少 REDIS_IMAGE}
container_name: sfile-${SFILE_ROLE}-redis
restart: unless-stopped
command: ["redis-server", "--appendonly", "yes", "--maxmemory", "${REDIS_MAXMEMORY:-512mb}", "--maxmemory-policy", "allkeys-lru"]
environment:
TZ: ${TZ:-Asia/Shanghai}
volumes:
- ${SFILE_DATA_ROOT}/redis:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 5
networks: [sfile]
mq:
image: ${MQ_IMAGE:?缺少 MQ_IMAGE}
container_name: sfile-${SFILE_ROLE}-mq
restart: unless-stopped
stop_grace_period: 30s
environment:
TZ: ${TZ:-Asia/Shanghai}
RABBITMQ_DEFAULT_USER: ${MQ_USER:?缺少 MQ_USER}
RABBITMQ_DEFAULT_PASS: ${MQ_PASSWORD:?缺少 MQ_PASSWORD}
volumes:
- ${SFILE_DATA_ROOT}/mq:/var/lib/rabbitmq
healthcheck:
test: ["CMD-SHELL", "rabbitmq-diagnostics -q ping"]
interval: 20s
timeout: 10s
retries: 10
start_period: 60s
networks: [sfile]
networks:
sfile:
driver: bridge
ipam:
config:
# 锁 app 容器 IP 是为了发号器 workerId 取键;subnet/网关/app IP 三个值必须同网段。
# 若与客户机器上已有 docker 网段冲突,整段换掉(如 172.31.88.0/24)即可
- subnet: ${APP_SUBNET:?缺少 APP_SUBNET}
gateway: ${APP_GATEWAY:?缺少 APP_GATEWAY}