跳转到主要内容

这是本节的多页打印视图。 .

返回本页常规视图.

兼容性

Silo 与 MinIO 在服务端、客户端与控制台三处的一致之处,以及有意为之的差异。

Silo 是 MinIO 的社区分支。本节记录 Silo 从 MinIO 继承了什么、在哪些地方有意做出不同选择,以及这对双向迁移意味着什么。

内容按阅读顺序:迁移指南(范围与 Docker)、原生软件包迁移(RPM/DEB)、服务器兼容性审计mcli 客户端说明特性设计子节记录相反的方向:Silo 在上游之外新增能力的设计文档。

1 - Silo 与 MinIO 服务端兼容性

经过源码验证的 Silo 服务端分叉兼容性审计:二进制、配置、S3 与管理行为、存储内部协议、软件包、容器与 Helm。

Silo 是持续维护的 MinIO 服务端分叉。它保留了面向 S3 客户端和磁盘数据的兼容性,但 绝不是一次在运维层面完全无感的二进制改名。本页是从上游基线迁移到 2026-08-06 所准备 Silo 源码时的兼容性契约。

警告

替换 MinIO 部署前请先阅读本页。 二进制、软件包、服务账号、systemd 单元、默认本地配置目录、容器路径、Helm 资源名、内嵌控制台、更新行为、若干授权判定及部分错误响应已经改变;数据盘和 MINIO_* 配置命名空间没有随产品改名。

审计范围与方法

这是一份源码审计,而不是对 release note 的简单汇编。

边界 审计值
上游基线 minio/minio@7aac2a2c5b7c882e68c1ce017d8256be2feea27f,2026-02-11
Silo 快照 pgsty/silo@219670d3176a5b27ded60914390d5ee7e763cf58,2026-08-06
提交集合 7aac2a2c..219670d3:共 96 个可达提交;审计时其中 93 个位于 origin/main,最后 3 个已在本地提交
源码净差异 523 个文件,新增 36,715 行,删除 21,450 行
解释口径 记录最终快照的行为;后来被替换或删除的中间状态,不会被写成当前行为

范围内每个提交都经过检查。Release 与 Security 文章只用来索引“预期变化”,随后仍以最终实现、测试、依赖图、构建配方、软件包载荷、容器入口和渲染后的 Helm 清单逐项核实。完整提交覆盖账本确保文档、CI 或被后续替代的提交也不会静默漏出范围。

该范围按 Git 可达性定义,而不是按 author date 排序。因此它包含 d4cd4b433:该提交的作者日期是 2025 年 12 月,但后来才接入基线后的提交图;它不是一条未记录的额外基线。

已打标签的 RELEASE.2026-08-04T00-00-00Z 停在 d88f46cce,比本次审计 HEAD 少 18 个提交。因此本页记录的是 2026-08-06 准备中的源码状态,并不声称最后 18 个变化已经进入公开软件包、镜像、标签或线上网站。

兼容性总览

表面 状态 实际结论
S3 线协议 除明确列出的例外外保持兼容 路由、XML/JSON 模式、SigV4、S3 头、端口与常规错误码沿用 MinIO 契约;下列安全与正确性修复会有意拒绝部分旧代码曾接受的请求
数据盘 兼容 .minio.sys、纠删码元数据、桶/对象布局、修复、复制和加密格式保留原名与模式;被污染或不可用的元数据会更早被拒绝
配置 基本兼容 既有 MINIO_* 变量、配置键、KMS/KES、IAM、通知与存储设置都保留;用户级默认目录变为 ~/.silo,并带有确定性的 ~/.minio 回退
指标与自动化 API 兼容 minio_* Prometheus 指标、/minio/* 路由、x-minio-* 头、管理/S3 错误标识及 release tag 语法不变
二进制与分发 已改名 minio 变为 silo;软件包、单元、镜像、Chart、归档、校验文件及路径改用 Silo 身份;服务端二进制没有安装兼容别名
运行时身份 已改变 CLI 文本、启动横幅、HTTP Server、User-Agent 应用名、FTP 横幅、日志名、支持链接以及部分人类可读错误改为 Silo
上游网络服务 已禁用 原地更新、更新轮询、callhome、SUBNET 注册与诊断上传不会联系 MinIO 服务
授权与安全 有意收紧 OIDC HMAC token、不安全 LDAP 失败、伪造复制元数据、对象资源对受保护桶写操作的越权、策略输入遮蔽、歧义版本 ID 以及若干畸形节点间请求均改变行为
内嵌 UI 与 Go 依赖 通过兼容 import path 使用分叉 Silo Console、MCLI 与 Silo Pkg 由 replace 选中,同时保留 github.com/minio/... 模块/import path
混合版本集群 此迁移边界不支持 私有 ReadMultiple storage-REST 操作已删除,却没有提升 storage REST v63;所有节点应作为同一构建整体升级

明确保留的兼容契约

协议、存储与配置名称

下列 MinIO 标识属于兼容性标识,并非尚未完成的品牌替换,必须继续可见:

  • Go 模块路径 github.com/minio/minio 及继承的 github.com/minio/... import;
  • MINIO_* 环境变量命名空间,包括 MINIO_ROOT_USERMINIO_ROOT_PASSWORDMINIO_VOLUMESMINIO_OPTS 和通知变量;
  • /minio/* 下的 S3/管理路由、x-minio-* 头、MinIO 特有 S3 扩展及既有 API 错误码;
  • minio_* Prometheus 指标名称;
  • .minio.sys 内部卷及所有既有磁盘元数据名称;
  • 默认 S3 端口 9000、既有 --address / --console-address 参数,以及 RELEASE.YYYY-MM-DDTHH-MM-SSZ 标签格式;
  • 配置 KV 格式、IAM 数据、KMS/KES 配置、加密元数据、桶元数据、复制状态与修复状态。

自动化 rebrand 基线记录了 137 个兼容 import、436 个环境变量名、19 个指标命名空间、84 个头、330 条路由、1 个内部根目录、3 个 Grid 命名空间、15 个 storage-REST 标识、58 个策略标识和 9,014 个导出符号。Guard 会把任何未评审的基线漂移视为兼容性失败。

同一组数据盘从 MinIO 切到 Silo 时不需要复制数据或重写元数据。但这并不意味着一切畸形历史对象都会继续被接受:下文的存储加固会拒绝不安全路径、非法纠删码几何、负 part size 以及过去可能继续向下传播的污染元数据。

源码兼容性

服务端模块仍为 github.com/minio/minio。Silo 在不强迫调用方改写 import 的情况下选择维护中的分叉:

replace github.com/minio/console => github.com/pgsty/silo-console ...
replace github.com/minio/mc      => github.com/pgsty/mc ...
replace github.com/minio/pkg/v3  => github.com/pgsty/silo-pkg/v3 v3.11.0

这保留了大部分源码兼容性,但不表示所有私有或导出 Go 符号永久冻结。内部 ReadMultiple 存储接口已删除,所选 silo-pkg 版本也包含依赖章节所述的开发者可见变化。

维护中的源码 remote 是 github.com/pgsty/silo,默认分支为 main;原 minio 分支已归档。go install github.com/minio/minio@... 仍会解析到上游项目,而不是 Silo,因此需要 clone Silo 仓库或使用显式 module replace。贡献不再要求 MinIO CLA,但提交必须带 DCO sign-off(git commit -s)。

身份、二进制与外联服务变化

表面 上游基线 Silo 快照 兼容性影响
服务端可执行文件 minio / minio.exe silo / silo.exe 脚本及绝对路径必须修改;归档、软件包和镜像不会安装 /usr/bin/minio 服务端别名
版本输出 MinIO 身份 Silo release/commit/runtime、AGPL、上游版权、PGSTY 修改版权与 MinIO 技术沿革 解析器应依赖稳定字段,不能 grep MinIO 文案
本地配置主目录 ~/.minio 新用户主目录默认 ~/.silo 见下述回退规则;与数据盘无关
HTTP 身份 Server: MinIO 与 MinIO 应用 UA Server: Silo;内部 batch/fan-out/perf UA 使用 silo-* / Silo 名称 x-minio-* 等协议头保持不变;按产品身份匹配的监控需要调整
人类可读文本 MinIO 横幅、帮助、错误、样例、FTP 欢迎语、支持链接 Silo 身份;样例优先 mysilo 精确匹配文本的日志解析器和快照会变化;除另行列出外,状态码/错误码不变
集成可见标签 MinIO NATS/Redis 连接名与 Veeam model NATS 名 Silo Notification、Redis CLIENT SETNAME Silo、Veeam model "Silo <release>" Broker 看板、连接名过滤器及 Veeam Inventory 展示会改变
KMS 校验文案 带 MinIO 品牌的冲突错误 中性的“同时存在 KMS/KES/static-key 配置”错误 配置规则不变;精确匹配文本的自动化会改变
更新器 release 轮询与原地更新路径 永久禁用 MINIO_UPDATE 会被解析但无法重新开启;管理更新路由保留并稳定失败,而不是消失
Callhome/SUBNET 注册、callhome、支持上传、内嵌 MinIO 支持公钥 为迁移保留配置解析但强制关闭;不注册/上传/POST;没有兜底加密公钥 删除依赖 MinIO 运营服务的自动化;请求方公钥的 inspect 加密仍可用
内嵌控制台 上游快照已移除 Console Silo Console v2.1.1,中英双语、Metrics V3、无 SUBNET UI 浏览器行为和资源发生变化;S3/Admin API 仍由服务端契约约束
OCI 内置客户端 没有维护分叉契约 /usr/bin/mcli/usr/bin/mc -> mcli 这里的 mc 只是客户端兼容别名,从来不是服务端别名
温层探针 临时对象内容为 MinIO 同长度探针内容为 Silo! 只可能通过后端检查或清理失败残留观察到;协议语义不变
日志轮转默认名 minio-*.log silo-*.log 按文件名匹配的采集器需要修改

被删除的 /api/health/upload 引用是一个 出站 SUBNET URL 路径,并不是 Silo 本地 HTTP 路由。正确的兼容性描述是“Silo 不再发起该 POST”,不能写成“删除了服务端 API”。

默认配置目录选择

未显式给出 --config-dir 时,Silo 会在启动时作一次选择:

用户主目录状态 选择目录 提示
两者都不存在 ~/.silo
只有 ~/.silo ~/.silo
只有 ~/.minio ~/.minio 旧目录提示;不会移动任何文件
两者都存在 ~/.silo 歧义警告

--config-dir 始终优先。除非同时指定 --certs-dir,证书目录跟随所选配置目录。自动化应明确传入 --config-dir,不要依赖文件系统探测。

分叉只新增了三个会实质改变兼容行为的服务端配置控制项;不存在一套并行的 SILO_* 替换命名空间:

设置 用途 默认值
MINIO_API_TRUSTED_PROXIES 通用来源地址信任边界 未设置:精确保留历史 trust-any 行为
MINIO_IDENTITY_LDAP_STS_TRUSTED_PROXIES / LDAP key sts_trusted_proxies LDAP STS 失败限流的来源分桶 不信任代理;使用 socket peer
MINIO_API_LEGACY_BUCKET_RESOURCE_MATCH 受保护桶/对象 IAM 边界的临时回滚 off

通知 KV 注册修复不会重命名既有环境变量。MINIO_UPDATE、SUBNET 与 callhome 输入仅作为被忽略/迁移兼容的输入保留,详见下一节。SILO_OPTS 只出现在自动生成的 inspect helper 内,不是 MINIO_OPTS 的通用替代品。

更新器、callhome、SUBNET 与 inspect

  • 启动过程不再轮询 dl.min.io;release URL 与上游 minisign 根公钥已移除。
  • 要求开启更新的 MINIO_UPDATE 值会产生警告并被忽略。公开及节点间管理更新 handler 仍然注册,返回 MethodNotAllowed 或稳定的“原地更新已禁用”错误。
  • subnetcallhome 键仍可解析,让旧配置能够启动;注册状态恒为 false,Console 的 SUBNET 变量被清空,callhome 强制关闭,不上传诊断或许可证负载。
  • Inspect 只会用请求方提供的公钥加密,不再回退到 MinIO 内置支持公钥。帮助脚本名为 start-silo.sh、执行 silocluster.info 只出现在请求方公钥流程中。
  • 请通过包管理器、镜像滚动或编排器升级;不要把针对 Silo 的 mc admin update / mcli admin update 当作升级方案。

安装与部署兼容性

RPM、DEB 与 APK

Linux amd64/arm64 软件包名为 silo,关键载荷如下:

/usr/bin/silo
/usr/lib/systemd/system/silo.service
/etc/default/silo                 (config, noreplace)
/usr/lib/sysusers.d/silo.conf
/usr/share/doc/silo/LICENSE
/usr/share/doc/silo/NOTICE

软件包会创建无 home 的系统账号 silo:silo。它 不会 chown 既有数据、迁移所有权、在安装时停止运行中的 MinIO 服务,也不会在包管理器层面对 minio 声明 ProvidesObsoletesReplacesConflicts。因此两个包可以同时安装,但使用随附 unit 时两个服务不能同时运行。

silo.service 声明 Conflicts=minio.service,以 silo:silo 运行,先读 /etc/default/minio,再读 /etc/default/silo。随包的 Silo 文件没有有效赋值,所以管理员在后一个文件显式覆盖前,旧文件仍然生效。启动命令为:

/usr/bin/silo server $MINIO_OPTS $MINIO_VOLUMES

切换 unit 前,必须确保 silo 可读取所有数据、证书、KMS 凭据和环境文件,并可写所有应写路径。由 minio:minio 持有的旧部署否则会启动失败。真正卸载软件包时会停止/禁用 silo.service;升级时 pre-remove 不会有意停掉运行中的服务。

容器镜像

源码仓库是 github.com/pgsty/silo,预期镜像名是 docker.io/pgsty/silo;不存在 pgsty/minio 在 Registry 层必然重定向到它的承诺。

  • 服务端只存在于 /usr/bin/silo;显式执行 /usr/bin/minio ... 会失败。
  • entrypoint 会把 argv 第一个词 minio 翻译为 silo;当 argv 以 serverfmt-gen 或选项开头时前置 silo。因此常见的 command: minio server /data 形式仍然可用。
  • 显式指定的 shell 或其他工具保持原样。
  • 所有降权路径最终都使用 exec,服务端成为 PID 1 并收到 SIGTERM 完成优雅退出,不会再隔着 entrypoint 超时。
  • 镜像默认 HOME=/tmp;任意 UID 或旧 MINIO_USERNAME 降权流程也会把 HOME 归一到可写目录。
  • 端口 9000/dataMINIO_* 接口不变。amd64/arm64 镜像 manifest 还包含经过校验的 MCLI RELEASE.2026-08-04T00-00-00Z 和只面向客户端的 mc 符号链接。
  • OCI 法律材料位于 /licenses/{LICENSE,NOTICE,CREDITS}

Helm Chart

继承的 helm/minio Chart、helm-releases、根 Chart 索引与重建脚本已删除。维护中的 Chart 为 helm/silo,版本 7.0.0

大多数 values 刻意保留原名,包括 minioAPIPortminioConsolePort 及所有 MINIO_* 环境设置。迁移时需要关注:

  • 镜像仓库改为 pgsty/silo
  • 生成的资源名和 label 跟随 Chart 名 silo
  • 默认服务账号改为 silo-sa
  • 证书/客户端挂载路径从 /etc/minio/{certs,mc} 改为 /etc/silo/{certs,mc}
  • 默认不再创建不安全的 console/console123 用户(users: []);
  • post-job 样例优先使用别名 mysilo,同时注册 myminio,让继承的 customCommands 仍能解析;
  • 新 Chart 执行 silo,因此只把镜像回滚到旧 MinIO 镜像并不安全。

使用新 Chart 且需要保留旧 Kubernetes 对象身份时,应从完整旧 values 开始,至少设置:

nameOverride: minio
fullnameOverride: <旧的完整 release 名>   # 例如 my-release-minio
serviceAccount:
  name: minio-sa
image:
  repository: pgsty/silo
mcImage:
  repository: pgsty/silo

应用前分别渲染新旧 Chart,比较 Service、selector、StatefulSet/Deployment、PVC 模板、Secret、服务账号、存储挂载、环境变量和端口。回滚时必须把 Chart 与镜像一起回滚

归档、来源证明与法律文件

Release 归档命名为 silo_<version>_<os>_<arch>,包含可执行文件、README、LICENSE 和 NOTICE。校验清单名为 silo_<version>_checksums.txt;每个归档带有 SPDX JSON SBOM,校验集合配有无密钥 Sigstore bundle。静态、禁用 CGO、带 kqueue tag 的二进制只发布 Linux、macOS、Windows 的 amd64/arm64 组合;过去只做编译检查但从不分发的架构不再作为 release gate。RPM/DEB/APK 目标为 Linux amd64/arm64,使用 YYYYMMDDHHMMSS.0.0 形式的时间戳包版本(RPM release 1),并有独立软件包校验清单。常规构建不再把构建机 GOPATH/GOROOT 写入二进制,提高了可复现性并消除路径泄漏。

上游基线 Docker 配方会下载并验证 dl.min.io 的预编译服务端;Silo release 镜像则消费所选 tag 上由源码构建、带 attestation 的精确归档。镜像发布是 GitHub Release 之后显式 dispatch 的独立流程;构建成功本身不会发布镜像。

CREDITS 根据实际链接进服务端的模块重新生成,并由 CI guard 保护。OCI 镜像包含它;考虑到约 1.8 MB 的体积,软件包与归档有意省略。上游 AGPL 与版权声明保留,同时加入 PGSTY 修改声明。

运行时与安全行为变化

即使是漏洞修复,只要用户可感知,也属于兼容性变化。下文的“收紧”表示过去可能成功、或以另一种方式失败的请求、策略、token、配置或损坏内部消息,现在会被拒绝。

认证、IAM 与请求身份

变化 最终行为 谁需要处理
OIDC JWT 校验(d24f449e0 client secret 不再作为验签 key。只接受非对称 JWKS 算法 RS256/384/512ES256/384/512RS3256/3384/3512ES3256/3384/3512HS256/384/512 失败;未知 kid 仍走既有 JWKS 刷新/重试 使用 HMAC 为 Silo token 签名的 IdP 必须迁移到非对称 JWKS key
LDAP STS 错误(3b950f8fa 未知用户与错误密码共享同一个外部 InvalidParameterValue 认证失败;LDAP 基础设施错误仍是服务端错误并记日志 客户端不能再从响应文本判断账号是否存在
LDAP STS 限流(18b712d499e10f6d9af441108905e40665ac 每来源、每节点内存桶:突发 10 次,每 6 秒补 1 个 token,空闲 TTL 15 分钟。只有认证失败消耗 token,成功与基础设施失败会退回。耗尽返回 HTTP 429、ThrottlingExceptionRetry-After: 6 代理应配置独立的 MINIO_IDENTITY_LDAP_STS_TRUSTED_PROXIES
LDAP 可信代理来源 socket peer 在允许列表中时,优先干净的 X-Real-IP;否则从右向左遍历 XFF 并跳过可信 hop。忽略 RFC 7239 Forwarded。代理 必须覆盖 X-Real-IP 审核 Ingress 头部清洗;该限流不是分布式账号锁
LDAP service-account 查找 对“User DN not found”的匹配改为大小写不敏感,依赖消息大小写变化时仍能得到预期 Admin no-such-user / login-name 错误分类 只有依赖偶然错误分类的脆弱客户端会观察到变化
桶/对象 IAM 边界(97b7d2804 12 个受保护桶写操作不再仅因 arn:aws:s3:::bucket/* Allow 而获准,必须有裸桶 ARN。Deny/NotResource 与内置 * 策略语义不变 自定义策略中确实需要桶写操作时加入 arn:aws:s3:::bucket;或临时使用全局逃生开关 MINIO_API_LEGACY_BUCKET_RESOURCE_MATCH=on
受保护操作 删除/强删桶;写/删桶策略;写复制、生命周期、对象锁、版本、CORS;删 CORS;写桶 QoS 或 Inventory 配置 读/list、创建桶、标签、加密与通知仍沿用历史匹配行为
有效策略输入(2f55347f7 服务端派生条件不能被同名 header/query 遮蔽;策略包中精确 key 优先。已有/请求标签、存储类、content/copy/checksum、对象锁、签名年龄与 list 参数均来自操作实际消费的值 误依赖攻击者可控遮蔽值的策略不再匹配
请求标签 PutObject、CreateMultipartUpload、PutObjectTagging 把 s3:RequestObjectTag/* 绑定到已解析输入;ExistingObjectTag 只来自已存元数据;其他操作路径保留旧 header 回退 重新测试带标签条件的写策略
s3:signatureAge 只在已经验证的 presigned SigV4 请求中出现 注入原始 x-amz-signature-age 不再产生该条件
s3:versionid744a9dcd7 缺失就真正缺失;空白被归一;DeleteObject/MultiDelete 使用每个对象的有效版本。URL version 不能诱骗不同 XML version 重测基于版本的删除策略及依赖 Null 的策略
复制元数据(56fa63bfd 普通 PUT/COPY 不能注入内部复制状态/时间元数据;经认证的 replica 请求必须拥有 ReplicateObjectAction;multipart 与 Snowball 的合法复制流程保留 自定义复制调用方必须走受授权的复制路径

桶边界逃生开关在启动时全局生效,而且是全有或全无;它用于迁移,不是长期混合策略模式。空或无法解析的 LDAP 来源不会被放入一个共享限流桶;在能派生出可用来源前不对它限流。

通用来源地址信任

fe6dc4780 新增 MINIO_API_TRUSTED_PROXIES,因为所选客户端地址会进入 aws:SourceIp、审计 remotehost、事件 Host、管理 trace 及节点间转发。

结果
未设置 精确保留历史行为:相信任何 peer 提供的来源头;依次选择最左 XFF、X-Real-IP、Forwarded
noneoff 忽略三种来源地址头,使用 TCP peer
IP/CIDR 列表 只有 TCP peer 在列表中时才相信头;XFF 从右向左越过可信 hop(最多 100 个),随后取最后一行 X-Real-IP,再从右向左遍历 Forwarded

畸形配置、非空但没有任何代理的列表,或远程 env:// 读取失败都会 fail closed 并阻止启动,而不是回退到 trust-any。收到的链中无效值会跳过;没有可用地址时回退 peer。只在 FTP/SFTP peer bridge 中隐式信任 loopback,不会把链内任意 loopback 当成可跳过 hop。

继承的 _MINIO_API_XFF_HEADER=off 保持旧语义和初始化时点:它只关闭 XFF 解析,不影响 X-Real-IP 或 Forwarded,因而不是安全边界。需要把所有合法转发请求的集群节点加入列表,并确保边缘代理覆盖或剥离客户端提供的三种来源头。

S3 请求与响应行为

范围 变化与兼容性影响
Presigned 流式认证 query/presigned SigV4 请求声明 STREAMING-UNSIGNED-PAYLOAD-TRAILER 时返回 SignatureVersionNotSupported,不能降级为匿名授权;header SigV4 会在读取 body 前验证
Snowball 先授权后解包 tar;流式 trailer 绕过无法在后续失败前写入对象
S3 Select 记录限制 CSV 输入、JSON Lines 输入和输出记录超过 1 MiB 时返回 OverMaxRecordSize 事件。JSON Lines 始终使用有界 reader(在支持 SIMD 的 CPU 上可能变慢),JSON 解析错误为 JSONParsingError,已完成记录可先于终止错误送达
流式响应 tracking writer 实现 Flush;Write/Flush 会记录隐式 HTTP 200。ListenBucketNotification/watch 流及 S3 Select keepalive 能及时送达,审计/状态指标也能记录已提交状态
Multipart 整对象 checksum FULL_OBJECT CRC32/CRC32C/CRC64NVME 完成时可完全省略每 part checksum;只要提供任意一个仍会校验;COMPOSITE 仍要求每个 part。零字节 multipart 对象 checksum 会正确保存
Multipart part 顺序 重复或非递增 part number 在组装前以 InvalidPartOrder 失败;允许空洞及不从 1 开始;upload 保留供重试
纠删码读缓冲池 恢复正确 shard buffer 所有权,避免池失效以及可能造成卡死、损坏或严重性能下降的错误缓冲关联
更新缓冲 返回的下载 buffer 具有正确所有权;公开更新器随后已禁用,所以当前受支持升级路径不会运行该代码

分布式存储与私有 API

这些变化通常对 S3 客户端不可见,但会影响混合版本集群、自定义内部调用方、损坏磁盘及恶意 peer。

  • ReadMultiple 及私有 storage-REST /rmpl endpoint、客户端方法、导出 Go 类型和指标均已删除。外部 S3 List/Get 不变。storageRESTVersion 仍为 v63,不能据此推断混合节点兼容。
  • 每个远程 StorageAPI 路径字段、嵌套元数据名和原始 volume sink 都在存储边界验证,包括 peer-S3 Grid 消息;拒绝词法遍历、volume root alias 及 Windows 分隔符/盘符形式。
  • 所有解码 sink 及 CheckParts/VerifyFile 都会拒绝非法纠删码几何、非正 block、负 part size 及不可用已存纠删码元数据。
  • 节点间分配声明受限:AppendFile 预分配最多 1 MiB 但仍接受 body;DeleteVersions 边解码边增长并拒绝负声明;旧 ReadFile 上限为 5 GiB。
  • 有 deadline 的工作会把 worker panic 转成错误并记录有界堆栈,不再让进程崩溃。
  • ReadParts 会跨 keepalive frame 保留真实后端错误;空 part 列表成功返回空结果,不触发 trace panic 或 goroutine 泄漏。
  • ReadMultiple 删除后遗留的 HTTP stream helper 随后被清理;该清理没有额外公开行为变化。

该 containment 是词法层面的,不解析文件系统符号链接;审计也没有原生 Windows 服务端 CI。应整体升级所有节点,即使节点间端口有 root 凭据与输入验证,也不要向不可信客户端暴露。

通知配置与审计输出

  • NATS 现在注册解析器实际读取的 user_credentialsnkey_seedtls_handshake_first;AMQP 注册 immediate;既有环境变量名不变。
  • 旧字面量 MINIO_NOTIFY_NATS_USER_CREDENTIALS 仍只对 NATS 接受;优先级为环境变量、新 key、旧迁移 key。
  • AMQP 旧配置迁移会正确映射 immediate。非法通知配置错误只标识名称,不再回显凭据值。
  • 自动生成的 PostgreSQL 通知 DSN 会正确引用/转义每个 libpq 值并使用正确 user 关键字;显式 connection_string 原样透传。
  • dangling object 删除审计重新在 merrs 中包含每块盘的错误。

有一个继承的迁移风险被明确记录,而没有伪装成已经修复:PostgreSQL/MySQL 旧通知迁移可能写入当前 target schema 未注册的 host/port/user/password/database key,导致下次加载时禁用全部 target;历史密码还可能以明文存储。重启到 Silo 前必须审核旧通知 KV 状态。

工具链、依赖与内嵌组件

构建声明从 Go 1.24 + 1.24.8 toolchain 迁移到 go 1.26.5。即便没有修改 Silo 源码,这也可能改变 TLS、HTTP、DNS、调度器、GC 及标准库边缘行为。Go-Jose、OpenTelemetry、Go crypto/network 模块、云 SDK、etcd、NATS 与压缩库等安全敏感依赖也已升级。

这些升级带来的可观察边缘修正包括:Go TLS/X.509/URL/archive、MQTT 超长 UTF-8 packet 编码、畸形 Azure NTLM challenge、Thrift framed transport 与 32 位编译、NATS 认证/授权/身份/拒绝服务,以及 Prometheus remote-read/write 与 UI 加固。它们属于依赖行为变化,但不表示每条 advisory 路径都能从 Silo 到达。jsonparser 的 CVE-2026-32285 调查没有产生补丁:解析到的 v1.1.2 已包含修复,也没有发现可达的脆弱符号,所以本范围内没有相应兼容性差异。

重要的有意依赖决策如下:

组件 最终选择 兼容性理由/影响
Console pgsty/silo-console v2.1.1,保持 github.com/minio/console 路径 恢复内嵌 UI,应用 Silo 品牌和双语文本,加入 Metrics V3,移除 SUBNET 流程并修复指标图例未翻译问题
客户端库 pgsty/mc,保持 github.com/minio/mc 路径 Console import path 不变,同时使用维护中的 MCLI 分叉
公共包 pgsty/silo-pkg/v3 v3.11.0,保持 github.com/minio/pkg/v3 路径 提供 IAM 精确匹配的一半、LDAP TLS/StartTLS/deadline/close 修复、证书 watcher 清理和 RNG 修复
Kafka Sarama 1.45.1 固定以避免破坏性的 broker 协商漂移
PostgreSQL lib/pq 1.10.9 固定以避免 nil []byte / PostgreSQL 14 以前版本的行为回归;自动 DSN 引用在服务端代码中修复
压缩 klauspost/compress 1.18.7 显式安全/正确性升级
Thrift 0.24.0 修复 32 位构建
systemd 库 require 22.7,replace 为 22.6 在上游修复单调时钟回归前保留 NetBSD 编译能力

LDAP 包现在会在 ldaps:// 中使用 TLS 字段;即便开启 server_insecure,StartTLS 仍保持启用;还修复了 nil TLS panic、StartTLS deadline 以及失败后连接关闭。证书 watcher 停止时不再泄漏;Windows 上轮询可能让 reload 最多延迟约十秒。RNG subkey 熵/reset 行为也已修正,不过服务端不会走 reset 路径。

对外部 silo-pkg Go 使用者,有两项变化比服务端自身调用路径更宽:xtime.Duration JSON 从整数纳秒变为 duration 字符串;部分 AIStor action 词汇/受保护 action helper 与上游不同。特别是 Policy.IsAllowedActions 可能在受保护 action 上产生不同结论,但服务端并不调用它。服务端通过 YAML/msgp 保存相关状态,也没有调用这些有差异的 AIStor/action helper 路径,因此没有发现由这些库变化引起的服务端数据迁移或授权变化。

新工具链重新生成了 String() 文件。合法枚举输出不变;差异来自生成器来源与非法值格式化机制,不单独宣称为 S3 行为变化。

已知残余风险与未修复项

本次审计不会把继承的限制包装成兼容性承诺:

  1. 默认来源 IP 仍可伪造。 未设置 MINIO_API_TRUSTED_PROXIES 时刻意保留上游 trust-any 行为。直连部署设为 none,代理部署使用精确 allowlist。
  2. 版本条件仍有缺口。 MultiDelete governance bypass 的二次授权仍读取 query/缺失 version,而非每个 XML entry;Snowball 在逐文件授权后才读 PAX minio.versionIdusernameuseridsignatureversionauthType 条件 key 仍无条件插入空值,因此对它们使用 Null 仍是“存在但为空”的语义。
  3. Multipart parser 防御尚未完整。 Handler 已修复顺序,但 object layer 没有独立 uniqueness 防线;XML root 验证和继承的非数字 part 错误映射未修改。
  4. 旧通知迁移仍有风险。 按上文说明审核。
  5. 存储路径校验为词法层面。 不解析符号链接;未独立覆盖原生 Windows 执行。
  6. 私有 API 不是稳定兼容承诺。 ReadMultiple 表明即便 storage REST 协议号不变,操作仍可能消失。不要跨越该边界滚动运行混合构建。
  7. 源码结果不等于已发布制品。 在逐渠道验证前,本页不声称 GitHub 标签、软件包、OCI manifest、签名或线上站点已经包含仅存在于审计 HEAD 的最后三个提交。
  8. 信息性 HTTP 响应的跟踪仍不完整。 response tracking 层会把 1xx 当成最终响应;Flush/隐式 200 修复没有引入该行为,也没有声称修复它。

迁移检查清单

从 MinIO 切换到 Silo 时,建议按此顺序执行:

  1. 记录精确 MinIO 二进制/tag、Chart 与 values、镜像 digest、包载荷、service unit、环境文件、配置目录、数据所有权、IAM 策略、OIDC/LDAP 设置、通知 target 与代理拓扑。
  2. 备份配置与 IAM 元数据。Silo 原地读取旧磁盘,但回滚仍需要旧可执行文件/配置/Chart,以及可用的原所有权。
  3. 把服务端执行名改为 silo,不要假设 /usr/bin/minio 存在。容器可翻译 argv 层的 minio server,不能翻译绝对路径。
  4. 明确决定配置目录:用 --config-dir ~/.minio 复用,或让“只有旧目录”回退选中它。不要意外创建空 ~/.silo,再误以为旧配置丢失。
  5. 使用软件包时,授予 silo:silo 对数据、证书、secret 和日志的权限。把有意覆盖移入 /etc/default/silo,并理解它会覆盖 /etc/default/minio
  6. 使用 Helm 时,以完整旧 values 分别渲染新旧 Chart;需要时用 nameOverride / fullnameOverride / serviceAccount.name 保留名称;Chart 与镜像原子切换。
  7. 删除 updater、callhome、SUBNET 注册和支持上传自动化,改用软件包/镜像/编排器滚动及自己的诊断传输渠道。
  8. 把 HMAC OIDC token 改为非对称 JWKS。分别测试 LDAP 成功、错密码、未知用户、后端故障和限流路径。
  9. 为 12 个受保护 action 加裸桶 ARN;测试有效 tag、signature-age、source-IP 和逐版本删除条件。旧桶开关只作为临时回退杆。
  10. 设置 MINIO_API_TRUSTED_PROXIES=none 或精确列表,清洗三种来源地址头,并加入会转发认证请求的集群 peer。
  11. 测试超大 S3 Select 记录、流式通知、unsigned trailer 拒绝、multipart 整对象 checksum、重复 part、复制、修复、KMS、每个通知 target、审计摄取及容器优雅关停。
  12. 把分布式集群所有节点作为同一构建升级。回滚时旧 Chart 与旧镜像成对使用,绝不能只回滚一个。

验证证据

本次审计以最终源码为准,而不是只相信文字。在记录的快照上:

检查 结果/边界
提交枚举 下方账本对 96/96 个提交完成分类;origin/main 93 个,本地准备态 HEAD 另 3 个
净差异审查 523 个变化路径全部归入服务端运行时、内部协议、依赖、分发、文档、测试或被替代变化
Rebrand 兼容 guard 通过;兼容 manifest、分发/运行时断言及 Docker argv 测试保持稳定
Go 测试 219670d31 上完整 go test ./... 通过,包括 cmd、OIDC、LDAP、notify、event target、Grid、handler、hash 与全部 S3 Select 包
Helm 迁移 buildscripts/verify-helm-migration.sh 通过:lint、render、旧版升级、归档及 7 个渲染资源的身份比较
软件包生命周期 buildscripts/package/lifecycle_test.sh 通过;包载荷/provenance 断言覆盖空 DEB conflict 元数据、unit/default 路径与法律文件
站点 严格 make check 通过:模块校验、warning-fatal Hugo 渲染(617 EN / 615 ZH 页)以及 1,084 个 HTML 文件中的 388,962 个内部引用;git diff --check、双语锚点及 96/96 提交覆盖也通过

Security 文章提供更深入的威胁模型和测试向量,但其中历史性的“已发布/未发布”状态只描述写作当日。与最终快照冲突时,以本页审计边界为准。

完整提交覆盖账本

哈希按图顺序排列。Merge、文档、测试和 CI 提交也全部列入,因为分发行为和兼容性主张的证据强度同样会影响用户;“无独立运行时差异”只表示这一点,不表示没有审查。

分类 提交 验证后的净效果
初始分叉、Console、CI 与依赖基线 d4cd4b4338630937e768521b37f00f3cf74f5abd9a80f377fc616df2f9a40dcee55e5391ce1c537eb68e0ba9971869bd30bff58df949e4fa06394 Go/SDK 演进;恢复/分叉内嵌 Console;OCI 内加入 MCLI;替换 CI;修复 LDAP TLS 回归;升级安全依赖。两个 merge 提交没有超出父提交的额外差异
四月安全系列 d24f449e03b950f8fa56fa63bfd3252d5b7ff444b6f37efb6e5b00db4c0fd5e18b712d499e10f6d9af44110890f48dbe777 OIDC、LDAP STS、复制元数据、S3 Select、unsigned trailer/Snowball、Go 1.26.2、限流计数/来源加固及安全文档
五至六月可靠性与私有 API 65795ee1f5e40665acfd69c89d073ac52472df627ff893e61b1d3ad495d30d5 HTTP Flush、最终 LDAP 分桶、完整 S3 Select 边界、删除 ReadMultiple、Go 1.26.4/依赖更新及文档链接变化
八月前的组件集成 ce01ccbdc4dfc27ce3b7f52ca437babc0c39c1aec051815fcc3c8a3f192f3f0 历史 Chart 镜像切换、安全依赖更新、通知流 merge、可移植依赖 pin、压缩库、MCLI replace、Console v2.0
八月运行时正确性/安全 c8590413f3e14733f192471792689d346bf58069a32aca36fd8fffca7baa67080e8eaa42b6f70ab081af351a7038366f65422c1e41fd97b7d28042f55347f7744a9dcd7fe6dc4780162ded3430c14d81519dd1dc1722602177ef Multipart、纠删码 buffer、响应提交、panic containment、路径/元数据/分配/ReadParts containment、遗留清理、IAM/有效值/version ID/source trust、通知/libpq 与审计细节
Chart 加固与审计文档 dfe6698625f4513fd4b42ee4e8a8eae745ab 安全的 Chart 用户默认值、门户/文档路由、维护者 ignore 规则与 advisory;只有 Chart 默认值直接改变运行时分发行为
到 20260804 标签的发布工程 9c799f42d10c7670b8cf7df097b32863c852632ade1111814ae52f475236c7911d79fddc3b8a55deeca674a6964c185d5a62ca4971d9e064b5555aa5139369021110b45d88f46cce RPM/DEB/APK、provenance、OCI 发布 gate、固定 lint/generation、修复 S3 Select 测试竞态、广泛 CI、PID 1 信号、已发布目标交叉构建、安全 release dispatch、可复现性、删除陈旧配置、systemd 路径、真实失败 gate 与运行镜像关停断言
Silo 切换与 2026-08-06 准备态 HEAD 15def34dc77bdc4c0c15ab1083330749911be071bb77ebd8df51666613c2a3cfd2ca1c6dc46b16ec6c47733abcf1c77d5a262717d7bf6740e6978b57275be305be686b8a6d6d9b026bd9cf77e219670d31 清理未发布 MinIO 分发残留;Silo 运行身份/离线边界;改名软件包、OCI、Helm 与迁移 guard;固定 fixture;文档/仓库切换;Console 2.1.0→2.1.1;Node 24 action;DCO/法律/文档完善;重生成 CREDITS;交付 LICENSE/NOTICE

账本共 96 个唯一提交。范围内被替换的变化——例如 Console 2.0 → 2.1.0 → 2.1.1、历史 pgsty/minio 镜像/Chart 状态,以及更新器禁用后的下载 buffer 代码——只在留下最终兼容性影响时描述。

参见

2 - 从 MinIO 迁移到 Silo

哪些改变、哪些不变,以及容器部署如何切换。软件包安装见《原生软件包迁移》。

从 MinIO 迁移到 Silo 是一次原地二进制替换,不是数据迁移。不导出、不重新导入任何东西。容器部署中,唯一必须修改的是镜像名。RPM/DEB 安装见原生软件包迁移

哪些改变

按重要性从大到小:

  1. 容器镜像minio/minioquay.io/minio/miniopgsty/minio 统一替换为 docker.io/pgsty/silo
  2. 软件包、systemd 服务与服务端二进制miniosilo
  3. 上游服务:原地更新器与 MinIO 官方 callhome/SUBNET 被禁用;升级通过软件包、镜像或编排系统进行。
  4. 默认操作系统服务账号silo——仅影响全新安装;迁移场景继续以数据现属主运行。
  5. 品牌呈现:启动横幅、Console 外观、日志措辞与产品链接显示 Silo。

哪些不变

  • 对象数据与 .minio.sys 元数据目录——磁盘格式未变,同一份数据与 MinIO 双向通用。
  • Bucket、版本、用户、Access Key、策略、生命周期规则、复制状态、加密元数据。
  • S3 API、SigV4 签名、SDK、mc/mcli、预签名 URL 行为。
  • 端点主机名、API 端口 9000、Console 端口、卷挂载。
  • MINIO_* 环境变量与既有服务端参数。
  • /minio/* 路由、x-minio-* 头、minio_* 指标。

没有数据转换步骤。若你的 MinIO 版本已很陈旧,需要在预发环境验证的是版本跨度本身——那是一次大版本软件升级,不是格式变化。

Docker 迁移

无论当前使用哪个镜像,统一替换为:

docker.io/pgsty/silo:<RELEASE-tag>

tag:不可变的 RELEASE.YYYY-MM-DDTHH-MM-SSZ(建议钉住)、滚动 latest,以及下述 -distroless 变体。旧的 pgsty/minio 仓库保持已发布状态,冻结在最后一个 tag。

Compose 中只改镜像一行:

services:
  minio:                              # 服务名可继续为 "minio"
    image: docker.io/pgsty/silo:<RELEASE-tag>
    command: server /data --console-address ":9001"
    environment:                      # MINIO_* 不变
      MINIO_ROOT_USER: ${MINIO_ROOT_USER}
      MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
    ports: ["9000:9000", "9001:9001"]
    volumes:
      - minio-data:/data              # 同一卷、同一份数据
volumes:
  minio-data:
docker compose pull minio && docker compose up -d minio

entrypoint 会翻译旧的第一个参数,继承下来的 command: minio server /data 继续可用;写死的 entrypoint: /usr/bin/minio 需改为 /usr/bin/silo。现有 mc ready local 健康检查继续可用,原生替代为 test: ["CMD", "silo", "healthcheck", "ready"]参考)。不要执行 docker compose down -v——-v 会删除数据卷。

Distroless 变体

pgsty/silo:<RELEASE-tag>-distroless 只包含 silo 二进制:没有 shell、没有 mc、没有 curl。内置 HEALTHCHECK(即原生探针),任意 --user 下均可运行:

docker run -d --name silo \
  -p 9000:9000 -p 9001:9001 \
  -e MINIO_ROOT_USER=admin \
  -e MINIO_ROOT_PASSWORD=change-me-long-password \
  -v silo-data:/data \
  docker.io/pgsty/silo:<RELEASE-tag>-distroless \
  server /data --console-address ":9001"

同一部署的 Compose 写法:

services:
  silo:
    image: docker.io/pgsty/silo:<RELEASE-tag>-distroless
    command: server /data --console-address ":9001"
    environment:
      MINIO_ROOT_USER: admin
      MINIO_ROOT_PASSWORD: change-me-long-password
    ports: ["9000:9000", "9001:9001"]
    volumes:
      - silo-data:/data
volumes:
  silo-data:

depends_on: condition: service_healthy 无需配置 healthcheck: 即可生效。卷上的数据格式与经典镜像、MinIO 完全一致——几种镜像可在同一份数据上互换使用。TLS 证书挂载到 /tmp/.silo/certs。若命令行 flag 改动了监听地址,用 MINIO_HEALTHCHECK_URL 指定内置探针的目标。镜像内没有 shell,调试用 docker debug / kubectl debug

Kubernetes

kubelet 探针是 pod spec 中的 httpGet 请求;Docker HEALTHCHECK 被忽略,两个镜像变体的探测方式完全相同,现有探针配置继续可用。Helm 部署用 nameOverride/fullnameOverride 保持 release 身份,应用前比对 helm template 输出(细节)。

回滚

磁盘格式未变、两侧通用:把 image: 改回记录的 MinIO tag,docker compose up -d。同一卷保持挂载,Silo 运行期间写入的数据 MinIO 仍可读取。

一个集群只运行一种二进制

分布式节点在 bootstrap 时相互校验二进制。起进不同二进制对端之间的节点不会报错退出,而是无限停在 activating,日志记录:

Expected Silo binary checksum: ..., seen: ...
Waiting for at least 1 remote servers with valid configuration to be online

该约束适用于任意两个不同的二进制:MinIO 与 Silo 之间如此,两个不同版本的 Silo 之间同样如此。因此不要逐节点迁移——将来升级也一样。所有节点一次切换:全部停旧二进制,再全部起新二进制(Compose 中一次编辑改完所有节点的镜像,执行一次 docker compose up -d)。单机部署不受影响。回滚同理。同一二进制的滚动重启正常可用,重启前用 silo healthcheck --maintenance cluster 把关(退出码 0 = 停掉本节点安全)。

验证

silo healthcheck ready                   # 本节点在服务;退出码 0/1
silo healthcheck cluster                 # 集群级写 quorum
mc admin info <现有别名>                  # 所有节点在线、新版本、旧别名直连

随后下载一个已知对象比对校验和,用现有 SDK 跑通一个应用,重启服务一次并复查。

3 - MCLI 客户端兼容性注记

pgsty/mc 与上游 minio/mc 的差异

mcli 是 Silo 构建的 MinIO 客户端(mc)。本页记录二者在哪些地方可以互换使用,在哪些地方存在差异。

pgsty/mc 从上游项目 minio/mc 的最终提交 77f82e18(2025-11-06)分叉而来。上游仓库已于 2026 年 7 月归档,且从未发布过包含该提交的版本 —— 因此每一个 mcli 版本都比历史上任何官方 mc 二进制更新。分支至今的发布版本:2026031320260321202604172026080420260806

兼容原则

本分支只遵循一条规则:改名的是交付物与渠道,不是你手里的工具。

  • 改名 / 更换 —— 磁盘上的制品名(mcli)、--version--help 中的产品身份、分发渠道(GitHub pgsty/mc、Pigsty 软件仓库、docker.io/pgsty/mc),以及制品签名密钥。命令语法没有改 —— 而且取决于你怎么安装,连你敲的名字都可以不变。
  • 保持不变 —— 全部命令、子命令与参数;S3 与 admin API 行为、请求签名与协议头(x-minio-*);JSON 输出结构;正常操作的退出码;配置文件格式与别名语义;MC_* 环境变量(含 MC_HOST_<alias>);.part.minio 断点续传后缀;以及 Go 模块路径 github.com/minio/mc
  • 切断 —— 所有连向 MinIO 运营服务的通道:发布/更新源、SUBNET 支持与许可门户、遥测,以及预置的 play 演示别名。受影响的命令为保持脚本兼容而保留,以稳定错误快速失败,而不是直接消失。
  • 保留 —— 上游版权与 AGPL-3.0 许可证。运行时输出同时致谢 MinIO, Inc. 与 PGSTY。

上游 mc 写出的配置文件 mcli 原样可读,反之亦然;两个客户端都可以连接 MinIO 服务器、Silo 服务器以及任何其他 S3 兼容端点。

差异区别

按「影响到你的可能性」从高到低排列。

1. 名字 —— 你敲的是什么,配置就在哪

对许多用户来说这里其实什么都没变:容器镜像的入口仍然是 mc,以 mc 名安装的二进制与上游行为完全一致。变的是我们 交付 的东西 —— 归档包与 Linux 软件包把二进制装为 /usr/local/bin/mcli(软件包名 mcli)。

两个名字都没有被硬编码在任何地方。上游客户端自 2016 年起就根据被调用的名字派生运行时身份,而 mcli 正是上游自己的 CONFLICT.md 为解决与 Midnight Commander 的冲突所推荐的改名方案(issue #873)—— 本分支只是把这个建议扶正为官方发行名,代码零改动。这套机制的实际含义如下:

跟随调用名 与名字无关、恒定不变
配置目录:~/.mc vs ~/.mcli(Windows:%USERPROFILE%\mc\ vs …\mcli\ 环境变量:恒为 MC_* —— 不存在 MCLI_CONFIG_DIR
帮助与用法文本中显示的程序名 config.json 格式 —— 双向完全一致、可互换
shell 自动补全的注册名 全部命令、参数、JSON 输出、退出码
User-Agent 应用后缀(mc/… vs mcli/… --config-dirMC_CONFIG_DIR 覆盖

唯一真正的坑:第一次运行 mcli 时,你已有的 mc 别名不会出现 —— 它从空的 ~/.mcli 开始。两种解法:继续以 mc 名调用它(一个符号链接即可 —— 起作用的是 argv[0]),或用 cp -a ~/.mc ~/.mcli 一次性搬运状态。自动化与配置模板则应显式设置 MC_CONFIG_DIR:环境变量前缀不跟随名字,一套模板即可通吃两个名字。详见迁移指南

获取渠道:GitHub Releases(SHA-256 校验文件 mcli_<version>_checksums.txt)、Pigsty 软件仓库(RPM 经 GPG 签名,密钥指纹 9592A7BC7A682E7333376E09E7935D8DB9BD8B20),或 docker.io/pgsty/mc。上游的 minisign 公钥不为这些制品签名,dl.min.io 永远不会被访问。发布标签(RELEASE.YYYY-MM-DDTHH-MM-SSZ)与软件包版本(YYYYMMDDHHMMSS.0.0)沿用上游方案。

2. mcli update 恒定失败 —— 这是有意的

自更新已移除。mcli update 不联网、不替换自身二进制,打印明确提示并 恒定以退出码 1 结束。上游 mc update 在已是最新版时返回 0,因此 任何调用它并把非零退出码视为失败的 cron 任务或脚本都会开始报错 —— 请删除该调用,改用软件包管理器或 GitHub Releases 升级。每次执行时向上游发布源的版本探测也已移除,MC_UPDATE / MINIO_UPDATE 不再被读取。

mcli admin update ALIAS —— 升级 服务端 —— 命令仍在,但 Silo 服务端会在服务侧拒绝就地升级。)

3. SUBNET、许可与遥测命令

所有连向 MinIO SUBNET 的路径均在构建期关闭。受影响的命令保留名称与参数,打印稳定提示 —— “MinIO SUBNET services (registration, licensing, uploads) are disabled in this Silo build of mc; diagnostics remain available locally.” —— 并以退出码 1 结束:

命令 现在的行为 替代方式
mcli license register 提示后退出码 1 ——
mcli license update ALIAS(在线续期) 提示后退出码 1 mcli license update ALIAS license.key(离线,仍可用)
mcli support upload 提示后退出码 1 通过自有渠道传递文件
mcli support proxy set 提示后退出码 1 proxy remove 仍可清除遗留配置
mcli support callhome enable 提示后退出码 1 disable / status 照常工作

诊断能力本身全部保留:mcli support diag / perf / profile / inspect 恒定以本地(airgap)模式运行 —— 结果写入本地文件,不上传任何数据,SUBNET 注册也不再是前置条件。两项相关加固:inspect 不再回退到用内置的 MinIO 公钥加密输出(你的归档始终由你自己解密);自 20260804 起 --debug 输出对 SUBNET 凭证脱敏 —— 如果你分享过旧版本的调试日志,请轮换其中的密钥。mcli license infounregister 在本地照常工作。

4. play 演示别名不再预置

全新配置只预置 locals3gcs,不含 play。假定演示别名存在的教程与冒烟脚本需要显式添加:mcli alias set play https://play.min.io <access-key> <secret-key> 即可恢复旧行为 —— 主动连接任何 S3 端点都不受限制。已有配置文件永远不会被修改。

5. 输出文案携带 Silo 身份

mcli --version 保留机器可读的首行,新增身份行与双版权行;--help 显示 “Silo client”,示例使用 mysilo。命令语法分毫未动 —— 只有 grep 上游身份字样(如 “MinIO Client”)的脚本需要调整。

6. 面向开发者

模块路径保持 github.com/minio/mc,现有 import 无需修改即可编译 —— 但 go install github.com/minio/mc@latest 安装的是 已归档的上游版本,不是本分支。请从源码构建(git clone https://github.com/pgsty/mc && cd mc && make)或通过 replace 指令引用。贡献无需 CLA,但每个提交必须携带 DCO 签署(git commit -s)。上游已归档也意味着继承的缺陷只会在本分支修复 —— 最需要注意的是 minio/mc#5139(版本化桶上的 mirror --remove --watch)。

迁移指南

从官方 mc 二进制迁移到 mcli

  1. 安装 mcli,任选一条分支渠道(校验方式见 §1):GitHub Releases 归档包、Pigsty 软件仓库的 yum install mcli / apt install mcli,或 docker pull pgsty/mc
  2. 决定用什么名字调用它 —— 这决定它读取哪份配置:
    • 保留 mc 名称(摩擦最小):确认系统中已无上游二进制(command -v mc)后,以 mc 名安装 —— 例如 ln -s /usr/local/bin/mcli /usr/local/bin/mc。以 mc 调用时它原样读取你现有的 ~/.mc,无需迁移任何东西。
    • 改用 mcli 名称:用 cp -a ~/.mc ~/.mcli 一次性搬运状态,或设置 MC_CONFIG_DIR=~/.mc。两个客户端也可以并存,各用各的目录。
  3. 清理自动化脚本
    • 移除 mc update 调用 —— 现在恒定退出码 1
    • 移除 license registersupport uploadsupport callhome enablesupport proxy set —— 同样是稳定失败;
    • support diag / perf / profile / inspect 照常工作并写本地文件;删除任何期待「上传到 SUBNET」的后续步骤;
    • 检查所有解析 --version 首行之外内容的逻辑。
  4. 复查 play 依赖(见 §4)。
  5. 验证mcli --versionmcli alias ls,然后对你的服务器执行 mcli ls <alias>mcli ping <alias>
  6. 回退 始终是平凡操作:配置格式双向一致,保留旧的 mc 二进制即可随时切回。

参考阅读

4 - 原生软件包迁移

silo RPM/DEB 软件包相对 minio 软件包的变化:文件布局、服务账号、接管语义与注意事项。

Silo 为 amd64/arm64 发布 RPM、DEB 与 APK 软件包,托管于 GitHub Releases,附 SHA-256 校验和与构建溯源 attestation。本页记录相对 minio 软件包安装的变化:文件布局、服务账号与注意事项。迁移的总体范围见迁移指南

文件布局

MinIO 安装 Silo 软件包
/usr/bin/minio /usr/bin/silo(同时提供 silo healthcheck
minio.service /usr/lib/systemd/system/silo.service
/etc/default/minio 仍然优先读取;/etc/default/silo 按变量覆盖(noreplace/conffile,升级不覆盖)
OS User minio-user silo:silo,声明于 /usr/lib/sysusers.d/silo.conf,安装时创建
- /usr/share/doc/silo/LICENSENOTICE(AGPL-3.0-or-later)

Silo 提供的 RPM DEB 包可以与 minio 包并存安装,且不会覆盖原有文件。DEB 包不提供安装自动启动。

服务账号

unit 默认 User=silo,而现有数据、TLS 私钥与 KMS 凭据属于原 MinIO 用户。不要 chown 数据,用 drop-in 让 Silo 以现属主运行:

ls -ld /path/to/your/data              # 记录属主,例如 minio-user
sudo mkdir -p /etc/systemd/system/silo.service.d
sudo tee /etc/systemd/system/silo.service.d/10-legacy-user.conf <<'EOF'
[Service]
User=minio-user
Group=minio-user
EOF
sudo systemctl daemon-reload

这同时保证 TLS 可用:Silo 从运行用户的家目录解析证书(~/.silo/certs,仅存在旧 ~/.minio/certs 时回退使用),现有 public.crt/private.key/CAs/ 无需拷贝即被找到。缺少 drop-in 时,TLS 部署启动失败:

FATAL Unable to start the server: HTTPS specified in endpoints,
      but no TLS certificate is found on the local machine

改用 silo 账号是之后的可选变更:把证书移到 silo 可读路径、在 MINIO_OPTS 中设置 --certs-dir,并在迁移窗口之外转移数据属主。

接管与回滚

这是一个接管型 unit:

[Unit]
After=network-online.target minio.service
Conflicts=minio.service

[Service]
Type=notify
EnvironmentFile=-/etc/default/minio
EnvironmentFile=-/etc/default/silo
ExecStart=/usr/bin/silo server $MINIO_OPTS $MINIO_VOLUMES
Restart=always
  • Conflicts=minio.service:systemd 不允许两者同时运行,起一个即停另一个,双向实现接管与回滚。
  • EnvironmentFile 链使 /etc/default/minio 中的 MINIO_VOLUMESMINIO_OPTS、凭据与 KMS 配置原样生效。
  • Type=notifysystemctl start 仅在服务端真正就绪后返回成功。

切换:

sudo systemctl disable --now minio.service
sudo systemctl enable  --now silo.service
silo healthcheck --url https://127.0.0.1:9000 ready    # 未启用 TLS 用 http://
mc admin info <现有别名>

回滚(无需还原任何东西——数据属主、证书与旧 unit 均未被触碰):

sudo systemctl disable --now silo.service
sudo systemctl enable  --now minio.service

注意事项

  • 集群所有节点一起切换。 任意两个不同的二进制都无法组成集群——MinIO 与 Silo 之间如此,两个不同版本的 Silo 之间亦然;混跑节点无限停在 activating细节)。先在所有节点完成准备(装包、建 drop-in),再快速连续翻转所有节点:systemctl disable --now minio && systemctl enable --now --no-block silo。回滚与将来的升级同理,所有节点一起。
  • 非软件包安装同样适用。/usr/local/bin/minio 加自定义 unit 的部署以相同方式被接管,只要其配置位于 /etc/default/minio
  • 崩溃循环有频率限制。 配置错误(如缺证书)时 Restart=always 反复重启,直至触发 systemd 启动限制(Start request repeated too quickly)。修复根因后执行 systemctl reset-failed silo && systemctl start silo
  • 保留回滚窗口。 验证完成前保留 minio 软件包、unit 与二进制;已禁用的 unit 没有开销,之后可按需移除旧包。
  • 迁移后的滚动重启:每次重启前用 silo healthcheck --maintenance cluster 把关;退出码 0 表示停掉本节点仍保有写 quorum,HTTP 412 表示不能停。

5 - 特性设计

Silo 在上游 MinIO 之外新增能力的设计笔记——先落笔,再落地。

本节的组件页面记录 Silo 与 MinIO 的一致之处;这个子节则记录 Silo 有意超越上游的地方:每一页都是一份设计笔记,在实现落地之前写就,落地之后继续作为"当时决定了什么、为什么这么决定"的权威记录保留。

这里的文档描述的是设计意图,不代表已发布的行为——每页开头的状态行会注明当前处于哪个阶段。

5.1 - 原生健康检查与 Distroless 镜像

设计笔记:silo 二进制为何新增 healthcheck 子命令、mc ready 为何必须退役、单二进制 Distroless 镜像如何规划。

状态:P1(子命令,2ff594f4b)与 P2(distroless 镜像 + CI 门禁,4c34d2309)已在 pgsty/silo 落地;P3(Helm 探针)与 P4(文档)待办 · 决策日期:2026-08-06 · 归属pgsty/silo(命令、镜像、Helm chart)与本站(文档)

Silo 将新增一个原生的 silo healthcheck 子命令,并在现有容器镜像之外发布一个新的 Distroless 镜像变体——里面真正要紧的文件只有一个:silo 二进制。本文在动手实现之前把推理过程和设计决策记录下来,让代码有一份可以对照检验的规格,也让"为什么要做成这样"永远有出处可查。

背景

今天的发布镜像(docker.io/pgsty/silo)基于 ubi-micro,装了四样活动部件:silo 服务端、mcli 客户端(带 mc 别名)、一个静态链接的 curl,以及一个 POSIX shell 入口脚本。Compose 示例用镜像内置的客户端做容器健康检查:

healthcheck:
  test: ["CMD", "mc", "ready", "local"]

这套安排继承自上游 MinIO,其脆弱性有案可查:mc 曾短暂从镜像中消失,用户的健康检查随之失效,“除了禁用别无选择”(#9)。上游自己的历史也如出一辙——2023 年 MinIO 切换到 ubi-micro 丢掉 curl 时,维护者的答复是更加依赖 mc ready local(minio/minio#18373、#18389);而上游 minio/minio 如今已经归档,其二进制自始至终只有 server 一个子命令。上游不会有人来修这件事了。

Distroless 镜像把这个问题逼到了台面上。没有 shell、没有 curl、没有 mc——这正是 Distroless 的定义。容器里唯一保证存在的程序就是服务端二进制本身。如果这个镜像还想拥有 Docker 层面的健康检查,就只能由这个二进制自己来提供。

mc ready 为何必须从探针岗位上退役

细读 mc 的实现(cmd/ready-main.go)会发现:现在的健康检查是"碰巧能用",不是设计出来的。四个彼此独立的缺陷:

  1. 它自己永远不报告失败。mc ready 是一个"等到就绪为止"的循环:每 5 秒重试一次,只在成功时以零退出码结束,连接被拒也不会跳出循环。作为 Docker healthcheck 使用时,“unhealthy” 的判定完全来自 Docker 的 timeout 把进程杀掉——探测语义是 SIGKILL 的副作用。
  2. 它检查的尺度是错的。mc ready 请求的是 /minio/health/cluster——全集群写 quorum。于是每个容器的"健康"反映的都是整个集群的状态,这恰好是 Kubernetes 文档明确警告的级联失败反模式:quorum 一丢,所有节点同时被判不健康。
  3. 它有隐藏故障模式。 它需要可写的 ~/.mc 配置目录(只读 rootfs 或 OpenShift 任意 UID 下,服务器明明健康、探测却先失败了);首次运行会向 stdout 打印配置创建噪音;内置的 local 别名硬编码为 http://localhost:9000——一旦启用 TLS 或改了端口就立即失效,上游用户对此公开抱怨过。
  4. 它是镜像里捆绑第二个二进制的最后一个功能性理由。mcli 和钉版本的静态 curl 都有持续的供应链与维护成本(curl 被钉死在 v8.11.0,因为后续版本砍掉了 aarch64 构建),而这些事一个现有二进制的子命令用约 150 行代码就能做完。

决策

三条轨道,刻意解耦:

# 决策
D1 silo 二进制新增 healthcheck 子命令——服务端现有 /minio/health/* 端点的匿名 HTTP 薄客户端。它随每一个构建发布,所有镜像和裸机安装同时获得这项能力。
D2 现有镜像不动mclicurl、shell 入口脚本、mc ready local 示例全部保留。当前镜像的用户若想用新探针,覆盖自己的 healthcheck.test 即可选择加入——不拿走任何东西,不移动任何默认行为。
D3 并行发布一个新的 Distroless 变体 作为试点:单二进制、无 shell、原生 HEALTHCHECK 内置。试点验证充分后,它将成为推荐默认并完成切换;无论如何,经典镜像都会为兼容性继续保留。

D2 与 D3 回答了那个显而易见的问题——“为什么不直接给主镜像瘦身?"——因为主镜像的内容物本身就是兼容性表面。#9 的存在,正是因为这个表面曾经在用户脚下被抽换过一次。Distroless 镜像用一个新名字承载一份新契约:在它接受检验期间,没有任何人现有的健康检查、docker exec mc 习惯或入口脚本假设会被破坏。

silo healthcheck 命令

silo healthcheck [FLAGS] [CHECK]

CHECK —— 位置参数,与 /minio/health/<path> 一一对应:
  live          进程在提供服务(默认;不触碰任何外部系统)
  ready         live + KMS 与 etcd 可达(若有配置)
  cluster       全集群写 quorum
  cluster-read  全集群读 quorum

FLAGS:
  --address value   探测目标 host:port(EnvVar: MINIO_ADDRESS;默认 ":9000",
                    空 host 补全为 127.0.0.1)
  --url value       完整基址覆盖(http[s]://host:port);优先于 --address
                    与 TLS 自动判定(EnvVar: MINIO_HEALTHCHECK_URL)
  --maintenance     仅 cluster 有效:附加 ?maintenance=true——问"现在把这个
                    节点下线安全吗?"(HTTP 412 = 不安全,会破坏高可用)
  --timeout value   总超时;默认:live/ready 5s,cluster* 15s
  另继承全局旗标:--certs-dir、--config-dir、--json、--quiet

退出码:  0 = 健康 / 可安全操作 · 1 = 其余一切
输出:    单行,例如
  live: ok (200, 2ms)
  cluster: unhealthy (503) server-status=iam-offline write-quorum=3 healing-drives=2

这个形态背后的设计原则:

  1. 薄客户端,单一事实源。 命令永远只是规范健康 API 的 HTTP 客户端,绝不在进程内重新实现任何检查——“健康"的语义只存在于一个地方:服务端处理器。
  2. CLI 词汇 = API 词汇。 检查名就是端点路径。没有新概念要学,没有第二套词汇要同步。
  3. 共享服务器自己的配置。 端口来自服务器同一份 --address/MINIO_ADDRESS 契约;http 还是 https,由服务器启动时自己执行的那个证书检查(certs 目录下的 public.crt + private.key)来决定。这是 Traefik healthcheck 的模式——最接近的业界先例,它从与服务端相同的静态配置里解析 ping 端点——并把 Traefik 留成 // TODO 的 TLS 处理真正实现掉。这也是对 mc ready“端口靠猜"缺陷的正面修复。
  4. 默认检查是节点本地的。live 回答"这个进程是否在服务”,而这是单容器健康状态唯一应该回答的问题。集群尺度的检查存在,但只放在显式参数之后,并沿用 mc ready--cluster-read/--maintenance 词汇,让运维语言得以延续。
  5. 退出码只有 0 和 1。 Dockerfile 参考手册明文保留退出码 2(vault status 用 2 表示 sealed,是现成的反面教材)。丰富的诊断信息放进那一行输出里——Docker 会把探测输出的前 4096 字节存进 docker inspect,而命令会把服务端的诊断响应头(x-minio-server-statusx-minio-write-quorumx-minio-healing-drives)解码进去;这些正是裸 curl -f 会丢掉的细节。
  6. 跳过 TLS 证书校验,v1 不提供开关。 这是对匿名端点的环回自探,不传输任何数据——而 kubelet 对 HTTPS httpGet 探针的文档行为恰好也是跳过校验。与之对齐意味着同一套 TLS 部署在 Docker 和 Kubernetes 下得到同一个结论;默认校验只会制造假阴性,因为自签服务器证书极少包含 127.0.0.1 的 SAN。

两条从源码里挖出来的实现约束——它们是承重墙,不是风格偏好:

  • 请求必须严格匿名。 健康路由之所以能豁免保留路径守卫,仅限于被服务器判定为匿名的请求;带上 Authorization 头会改变请求的分类,结果不是得到应答而是被 拒绝ErrAllAccessDisabled)。
  • HTTP 传输层必须设置 Proxy: nil 容器经常继承 HTTP_PROXY 却没有把 127.0.0.1 写进 NO_PROXY;环回探测绝不能被路由进公司代理。(Traefik 的 healthcheck 出于同样的原因特意这样做了。)

还有一个看似随意、实则不然的数字:cluster 检查的默认超时是 15 秒,因为服务端评估集群健康时自身受 10 秒 cluster_deadline 约束——客户端若在 5 秒就放弃,等不到服务器深思熟虑后给出的 503,连同所有诊断头一起丢失。对抗性评审后补充了两个细节:--url 支持环境变量(MINIO_HEALTHCHECK_URL),因为探针进程看不到服务端的命令行——当服务端的地址或 TLS 配置来自 CLI 参数时,这是矫正内置 HEALTHCHECK 的正式途径;另外任何外层(Docker)超时都必须大于探针自身的截止时间,否则探针会在打印诊断行之前先被 SIGKILL。

这些端点到底在做什么

下表对照的是处理器源码,不是文档转述——并且修正了一个常见的误读:

端点 返回 200 的条件 失败形态 备注
/minio/health/live 几乎总是——对象层尚未初始化时也返回 200(该状态只通过 x-minio-server-status: offline 响应头传递) 请求队列饱和时 503 不触碰外部系统;唯一安静到适合高频探测的端点
/minio/health/ready live外加 KMS 能生成密钥、etcd 能应答读取——各自仅在配置了的情况下 KMS/etcd 故障;队列饱和 没有 KMS 与 etcd 时,readylive 是同一条代码路径
/minio/health/cluster 对象层、桶元数据、IAM 均已初始化,且每一个纠删集 都有写 quorum 503 附带 quorum 诊断头;带 ?maintenance=true 时失败为 412 每次评估失败都会在服务端写一条日志——不要高频轮询它
/minio/health/cluster/read 上一行的读 quorum 版本 同上

值得点破的推论:liveready存活 级别的信号——它们不能告诉你节点能否服务对象,只有 cluster 这一对能。这正是 cluster 端点必须远离单容器探针的原因(尺度错位、日志噪音、级联重启),也正是它适合回答运维问题的原因——“我现在可以把这个节点下线吗?"(--maintenance:200 表示安全,412 表示会失去高可用)。

Distroless 变体

基底gcr.io/distroless/static-debian12——够用,因为 siloCGO_ENABLED=0 构建。这个基底恰好带着服务器真正需要 rootfs 提供的四样东西:CA 证书(KMS/webhook/STS 出站 TLS)、tzdata、/tmp,以及带 root/nonroot 条目的 /etc/passwd。没有 shell、没有包管理器、没有 libc。

契约草图:

FROM gcr.io/distroless/static-debian12:latest
COPY silo /usr/bin/silo
COPY LICENSE NOTICE CREDITS /licenses/
ENV HOME=/tmp
# /data 在镜像层内创建、全员可写——见 issue #55:
# 这里已经没有入口脚本可以在运行时修补属主了。
VOLUME ["/data"]
EXPOSE 9000
HEALTHCHECK --interval=30s --timeout=10s --start-period=2m --start-interval=2s --retries=3 \
  CMD ["/usr/bin/silo", "healthcheck", "ready"]
ENTRYPOINT ["/usr/bin/silo"]

草图里折叠的决策:

  • ENTRYPOINT 就是二进制本身。docker run pgsty/silo:distroless server /data——没有 argv 翻译脚本,因为没有 shell 来跑脚本。经典镜像的 MINIO_USERNAME/MINIO_GROUPNAME 降权路径(依赖 GNU chroot 和可写的 /etc/passwd)在此变体中 不受支持;受支持的机制是 --user / Kubernetes runAsUser
  • /data 在镜像层内创建、mode 0777,试点期默认用户保持 root。 Issue #55 证明了:只声明 VOLUME ["/data"] 而不创建它,会让 所有 非 root 运行方式失败,而且事后没有任何入口脚本能修补——Distroless 里更是压根没有入口脚本。在层内以全员可写方式创建它,是唯一让全部权限模式(包括 --user)都能工作的选项,同时与经典镜像保持即插即用的对等;其暴露面被"镜像只运行一个进程"这一事实所限定。nonroot 默认(uid 65532)的姿态经过考虑后推迟:它会在 UID 不匹配时破坏文档记载的 bind-mount 工作流,而试点的任务是测量摩擦,不是制造摩擦。晋升为默认推荐时再议,可能以 -nonroot 标签的形式出现。
  • 健康检查内置、exec 形式。 Shell 形式的 HEALTHCHECK 字符串需要 /bin/sh,在这里不可能存在;JSON 数组形式是唯一选择。Compose 会自动继承镜像的 HEALTHCHECK(逃生门是 disable: true),所以这个变体的 compose 用户零配置就能得到可用的 depends_on: condition: service_healthy。选 ready 而不是 live,是因为 Docker 健康状态的主要用途是 门控(启动顺序),那是就绪语义——况且在没有 KMS/etcd 时两者本就相同。
  • 一项发布前必须完成的验证HEALTHCHECK 是 Docker 扩展,不在 OCI 镜像规范里(opencontainers/image-spec#749 至今开放),OCI 媒体类型的构建会静默丢弃它。发布流水线必须断言推送后的 manifest 上 docker inspect 能看到 Health 配置,否则就调整构建的媒体类型直到能看到为止。
  • 命名docker.io/pgsty/silo:<RELEASE>-distroless,外加滚动的 distroless 标签。服务端仓库新增 Dockerfile.distroless——它没有任何下载阶段,完全离线,因此可以在每次发布的 CI 里真实构建并断言,顺带堵上 #55 记录的那个"门禁测的是合成镜像而非发布镜像"的覆盖缺口。
  • 变体文档里要诚实写明用户失去了什么:没有 docker exec <container> sh 式调试(改用 docker debug / kubectl debug 临时容器);镜像内没有 mc(改用 pgsty/mc 镜像或宿主机安装的 mcli);没有 MINIO_USERNAME 路径(改用 --user)。

Kubernetes 完全不需要镜像配合

值得明说,因为它划定了问题的边界:Kubernetes 完全忽略 Dockerfile 的 HEALTHCHECK——kubelet 探针在 Pod spec 里配置,从容器外部以 httpGet 请求执行。因此两个镜像变体在 Kubernetes 下的探测方式完全相同:

startupProbe:            # 启动预算:5s × 60 = 5 分钟,护住大规模 IAM 加载
  httpGet: { path: /minio/health/live, port: 9000 }
  periodSeconds: 5
  failureThreshold: 60
livenessProbe:           # 何时重启:只看进程级信号
  httpGet: { path: /minio/health/live, port: 9000 }
  periodSeconds: 30
  timeoutSeconds: 5
  failureThreshold: 3
readinessProbe:          # 何时摘除流量:可以包含硬依赖(KMS/etcd)
  httpGet: { path: /minio/health/ready, port: 9000 }
  periodSeconds: 15
  timeoutSeconds: 5
  failureThreshold: 3

任何这类配置旁边都应放上三条警示:cluster 端点永远不进探针(放进 liveness 意味着 quorum 一丢整个集群同时重启;放进 readiness 会与分布式引导互相纠缠——chart 的 headless service 设置 publishNotReadyAddresses: true 正是为此);live 在请求队列持续饱和时会有意返回 503,所以饱和节点约 90 秒后被重启是设计使然;scheme: HTTPS 下 kubelet 跳过证书校验,自签部署无需任何额外配置。

Silo 的 Helm chart 目前 一个探针都没有(上游的 chart 也一样,尽管其文档写了探针示例)。把上面三个探针补进 chart 是计划中的独立后续项——它不依赖任何一条镜像轨道,而且相对上游这是差异化优势,不是兼容性风险。

落地路线

阶段 范围 仓库
P1 silo healthcheck 子命令 + 测试;随下一个发布版二进制交付(所有镜像同时继承该能力,镜像行为零变化) pgsty/silo
P2 Dockerfile.distroless + CI 构建与健康门禁 + 以试点身份发布 -distroless 标签 pgsty/silo
P3 Helm chart:补三探针;刷新过期的默认镜像标签 pgsty/silo
P4 文档:命令参考、探针指南、Distroless 迁移说明;试点反馈 → 决定是否将 Distroless 晋升为推荐默认 本站

贯穿所有阶段的兼容性承诺:经典镜像的内容物与示例不变;mc ready local 在今天能用的地方继续能用;健康 HTTP API 不动(子命令纯属增量);/minio/health/* 路径与本分支的其他所有 /minio/* 路由一样,作为兼容性表面继续冻结。

推迟的决定

记录在案,免得将来从零重新争论:

  • --wait 模式(阻塞等待直至健康——mc ready 那个循环唯一真正的正当用途):推迟——仓库内暂无消费者,日后添加完全向后兼容,旗标命名空间已预留。
  • Distroless 镜像默认 nonroot:推迟至晋升默认推荐时再议,理由见上文。
  • --json 的输出 schema:遵循全局旗标惯例;确切 schema 在实现时定稿并写入命令参考。