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
+54 -13
View File
@@ -31,20 +31,61 @@ services:
timeout: 5s
retries: 5
minio:
# 同 docker-compose.yml 的 minio 条目:Docker Hub 上 minio/minio 仓库已被整体移除
# (MinIO 自 2025-10 起停止发布容器镜像,仅源码分发),staging 环境同样拉不到。
# 改为仍在提供镜像的 quay.io,并钉死为与本地/CI 一致的 RELEASE.2025-09-07T16-13-09Z。
image: quay.io/minio/minio:RELEASE.2025-09-07T16-13-09Z
command: server /data --console-address ":9001"
seaweedfs:
# 同 docker-compose.yml:对象存储已从 MinIO 换成 SeaweedFS(钉 4.47)。
# staging 与本地/生产保持同一实现,避免「只有预发用的是另一套存储」这类偏差。
image: chrislusf/seaweedfs:4.47
container_name: danyang-match-staging-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: staging
MINIO_ROOT_PASSWORD: staging_dev_password
MINIO_ACCESS_KEY: staging
MINIO_SECRET_KEY: staging_dev_password
ports:
- "9010:9000"
- "9011:9001"
# 与生产错开一位(8080/8081、5432/5433 同理):本地栈的 8333 已被占,故用 8334。
- "8334:8333"
- "9334:9333"
- "8889:8888"
volumes:
- minio_staging_data:/data
- seaweedfs_staging_data:/data
healthcheck:
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
storage-init:
# 同 docker-compose.yml:SeaweedFS 不自动建桶,必须预建 public / private。
image: chrislusf/seaweedfs:4.47
container_name: danyang-match-staging-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
restart: "no"
backend:
build:
@@ -66,7 +107,7 @@ services:
REDIS_HOST: redis
REDIS_PORT: 6379
REDIS_PASSWORD: staging-redis-pass-change-me
MINIO_ENDPOINT: http://minio:9000
MINIO_ENDPOINT: http://seaweedfs:8333
MINIO_ACCESS_KEY: staging
MINIO_SECRET_KEY: staging_dev_password
JWT_ACCESS_SECRET: staging-access-secret-dev-only
@@ -103,4 +144,4 @@ services:
volumes:
postgres_staging_data:
redis_staging_data:
minio_staging_data:
seaweedfs_staging_data: