技术

一直拿外部 S3 做测试太不合理了,所以我自己搭了基于 SeaweedFS 的存储

AAnonymous
13分钟阅读

开始之前

在本地反复测试上传、presigned URL、公开 bucket、CDN 缓存和缩略图时,如果还一直为了测试去接外部 S3,整个流程会比想象中更不自然。问题不只是成本,更在于每次做实验都得绕经外部账号和资源,这一点一直让我不太舒服。

所以这次我换了个方向。测试中会反复使用的存储,干脆直接搬到本地部署环境里,而应用看到的接口则尽量保持 S3 그대로。因为如果在 local、dev 环境接入本地部署的 S3 兼容存储,而在生产环境里又能用同一套接口直接切换到 AWS S3 或 Cloudflare R2,这会是更合理的结构。

最终,我用 SeaweedFS + Nginx CDN + TLS + TTL 这套组合搭了一个按单节点规模运行的 S3 兼容存储。而且这次工作本身也是一个 AI 全自动化案例:从调研初稿、基础设施配置草案、manifest 编写,到部署流程整理,基本都由 AI 完成。本文会对真实密钥、Secret、内部路径、内部 IP 等敏感信息全部做脱敏处理。

为什么要自己搭

AWS S3 本身并没有问题。只是放到测试场景里,它越来越显得别扭。

  • 我需要在 local 和开发环境里反复验证文件上传流程。
  • 我需要区分公开 bucket 和私有 bucket,同时一起确认 CDN、presigned URL、缩略图这一层是否正常。
  • 我有很多临时文件会以很短的周期被创建又删除。
  • 每次都以外部存储为前提来做这些实验,对一个测试流程来说还是太重了。

我想要的不是一个庞大的存储平台,而是 테스트와 검증에 충분한 S3 호환성간단한 온프레미스 운영성。更准确地说,我需要把开发者面对的存储契约固定成 S3 호환 인터페이스,然后按环境只替换后端实现。

为什么是 SeaweedFS

一开始我先看了最熟悉的选项,但以 2025 年末为准,已经很难把 MinIO 当作默认选择。许可证变更和社区运营方向的变化,让我觉得它并不适合长期、轻量地带着走。

相反,Ceph RGW 是一个完整得多的方案,但对这次目标来说又太重了。我需要的是按单节点规模运行的测试用存储和简单的媒体承载能力,而不是去运营一个大型存储集群。

所以最终的选择是 SeaweedFS

  • 它采用 Apache 2.0 许可证,商业约束很轻。
  • 它提供 S3 Gateway,不需要大幅改动现有 SDK 流程。
  • 在上传、下载、按 bucket 隔离、presigned URL 这些主要使用范围内,S3 接口基本可以原样保留。
  • 基于 Helm 很容易直接部署到 Kubernetes。
  • 它也很适合处理图片、音频、短视频这类小型媒体文件。

与其说我找到了一个完整的 S3 替代品,不如说是在这次目标需要的范围内,选了一个最简洁的 S3와 거의 같은 계약으로 움직이는 저장소

实际是这样搭的

整体没有做得很复杂。以 SeaweedFS 为核心搭出 S3 兼容层,前面再挂一层 Nginx 缓存层。然后在上面补上 TLS、公开 bucket 专用缩略图层,以及临时文件 TTL 策略。

运行单元大致分成这样。

  • S3 endpoint:应用直接接入的基础存储
  • CDN endpoint:公开资源的缓存分发
  • thumbnail endpoint:仅面向公开 bucket 的图片转换层
  • bucket 划分:staticpublic 为公开,imagesvideosbgmfiles 为私有
  • TTL:_tmp/ 前缀在 2 周后自动过期

这样一来,测试里需要的关键流程就能一次性全部验证。上传、公开访问、私有 presigned URL、CDN 缓存、缩略图生成、临时文件清理,都能放在同一套存储之上完成。

Spring Boot 这一侧也没有大改原有的 S3 接入方式。在 local 和测试 profile 中,只是把 endpoint 换成本地部署的 S3 兼容地址,并统一了 path-style 访问与公开/私有 bucket 规则。也就是说,即便重新搭了一套存储,应用代码的大框架依然可以保持不变。

这一点尤其好。在 local、dev 环境里接 SeaweedFS 这类本地部署存储,而在线上环境里改成 AWS S3 或 R2 时,应用连接的接口几乎可以保持原样。也就是说,更换存储产品这件事,不会立刻演变成把应用里的存储抽象整个推翻重来。

换句话说,这次搭建的核心价值,并不只是把一套存储拉起来,而是 환경마다 저장소 구현체는 달라도, 코드가 바라보는 인터페이스는 S3 호환으로 고정했다。这样的接口对测试很友好,将来要更换线上存储时也会灵活得多。

这次 AI 做到了什么程度

这次工作里更有意思的,其实不是 SeaweedFS 本身,而是 AI 究竟能把这个过程推进到多深。

实际下面这些工作都由 AI 全自动完成,人只负责填写敏感信息和做最终复核。

  • S3 兼容存储候选方案的调研与对比初稿
  • MinIOSeaweedFSCephRustFS 做对比后收敛选型
  • Helm values、Nginx 缓存 manifest、TLS 配置草案编写
  • bucket 结构、TTL 策略、测试流程、运行检查清单整理
  • 部署指南与故障排查文档整理

这次经历再次让我确认,只要先把需求和安全边界划清,基础设施工作同样可以被 AI 自动化到很深。尤其像这次这种会反复出现的流程:초안 작성 -> 설정 파일 생성 -> 배포 절차 문서화 -> 실제 반영,体感上的效率提升非常明显。

收尾

这次搭建并不是什么宏大的存储平台引入记,更像是一份把因为测试而反复使用的 S3 拉回到自己掌控范围内的记录。

但即便只是做到这个程度,也已经很有意义。因为公开/私有资源、CDN、presigned URL、缩略图、TTL 这些真实运行流程,现在都可以在没有外部依赖的情况下更高频地验证。更重要的是,我拿到了一种结构:local 和 dev 用本地部署,线上则用 S3 或 R2,但都放在同一套 S3 兼容接口之下。

还有一件事也变得更明确了。AI 不只是能写代码,它对这类基础设施初稿、配置和部署文档,也已经能做到相当高水平的全自动化。人的工作当然还在,但至少现在,哪些部分可以放心交给它,已经比以前清楚得多。


附录

下面是 documents/workspace/infrastructure/S3_COMPATIBLE_STORAGE_RESEARCH.md 全文。

S3 兼容对象存储解决方案调研

작성日期:2025-12-30 目的:为替代 AWS S3 而选择自建基础设施方案

背景

MinIO 现状(2025 年 12 月)

MinIO 已不再推荐。主要原因如下:

  1. 许可证问题:Apache 2.0 → AGPL-3.0

    • 以网络服务形式提供时,有公开源代码的义务
    • 商业使用时年许可证从 $96,000 起
  2. 转入维护模式(2025 年 12 月)

    • 停止新增功能、改进项和 PR 接收
    • 仅在逐案评估后提供安全补丁
    • 停止分发社区版二进制文件(仅提供源代码)
  3. 移除管理 UI

    • 社区版删除管理控制台功能
    • 仅付费版本提供完整功能

参考:InfoQ - MinIO in Maintenance Mode


需求

项目需求
S3 兼容性必需 - 必须能够直接使用 AWS SDK
许可证优先选择没有商业使用限制的许可证(Apache 2.0、MIT 等)
存储对象图片、BGM、短视频
CDN 扩展能通过 Nginx 反向代理构建缓存 CDN
部署环境Kubernetes + Helm chart
成本用自建基础设施降低 AWS 成本

方案对比

1. SeaweedFS ⭐(推荐)

项目内容
许可证Apache 2.0(可自由商业使用)
语言Go
架构Master + Volume + Filer 结构
特点O(1) 磁盘 seek,基于 Facebook Haystack 架构
S3 兼容提供 S3 Gateway(完整支持基础 S3 操作)
Helm提供官方 Helm chart + Kubernetes Operator
成熟度已在生产验证(有 1.5PB+ 部署案例)
企业版25TB 以内免费,之后 $1/TB/月

优点

  • 可处理数十亿个文件(非常适合媒体存储)
  • 通过 O(1) seek 实现对小文件的快速访问
  • 支持 Erasure Coding,提高存储效率
  • 支持云分层(自动迁移 Cold 数据)
  • 支持 FUSE 挂载、WebDAV、Hadoop 集成

缺点

  • 部分高级 S3 功能尚不支持(如 lifecycle 策略)
  • 必须做好元数据备份(Filer 元数据丢失时文件会变成孤儿)

Helm 安装

Bash
helm repo add seaweedfs https://seaweedfs.github.io/seaweedfs/helm
helm upgrade --install seaweedfs seaweedfs/seaweedfs -n storage --create-namespace

参考:SeaweedFS GitHub


2. RustFS

项目内容
许可证Apache 2.0
语言Rust
性能相比 MinIO,4KB 对象快 2.3 倍
S3 兼容完整支持 S3 API
Helm提供官方 Helm chart
成熟度⚠️ 测试版(截至 2025 年 12 月为 0.0.77)

优点

  • 支持 MinIO 迁移与共存
  • 小对象性能优秀
  • Apache 2.0 许可证

缺点

  • 仍处于 beta 阶段(生产使用需谨慎)
  • 文档和社区相对不足

Helm 安装

Bash
helm repo add rustfs https://charts.rustfs.com
helm install rustfs rustfs/rustfs -n rustfs --create-namespace \
  --set ingress.className="nginx"

参考:RustFS GitHub


3. Ceph RGW (via Rook)

项目内容
许可证LGPL 2.1
语言C++(数据路径)、Go(Rook Operator)
架构基于 RADOS 的统一存储(Block + File + Object)
S3 兼容顶级(通过 576 项 s3-tests)
Helm提供 Rook Operator
成熟度企业级(多年验证)

优点

  • S3 兼容性第一(支持最多的 S3 API)
  • 统一提供块、文件、对象存储
  • 支持多租户与命名空间隔离
  • 可做高级 Erasure Coding 配置
  • 可扩展到 EB 级规模

缺点

  • 安装和运维复杂
  • 资源要求高(至少 3 节点,并需要高速网络)
  • 学习曲线陡峭

Helm 安装(Rook)

Bash
helm repo add rook-release https://charts.rook.io/release
helm install rook-ceph rook-release/rook-ceph -n rook-ceph --create-namespace
# CephCluster CRD 적용 필요

参考:Rook Documentation


4. Garage

项目内容
许可证⚠️ AGPL-3.0(与 MinIO 存在同样问题)
语言Rust
特点轻量,适合地理分布式部署
S3 兼容支持核心 S3 操作(高级功能受限)
Helm社区 chart
成熟度适合小规模自托管

优点

  • 轻量且资源效率高
  • 默认支持多区域/多站点复制
  • Rust 带来的内存安全性

缺点

  • AGPL-3.0 许可证(商业使用受限)
  • 不支持 Erasure Coding(仅 3x 复制)
  • 高级 S3 功能不足

参考:Garage


S3 兼容性对比

方案s3-tests 通过数评价
Ceph RGW576最高
Zenko CloudServer382优秀
MinIO321良好
SeaweedFS56基础

SeaweedFS 对核心 S3 操作(PUT, GET, DELETE, LIST 等)支持完整, 但高级功能(Object Lock, Lifecycle 等)较为有限


最终推荐

🥇 SeaweedFS(强烈推荐)

推荐理由:

  1. Apache 2.0 许可证 - 商业使用完全自由
  2. 针对媒体存储优化 - 非常适合图片、音频、视频存储
  3. 已在生产验证 - 有多年大规模部署案例
  4. 对 Kubernetes 友好 - 提供官方 Helm chart 与 Operator
  5. 价格合理 - 25TB 以内免费,之后每月 $1/TB

🥈 备选方案

场景推荐
需要完全 S3 兼容Ceph RGW(接受复杂度)
偏好新技术且愿意实验RustFS(接受 beta 风险)
小规模且不介意 AGPLGarage

Nginx CDN 配置指南

下面是使用 SeaweedFS + Nginx 组合构建 CDN 的示例:

Nginx 缓存配置

Nginx
# /etc/nginx/nginx.conf

http {
    # 캐시 저장소 설정
    proxy_cache_path /var/cache/nginx/s3
        levels=1:2
        keys_zone=s3_cache:100m
        max_size=50g
        inactive=7d
        use_temp_path=off;

    upstream seaweedfs_s3 {
        server seaweedfs-s3.storage.svc.cluster.local:8333;
        keepalive 64;
    }

    server {
        listen 80;
        server_name cdn.example.com;

        # 이미지 캐싱 (7일)
        location ~* \.(jpg|jpeg|png|gif|webp|ico|svg)$ {
            proxy_pass http://seaweedfs_s3;
            proxy_cache s3_cache;
            proxy_cache_valid 200 7d;
            proxy_cache_valid 404 1m;
            proxy_cache_use_stale error timeout updating;
            proxy_cache_lock on;

            add_header X-Cache-Status $upstream_cache_status;
            add_header Cache-Control "public, max-age=604800";

            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }

        # 오디오/비디오 캐싱 (30일)
        location ~* \.(mp3|wav|ogg|mp4|webm|m4a)$ {
            proxy_pass http://seaweedfs_s3;
            proxy_cache s3_cache;
            proxy_cache_valid 200 30d;
            proxy_cache_valid 404 1m;
            proxy_cache_use_stale error timeout updating;
            proxy_cache_lock on;

            add_header X-Cache-Status $upstream_cache_status;
            add_header Cache-Control "public, max-age=2592000";

            # Range 요청 지원 (비디오 스트리밍)
            proxy_set_header Range $http_range;
            proxy_set_header If-Range $http_if_range;

            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }

        # 기타 파일
        location / {
            proxy_pass http://seaweedfs_s3;
            proxy_cache s3_cache;
            proxy_cache_valid 200 1d;

            add_header X-Cache-Status $upstream_cache_status;

            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
}

缓存状态头

  • HIT:由缓存直接提供
  • MISS:从源站拉取
  • STALE:返回已过期缓存(源站出错时)
  • UPDATING:正在后台刷新

参考:NGINX Caching Guide


Kubernetes 部署架构

┌─────────────────────────────────────────────────────────────────┐
│                        Kubernetes Cluster                        │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  ┌──────────────────────────────────────────────────────────┐   │
│  │                    Ingress (Nginx)                        │   │
│  │                   cdn.example.com                         │   │
│  └──────────────────────┬───────────────────────────────────┘   │
│                         │                                        │
│  ┌──────────────────────▼───────────────────────────────────┐   │
│  │              Nginx Cache Layer (CDN)                      │   │
│  │          /var/cache/nginx (PVC: 50Gi+)                   │   │
│  └──────────────────────┬───────────────────────────────────┘   │
│                         │                                        │
│  ┌──────────────────────▼───────────────────────────────────┐   │
│  │                   SeaweedFS                               │   │
│  │  ┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐      │   │
│  │  │ Master  │  │ Volume  │  │ Volume  │  │ Volume  │      │   │
│  │  │  (x3)   │  │   #1    │  │   #2    │  │   #3    │      │   │
│  │  └────┬────┘  └────┬────┘  └────┬────┘  └────┬────┘      │   │
│  │       │            │            │            │            │   │
│  │  ┌────▼────────────▼────────────▼────────────▼────┐      │   │
│  │  │              Filer (S3 Gateway)                 │      │   │
│  │  │           seaweedfs-s3:8333                     │      │   │
│  │  └─────────────────────────────────────────────────┘      │   │
│  └──────────────────────────────────────────────────────────┘   │
│                                                                  │
│  ┌──────────────────────────────────────────────────────────┐   │
│  │                  Persistent Volumes                       │   │
│  │     Volume #1      Volume #2      Volume #3               │   │
│  │     (100Gi+)       (100Gi+)       (100Gi+)                │   │
│  └──────────────────────────────────────────────────────────┘   │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

下一步

  1. 使用 SeaweedFS Helm chart 搭建测试环境
  2. 进行 S3 SDK 联调测试(确认与现有代码兼容)
  3. 配置 Nginx 缓存层
  4. 做性能基准测试(图片、音频、视频)
  5. 制定生产部署计划

参考资料