refactor(storage): 对象存储从 MinIO 换成 SeaweedFS(钉 4.47)

动机:MinIO 的 GitHub 仓库已于 2026-02 归档,不再产出安全补丁,已累积多个
CVE 且不修;本项目存身份证 / 实名材料 / 用户照片,无人修 CVE 才是真正驱动因素
(Docker Hub 下架只是表象,quay.io 仍在提供)。

变更:
- 三套 compose(dev / staging / prod)的 minio 服务替换为 seaweedfs 4.47:
  覆盖 entrypoint 渲染 -s3.config 注入 identity(S3 网关默认不鉴权,必须显式开启),
  -volume.max=100 解除第二个 bucket 写入时的 InternalError。
- 新增一次性 storage-init 容器,经 filer 接口预建 public / private 桶
  (SeaweedFS PutObject 到不存在的 bucket 直接失败;全仓库原无任何 createBucket)。
- S3Config 显式开启 path-style:AWS SDK v1 默认 virtual-host 会把 bucket 拼进
  主机名,容器名在 DNS 中不存在必然失败(本机有 http_proxy 时易误判为 502 拒绝)。
- ci.yml:镜像校验与起栈步骤改为 seaweedfs + storage-init。
- 运维脚本 health-check.sh / run-uat.sh 的 MinIO 探针改为 SeaweedFS
  (S3 网关开启鉴权后匿名根请求返回 403,200/403 均算存活)。

注:path-style 与建桶属换存储的必要项;presigned URL 的 host 须对外可达这一
既有缺陷(原 minio:9000 直发客户端不可达)留待下一轮「改缺陷」处理。
This commit is contained in:
WorkBuddy
2026-09-22 14:26:48 +08:00
parent aa1114338e
commit 797b1c46cf
8 changed files with 249 additions and 76 deletions
+62 -13
View File
@@ -39,23 +39,67 @@ services:
- danyang-net
restart: unless-stopped
minio:
# 同 docker-compose.yml 的 minio 条目:Docker Hub 上 minio/minio 仓库已被整体移除
# (MinIO 自 2025-10 起停止发布容器镜像,仅源码分发),生产环境同样拉不到。
# 改为仍在提供镜像的 quay.io —— 这是真实的部署阻塞,不只是 CI 问题。
# 版本与本地/CI 保持一致,钉死为 CI 实测确认可用的 RELEASE.2025-09-07T16-13-09Z。
image: quay.io/minio/minio:RELEASE.2025-09-07T16-13-09Z
container_name: danyang-match-minio
command: server /data --console-address ":9001"
seaweedfs:
# 同 docker-compose.yml:对象存储已从 MinIO 换成 SeaweedFS。
# 生产换的动力比 CI 更强 —— MinIO 仓库已归档、不再产出安全补丁,
# 而这里存的是身份证与实名材料。详见 docker-compose.yml 中的完整说明。
# 版本与本地/CI 一致(钉 4.47),避免各环境跑不同实现。
image: chrislusf/seaweedfs:4.47
container_name: danyang-match-seaweedfs
# 必须覆盖 entrypoint:镜像 ENTRYPOINT 是 /usr/bin/weed,只写 command 会被追加成
# `weed sh -c ...` → `weed: unknown subcommand "sh"`(本地实测踩到)。
entrypoint: ["sh", "-c"]
command:
- |
cat > /tmp/s3cfg.json <<CFG
{"identities":[{"name":"app","credentials":[{"accessKey":"$${MINIO_ACCESS_KEY}","secretKey":"$${MINIO_SECRET_KEY}"}],"actions":["Admin","Read","Write","List","Tagging"]}]}
CFG
# -volume.max=100:bucket 名即 collection,默认上限 8 个 volume 只够一个 bucket
# (第二个桶写入会 InternalError)。详见 docker-compose.yml 中的说明。
exec weed server -s3 -s3.port=8333 -s3.config=/tmp/s3cfg.json -dir=/data -volume.max=100
environment:
MINIO_ROOT_USER: ${MINIO_ACCESS_KEY:?请设置MINIO_ACCESS_KEY}
MINIO_ROOT_PASSWORD: ${MINIO_SECRET_KEY:?请设置MINIO_SECRET_KEY}
MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY:?请设置MINIO_ACCESS_KEY}
MINIO_SECRET_KEY: ${MINIO_SECRET_KEY:?请设置MINIO_SECRET_KEY}
volumes:
- minio_data:/data
- seaweedfs_data:/data
networks:
- danyang-net
healthcheck:
# 与本地一致:鉴权开启后匿名列桶返回 403,故 200/403 都算健康。
test: ["CMD-SHELL", "code=$$(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8333/); [ \"$$code\" = 200 ] || [ \"$$code\" = 403 ]"]
interval: 5s
timeout: 5s
retries: 24
restart: unless-stopped
storage-init:
# SeaweedFS 不会自动建桶,PutObject 到不存在的 bucket 直接失败。
# 生产同样必须预建 public / private,否则照片与实名材料全部上传不了。
# 走 filer 目录接口,无需签名,复用同一镜像的 curl。幂等:201 新建 / 409 已存在。
image: chrislusf/seaweedfs:4.47
container_name: danyang-match-storage-init
depends_on:
seaweedfs:
condition: service_healthy
entrypoint:
- sh
- -c
- |
for b in public private; do
code=$$(curl -s -o /dev/null -w '%{http_code}' "http://seaweedfs:8888/buckets/$$b/" || echo 000)
if [ "$$code" = "200" ]; then echo "bucket $$b: already exists"; continue; fi
i=0
while [ $$i -lt 30 ]; do
code=$$(curl -s -o /dev/null -w '%{http_code}' -X POST "http://seaweedfs:8888/buckets/$$b/" || echo 000)
case "$$code" in 200|201|409) break ;; esac
i=$$((i+1)); sleep 2
done
echo "bucket $$b: create -> $$code"
done
networks:
- danyang-net
restart: "no"
backend:
build:
context: ../backend
@@ -66,6 +110,10 @@ services:
condition: service_healthy
redis:
condition: service_healthy
seaweedfs:
condition: service_healthy
storage-init:
condition: service_completed_successfully
environment:
SPRING_PROFILES_ACTIVE: docker,production
SPRING_CONFIG_IMPORT: configtree:/run/secrets/
@@ -76,7 +124,8 @@ services:
REDIS_HOST: redis
REDIS_PORT: 6379
REDIS_PASSWORD: ${REDIS_PASSWORD:?请设置REDIS_PASSWORD}
MINIO_ENDPOINT: http://minio:9000
# 变量名仍为 MINIO_*(服务端配置键未改),实际指向 SeaweedFS 的 S3 网关。
MINIO_ENDPOINT: http://seaweedfs:8333
MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY:?请设置MINIO_ACCESS_KEY}
MINIO_SECRET_KEY: ${MINIO_SECRET_KEY:?请设置MINIO_SECRET_KEY}
JWT_ACCESS_SECRET: ${JWT_ACCESS_SECRET:?请设置JWT_ACCESS_SECRET}
@@ -138,7 +187,7 @@ services:
volumes:
postgres_data:
redis_data:
minio_data:
seaweedfs_data:
networks:
danyang-net: