大数跨境
0
0

突发|MinIO 社区版进入 maintenance-only:你的自建 S3 还能信任吗?

突发|MinIO 社区版进入 maintenance-only:你的自建 S3 还能信任吗? 晨章数据
2025-12-11
6
导读:工程师 48 小时应急计划


导语

MinIO 官方将 社区版改为“maintenance-only” 分发模式:社区不再提供预编译二进制、不再常规接受新特性和 PR,仅按需处理关键安全问题。换句话说:如果你把生产对象存储押在 MinIO 社区版上——你正在承受隐藏的生产风险与运维成本上升。


For SER


立刻做的三件事

3 Things to Do Immediately

1.把你的 MinIO 使用场景分为:关键生产 / 非关键环境 / 仅测试;

2.对关键负载立即执行 48 小时 PoC;

3. 选出 S3 兼容且“有人维护、可商用”的候选(例如 Ceph RGW、SeaweedFS、RustFS 等),并跑兼容性回归。


第1部分

为什么这不是“小事儿”——工程师的真实担忧


很多团队把对象存储当成“基础设施中的基础设施”——存很多冷数据、备份、日志、媒体文件、用户上传等。与数据库、消息队列不同,对象存储一旦出现“没人维护二进制/没人推补丁”的情况,带来的不是瞬时宕机,而是长期的补丁窗口延长、合规风险、以及运维成本陡升。尤其是对外暴露的 S3 接口、跨区域复制与授权管理,任何细微的不兼容都会成为事故触发点。



第2部分

可用替代都有哪些


我把候选项限定为:S3 协议兼容 + 在 2024–2025 年仍有活跃发布或商业支持 + 社区/厂商能提供补丁/支持。下面是工程角度的快速对比。


工程师速判:你需要 S3 的全功能(版本、生命周期、ACL、Multipart)吗? 如果“是” → Ceph(或商业 Scality)优先;如果“否、只需常规读写” → SeaweedFS / RustFS 可以快速上手并做 PoC。



第3部分

以 SeaweedFS 为例 48 小时 PoC

(实操脚本化可复制~)


1

整体目标

在 48 小时内得到能支持决策的量化结果,主要输出:

- 每个候选的兼容性问题清单(API / header / multipart / ACL 等)

- 基本吞吐与延迟数据(P50 / P95 / P99)在并发写入/并发读取场景下

- 运维复杂度量化(部署步骤数、恢复时间、脚本化比例)

- 故障恢复验证(单节点下线、恢复后的数据一致性)


2

时间分配(建议)

- 准备与 deploy:0–6 小时(并行部署三个候选的最简版)

- 数据迁移与并发基准:6–18 小时(并发写入/读取)

- 兼容性与功能回归:18–30 小时(multipart、ACL、head、lifecycle)

- 运维演练与故障注入:30–42 小时(关节点、重启、扩容)

- 汇总、报告、决策矩阵:42–48 小时


下面给出每个步骤的可执行脚本(把每段脚本放到对应机器或有 Docker 的 workstation 上运行)。


3

请确保你的测试环境满足

- CPU:每节点 4 vCPU(PoC 最低 2 vCPU,但 4 更稳定);

- 内存:每节点 8–16 GB(Seaweed/ RustFS 轻量,Ceph 建议 16GB 起);

- 磁盘(每节点):推荐 500 GB — 1 TB(PoC 用 500GB 足够,生产按容量/副本数/冗余设计);

- 网络:千兆网卡(1Gbps)或更好;低延迟私有网络;

- 操作系统:Ubuntu 20.04/22.04 或 CentOS 8/9;

- 6 个此类节点起。


通用工作目录与工具

(在每台节点上都执行)

#!/usr/bin/env bashsudo apt updatesudo apt-get install s3cmd
set -euo pipefail# 初始化工作目录WORKDIR="${PWD}/s3_distributed_poc"mkdir -p "$WORKDIR"{/seaweed,/scripts,/data}echo "工作目录: $WORKDIR"
# 检查依赖for cmd in s3cmd ; do  if ! command -v $cmd >/dev/null 2>&1; then    echo "Warning: $cmd not installed. Install it before running PoC."  fidone
cat > "$WORKDIR/README.md" <<'EOF'目录说明:- seaweed/: SeaweedFS 三节点部署脚本与 benchmark- scripts/: 通用测试脚本(生成数据、rclone benchmark)EOF
echo "初始化完成"


运行

chmod +x poc_setup_common.sh./poc_setup_common.sh



SeaweedFS

(快速部署 + PoC 脚本)


我们以 star 数最多的 SeaweedFS 为例。 SeaweedFS 拥有多 Master + 多 Volume + Filer + S3 gateway 的架构。三节点 PoC 的目标是:每台节点跑至少一个 volume,master 由于高可用需要做三主,s3 gateway 可部署在多节点以负载均衡。


假设你的六台节点主机名或 IP:

node1: 10.0.0.11

node2: 10.0.0.12

node3: 10.0.0.13

node4: 10.0.0.14

node5: 10.0.0.15

node6: 10.0.0.16


1) 每台节点准备(在每台节点上执行一次)

# 创建目录用于持久化mkdir -p /data/seaweedfs/{master,volume,filer,data}


2) 在 node1/2/3 上启动分别启动 Master:

nohup ./weed master -port=9333 -mdir=/data/seaweed/master -defaultReplication=001 -ip=10.0.0.11 -peers=10.0.0.11:9333,10.0.0.12:9333,10.0.0.13:9333  >> master.log2>&1 &
nohup ./weed master -port=9333 -mdir=/data/seaweed/master -defaultReplication=001 -ip=10.0.0.12 -peers=10.0.0.11:9333,10.0.0.12:9333,10.0.0.13:9333  >> master.log2>&1 &
nohup ./weed master -port=9333 -mdir=/data/seaweed/master -defaultReplication=001 -ip=10.0.0.13 -peers=10.0.0.11:9333,10.0.0.12:9333,10.0.0.13:9333  >> master.log2>&1 &


再在 node4/5/6 上分别启动 volume:

nohup ./weed volume -port=9334 -dir=/data/seaweed/volume -max=30 -mserver=10.0.0.14:9333,10.0.0.12:9333,10.0.0.13:9333 -dataCenter=dc1 -rack=rack1 -publicUrl=10.0.0.14:9334 -ip=10.0.0.14 >> v1.log2>&1 &
nohup ./weed volume -port=9334 -dir=/data/seaweed/volume -max=30 -mserver=10.0.0.15:9333,10.0.0.12:9333,10.0.0.13:9333 -dataCenter=dc1 -rack=rack1 -publicUrl=10.0.0.15:9334 -ip=10.0.0.15 >> v1.log2>&1 &
nohup ./weed volume -port=9334 -dir=/data/seaweed/volume -max=30 -master=10.0.0.16:9333,10.0.0.12:9333,10.0.0.13:9333 -dataCenter=dc1 -rack=rack1 -publicUrl=10.0.0.16:9334 -ip=10.0.0.16 >> v2.log2>&1 &


验证

# 验证curl http://10.0.0.11:9333


3) 在全部节点上部署 Filer + S3 gateway

./weed filer -s3 -ip=10.0.0.1x -s3 -master=10.0.0.11:9333,10.0.0.12:9333,10.0.0.13:9333


说明:在真实网络中,s3 gateway 应配置为绑定到实际 IP/域名,并通过负载均衡把多实例接入。PoC 为简化我们先做单点 gateway


4) SeaweedFS PoC 基准脚本(并发写/读 + multipart + head)

在第 4 个节点上创建 bench.sh:

!/usr/bin/env bashset -eo pipefail
# ----------------- USER CONFIG -----------------# Replace these placeholdersENDPOINT="https://s3.example.com"    # 你的 S3 endpoint(含 scheme)ACCESS_KEY="YOUR_ACCESS_KEY"SECRET_KEY="YOUR_SECRET_KEY"REGION="us-east-1"                   # 可选BUCKET="my-test-bucket"PREFIX="concurrent-test"             # S3 对象前缀FILE_SIZE_MB=50NUM_FILES=10CONCURRENCY=8TEST_DIR="./s3_test_files"DEV_SRC="/dev/zero"                  # 测试用 /dev/zero(真实负载用 /dev/urandom)# s3cmd extra options (add --host-bucket if your endpoint requires it)S3CMD_OPTS="--access_key=${ACCESS_KEY} --secret_key=${SECRET_KEY} --host=${ENDPOINT} --region=${REGION} --no-check-certificate"# ------------------------------------------------# Helper: detect parallel runnerparallel_cmd() {ifcommand -v parallel >/dev/null 2>&1; then   echo"parallel"else   echo"xargs"fi}# Creates test files named file.1, file.2, ...prepare_files() { mkdir -p "$TEST_DIR"/uploadecho"Generating $NUM_FILES files of ${FILE_SIZE_MB} MiB each in $TEST_DIR/upload ..."for i in $(seq 1 $NUM_FILES); do   f="$TEST_DIR/upload/file.${i}"   if [ -f "$f" ]; then     echo"  - $f exists, skipping generation"     continue   fi   # Use dd for portable creation; bs=1M count=... creates MiB   dd if="$DEV_SRC" of="$f" bs=1M count="$FILE_SIZE_MB" status=none   # optional: sync to ensure disk write finished   syncdoneecho"Files ready."}# Upload single file (used by parallel/xargs)upload_one() {local localpath="$1"local key="${PREFIX}/$(basename "$localpath")"# measure per-file time (seconds with nanoseconds) start=$(date +%s.%N) s3cmd put $S3CMD_OPTS"$localpath""s3://${BUCKET}/${key}" >/dev/null 2>&1 rc=$? end=$(date +%s.%N)if [ $rc -ne 0 ]; then   echo"ERR_UP|$(basename "$localpath")|$rc"   return$rcfiecho"OK_UP|$(basename "$localpath")|$(awk "BEGIN {print ($end - $start)}")"}# Download single filedownload_one() {local keyfile="$1"  # basename like file.1local remote="s3://${BUCKET}/${PREFIX}/${keyfile}"local dest="$TEST_DIR/download/${keyfile}" start=$(date +%s.%N) s3cmd get $S3CMD_OPTS"$remote""$dest" >/dev/null 2>&1 rc=$? end=$(date +%s.%N)if [ $rc -ne 0 ]; then   echo"ERR_DL|${keyfile}|$rc"   return$rcfiecho"OK_DL|${keyfile}|$(awk "BEGIN {print ($end - $start)}")"}run_uploads() {echo"Starting concurrent upload test: $NUM_FILES files, concurrency=$CONCURRENCY ..." mkdir -p "$TEST_DIR/logs" rm -rf "$TEST_DIR/logs/upload.log""$TEST_DIR/logs/upload_summary.log" || true runner=$(parallel_cmd) start_all=$(date +%s.%N)if [ "$runner" = "parallel" ]; then   # GNU parallel: we pass list of files   ls "$TEST_DIR/upload" | parallel -j "$CONCURRENCY" --no-notice --will-cite \     bash -c 'bash -lc "upload_one \"${0}\""' {} 2> >(grep -v '^$' > "$TEST_DIR/logs/upload.log")else   # fallback to xargs -P   ls "$TEST_DIR/upload" | xargs -n1 -P "$CONCURRENCY" -I{} bash -c 'upload_one "{}"' 2> >(grep -v '^$' > "$TEST_DIR/logs/upload.log")fi end_all=$(date +%s.%N)# Summarizeecho"Parsing upload logs..." awk -F'|''   BEGIN{ok=0;err=0;sumt=0}   /^OK_UP/ {ok++; sumt+=($3)}   /^ERR_UP/ {err++}   END{     print "upload_ok=" ok; print "upload_err=" err;     print "sum_seconds=" sumt;   }'"$TEST_DIR/logs/upload.log" > "$TEST_DIR/logs/upload_summary.log" total_seconds=$(awk -F'=''/sum_seconds/ {print $2}'"$TEST_DIR/logs/upload_summary.log") total_bytes=$(( FILE_SIZE_MB * 1024 * 1024 * NUM_FILES ))# compute throughput MB/s using awk for floating mathif [ -z "$total_seconds" ] || (( $(echo"$total_seconds == 0" | bc -l) )); then   throughput="N/A"else   throughput=$(awk -v tb="$total_bytes" -v s="$total_seconds"'BEGIN{printf "%.2f", (tb/1024/1024)/s}')fi wall_time=$(awk "BEGIN {print ($end_all - $start_all)}")echo"UPLOAD_SUMMARY: total_bytes=${total_bytes} B, wall_time=${wall_time}s, aggregate_throughput=${throughput} MB/s" cat "$TEST_DIR/logs/upload_summary.log"}run_downloads() {echo"Starting concurrent download test: $NUM_FILES files, concurrency=$CONCURRENCY ..." mkdir -p "$TEST_DIR/download" mkdir -p "$TEST_DIR/logs" rm -f "$TEST_DIR/logs/download.log""$TEST_DIR/logs/download_summary.log" || true runner=$(parallel_cmd) start_all=$(date +%s.%N)# Build list of filenames to download (assumes the same names as uploaded) filelist=$(ls "$TEST_DIR/upload")if [ "$runner" = "parallel" ]; then   echo"$filelist" | parallel -j "$CONCURRENCY" --no-notice --will-cite \     bash -c 'bash -lc "download_one \"${0}\""' {} 2> >(grep -v '^$' > "$TEST_DIR/logs/download.log")else   echo"$filelist" | xargs -n1 -P "$CONCURRENCY" -I{} bash -c 'download_one "{}"' 2> >(grep -v '^$' > "$TEST_DIR/logs/download.log")fi end_all=$(date +%s.%N)# Summarizeecho"Parsing download logs..." awk -F'|''   BEGIN{ok=0;err=0;sumt=0}   /^OK_DL/ {ok++; sumt+=($3)}   /^ERR_DL/ {err++}   END{     print "download_ok=" ok; print "download_err=" err;     print "sum_seconds=" sumt;   }'"$TEST_DIR/logs/download.log" > "$TEST_DIR/logs/download_summary.log" total_seconds=$(awk -F'=''/sum_seconds/ {print $2}'"$TEST_DIR/logs/download_summary.log") total_bytes=$(( FILE_SIZE_MB * 1024 * 1024 * NUM_FILES ))if [ -z "$total_seconds" ] || (( $(echo"$total_seconds == 0" | bc -l) )); then   throughput="N/A"else   throughput=$(awk -v tb="$total_bytes" -v s="$total_seconds"'BEGIN{printf "%.2f", (tb/1024/1024)/s}')fi wall_time=$(awk "BEGIN {print ($end_all - $start_all)}")echo"DOWNLOAD_SUMMARY: total_bytes=${total_bytes} B, wall_time=${wall_time}s, aggregate_throughput=${throughput} MB/s" cat "$TEST_DIR/logs/download_summary.log"}cleanup_remote_prefix() {echo"Cleaning up remote objects under s3://${BUCKET}/${PREFIX}/ ..."# Danger: this will recursively delete all objects under prefix s3cmd del $S3CMD_OPTS"s3://${BUCKET}/${PREFIX}/*" || trueecho"Remote cleanup done (errors ignored)."}usage() { cat <<EOFUsage: $0 [prepare|upload|download|both|cleanup] prepare   - create local files for upload upload    - run concurrent uploads download  - run concurrent downloads (assumes objects already on S3) both      - prepare + upload + download cleanup   - delete remote prefix objectsEOF}# Export functions so subshells (parallel/xargs) can call themexport -f upload_one download_oneexport S3CMD_OPTS ENDPOINT ACCESS_KEY SECRET_KEY BUCKET PREFIX TEST_DIR FILE_SIZE_MB NUM_FILES# maincmd=${1:-both}case"$cmd"in prepare)   prepare_files   ;; upload)   prepare_files   run_uploads   ;; download)   run_downloads   ;; both)   prepare_files   run_uploads   run_downloads   ;; cleanup)   cleanup_remote_prefix   ;; *)   usage   exit 1   ;;esac


运行

chmod +x bench.sh## 脚本支持 prepare|upload|download|both|cleanup 五个命令:## 只生成本地文件(不上传):./s3_concurrent_test.sh prepare## 只并发上传(会先生成本地文件,如果已存在会跳过生成):./s3_concurrent_test.sh upload## 只并发下载(假设对象已存在于 S3):./s3_concurrent_test.sh download## 完整流程:生成 -> 并发上传 -> 并发下载./s3_concurrent_test.sh both##清理远端 prefix(注意:会删除 s3://BUCKET/PREFIX/* 下的对象)./s3_concurrent_test.sh cleanup


关键指标:

指标

含义

测量方法

吞吐量 (Throughput)

单位时间内可读/写的数据量(MB/s 或 req/s)

并发上传/下载文件,统计总 bytes / 总时间

延迟 (Latency)

单个请求的响应时间

p50/p90/p99/p999 响应时间,可用 curl -w %{time_total} 或自定义脚本记录

错误率 (Error Rate)

请求失败的比例

统计 HTTP 5xx / 4xx 或 s3cmd 返回非 0 的次数

可用性 (Availability)

系统正常可访问的比例

uptime / ping 或健康检查接口

并发承载 (Concurrency / QPS)

系统在高并发下的表现

增加并发量,观察吞吐、延迟和错误率变化曲线

一致性/完整性 (Data Integrity)

上传后下载文件内容是否一致

对比 hash(md5/sha256)


6) SeaweedFS 故障模拟

在 nodex 上:

首先要下载 Chaosd,Chaosd 是 Chaos Mesh 提供的一款混沌工程测试工具,用于在物理机环境上注入故障,并提供故障恢复功能。执行以下命令:

curl -fsSLO https://mirrors.chaos-mesh.org/chaosd-$CHAOSD_VERSION-linux-amd64.tar.gztar zxvf chaosd-$CHAOSD_VERSION-linux-amd64.tar.gz && sudo mv chaosd-$CHAOSD_VERSION-linux-amd64 /usr/local/export PATH=/usr/local/chaosd-$CHAOSD_VERSION-linux-amd64:$PATH


我们可以注入如下错误:


占用 CPU(例如占 80% 持续 60 秒)

sudo chaosd attack cpu --cpu-percent 80 --duration 60s

网络延迟

sudo chaosd attack network delay --interface eth0 --delay 100ms --duration 60s

网络丢包

sudo chaosd attack network loss --interface eth0 --percent 10 --duration 60s

网络限速

sudo chaosd attack network bandwidth --interface eth0 --rate 1mbps --duration 60s

磁盘读写慢(IO 故障)

sudo chaosd attack disk latency --path /data --delay 100ms --duration 60s

占满磁盘

sudo chaosd attack disk fill --path /data --size 10GB --duration 60s


简单来说,通过 chaosd 来注入各种物理机故障,在此基础上观察关键指标。此外,别忘了保存好这个实验的 uid,用于后续的实验恢复。


!!!记录期间的 s3cmd 日志,比较错误率与恢复时间!!!



第4部分

真实案例警示


某些依赖 S3 的第三方工具(例如备份/归档工具)只在少数 S3 边缘 API 行为上有严格依赖——一次“看似小的 header 或签名实现差异”就能让生产备份失败。迁移不是“把数据搬过去”那么简单,是一次对接口、监控、备份与运维流程的全面考验。这就是为什么我主张做 48 小时 PoC,说白了:你要先把风险打成可量化的数字。


第5部分

致 CTO / 技术负责人的一句话


MinIO 社区版变“maintenance-only”不是一个“昨天的新闻”——它直接改变了自建 S3 的可靠性边界。对关键生产系统:别赌运气,做验证;对非关键环境:快速建立内部构建链;对长期策略:评估 Ceph / 商业厂商 / 活跃社区项目作为备选。时间不是你的朋友——但系统化的 48 小时 PoC 会给你可执行的答案。


A

更好的选择:EloqData


MinIO 进入维护模式,并不意味着市场在“放弃对象存储”,与之相反,对象存储的发展如火如荼。对象存储不再只是传统数据湖的基础设施,而正逐步成为新一代数据库底座。从以 Snowflake 为代表的OLAP湖仓,到 Kafka / Pulsar 等流式平台,越来越多核心基础设施选择构建在对象存储之上。真正的挑战只剩一个问题:如何在极低延迟、高并发的 OLTP 场景中,充分释放对象存储的潜力。


EloqData 给出了答案。


EloqKV 是一款完全兼容 Redis 的新一代事务型 KV 数据库,在百万级 QPS 压力下,实现纯磁盘读取 P9999 延迟低于 3ms;EloqDoc 是一款兼容 MongoDB 的文档数据库,采用先进的存算分离架构,在保证性能的同时大幅提升弹性扩展能力,并显著降低整体存储成本。


与传统云盘架构不同,EloqKV 与 EloqDoc 以对象存储作为主存储、本地 SSD 作为智能缓存层,在保障性能的同时,将存储成本压缩至传统方案的 十分之一。更重要的是,通过对象存储天然的多副本能力,构建“单计算副本 + 多存储副本”的高可用架构,显著减少了为高可用而付出的额外 CPU 与内存成本。


不仅如此,对象存储还为 EloqData 带来了秒级 Backup  Branch 能力,让数据备份、环境复制和测试发布从小时级缩短到秒级。


MinIO 进入维护模式,并不是对象存储的终点,而是新一代云原生基础设施的起点。随着底层存储形态的重构,EloqData 通过独创的“四元解耦”架构,率先突破了对象存储在 OLTP 场景中的性能限制,为数据库全面云原生化打开了真正可落地的路径,并正在定义下一代云原生数据库的技术标准。


EloqData,让对象存储真正进入 OLTP 核心战场。注册EloqCloud云服务(https://www.eloqdata.com),获取免费25GB存储和10000QPS的免费DBaaS服务。



信息来源

Source of Information

MinIO community edition: maintenance-only(官方 README)。GitHub   点击查看更多

社区与自托管圈对 MinIO 进入维护模式的报道与讨论。selfh.st   点击查看更多

Ceph RADOS Gateway — S3 兼容文档。Ceph 文档   点击查看更多

SeaweedFS 项目主页(含 S3 Gateway 说明)。GitHub   点击查看更多

Garage S3 兼容说明(项目文档)。Garage   点击查看更多


【声明】内容源于网络
0
0
晨章数据
内容 33
粉丝 0
晨章数据
总阅读7
粉丝0
内容33