一个四节点集群,每台机器上的配置文件看着一模一样,systemctl start rustfs之后有的节点起来了,有的在重启循环。查防火墙、查主机名解析、查时钟,都正常。问题往往在一条没写进配置文件的变量上:RUSTFS_RPC_SECRET。它默认值是derived,意思是"没设就自己派生一个",而派生出来的值是各节点各算各的。单节点部署看不出这个问题,多节点部署遇到它就很难查。
这条密钥只管节点之间,不认客户端
RUSTFS_RPC_SECRET的作用是给节点间 RPC 做认证。它的用途和根凭据那对 access/secret 完全不同:客户端用根凭据签名请求进 S3 接口,节点之间互相通信走的是另一条通道,这条通道有自己的鉴权。
这条通道跑在哪个端口上,决定了排查方向。RUSTFS_ADDRESS默认:9000,官方文档写明 S3 API 监听器同时也承载内部节点 RPC。节点间连通性那节的补充更明确:多节点集群里每个节点都必须能在 S3 端口上访问到其他每一个节点,没有独立的集群端口。也就是说,防火墙上拒绝 9000 的后果不只是外部客户端连不上,内部节点也会互相看不见。
这条事实把排查范围收得很窄。遇到节点起不来,第一件事是确认 9000 在这批机器上是对外可达的,而不是先去查数据盘挂载。
派生机制在单节点上完全看不出问题
默认值derived的行为是:变量未设置时,RustFS 从当前生效的那对 access/secret 密钥派生出 RPC 密钥。派生看的是最后生效的那两个值,不关心它们是从环境变量来的还是从文件里来的。单节点部署只有一个节点,不存在"两边算出来的值不一致"这回事,所以这套默认行为从头到尾看不出异常。
多节点才暴露出来。每个节点独立启动,各自从自己看到的凭据派生自己的值,四台机器得到四个不同的密钥。已经就绪的节点会认为向它同步的节点身份不对,请求被拒,表现出来通常是节点起不来或者反复重启。
日志这一侧的线索很有限。节点间调用被拒时返回的是403 AccessDenied: Invalid signature,日志里是一行签名校验失败的报错。它不会直接说"这两台机器的 RPC 密钥不一样",这个结论得靠人补上去。看到 Invalid signature 之后应该去比对各节点的配置,而不是继续在签名算法上找原因——同一条报错背后可能压着好几件事,密钥不一致只是其中一件。
全默认凭据下,多节点必须显式设置
文档对这条变量写了一句限定条件:当凭据组合为全默认时,多节点集群必须显式设置它。
要理解这句话,得先看凭据的默认值。RUSTFS_ACCESS_KEY和RUSTFS_SECRET_KEY的默认值都是rustfsadmin。官方文档写明这两个内置默认凭据只为首次启动方便,任何非临时部署都要设成非默认值。CLI 参考那节也重复了同样的行为:如果既没提供--access-key/--secret-key也没提供对应的文件变体,服务器会回退到内置默认凭据并打一条警告。
派生规则在这里反而添了乱。要让几个节点算出同一个密钥,前提很苛刻:所有节点必须同时完全走内置默认值,谁都没有通过环境变量或者*_FILE改过 access/secret。只要有一方配了非默认的根凭据,它派生出的值就和其余节点不同,故障表现随即变成一个节点掉队。官方文档把这条限定条件明确写出来,原因在这里:全默认凭据的多节点集群必须显式设置RUSTFS_RPC_SECRET。
反过来说,一旦显式设置了,凭据怎么改都不会牵动它。显式值挡在前面,derived那条路径就不参与决策。
修法很直接,显式设一个所有节点相同的值:
RUSTFS_ACCESS_KEY=<your-access-key> RUSTFS_SECRET_KEY=<your-secret-key> RUSTFS_RPC_SECRET=<a-shared-value>生产部署上更省事的做法是干脆把RUSTFS_RPC_SECRET写死在同一个模板文件里跟着配置一起分发,这样"派生"这条路径就永远不会进入生产。派生听起来省事,代价是它的结果不可见、也不可比对。
用文件挂载替代明文
明文写在/etc/default/rustfs里的做法在合规检查里过不去,RustFS 为此提供了文件变体:RUSTFS_ACCESS_KEY_FILE和RUSTFS_SECRET_KEY_FILE,各自指向一个存放凭据的文件,文档给的例子是 Docker 或 Kubernetes 的 secret 挂载路径。
这两个变量和对应的环境变量不能同时设置。文档对同设的措辞是一条硬错误,容器入口点会直接退出,不存在"以文件为准"这种优先级。
换一种注入方式不会改动派生算法本身,派生只认最后生效的那两个值,环境变量和文件在这里是等价的。真正让节点走散的是值本身不一样:文件只取第一行,两端的空白和 CR 会被去掉,空文件或读不到是硬错误退出。这几条规则都会让某个节点拿到的值和别人不同,这种不同比写在环境变量里更隐蔽。用文件挂载的时候,节点间 RPC 密钥仍然建议显式设置,因为文件解决的是凭据的存放形式,不解决派生值的一致性。
顺带说一个容易踩的点:文件里写的仍然是rustfsadmin时,触发的告警和环境变量直接写默认值的告警是同一条。换一种存放形式不改变默认凭据的危险性。
还有两个旧名字仍在映射表里:RUSTFS_ROOT_USER、RUSTFS_ROOT_PASSWORD,它们分别对应RUSTFS_ACCESS_KEY和RUSTFS_SECRET_KEY。旧写法仍然可用但会打一条弃用警告,规范名字优先级更高,两个都写在一起时生效的是规范名字。
换根凭据之前先把它写死
生产环境里换根 AK/SK 是迟早的事,这一动会连带 RPC 密钥,因为节点间认证的计算材料里就包含那对根凭据。
几个节点同时把根凭据换成新值,各自算出来的是同一个新密钥,集群照常跑。出事的是换的过程:先重启的那台用新值,还没轮到的还在跑旧值,同一个请求在两台机器上算出不同的签名,鉴权失败随之而来。RustFS 目前没有文档化的在线轮转接口,官方给的动作是逐台重启、等每台在 9000 上GET /health/ready返回 200 再动下一台。而派生还在生效的场景下,逐台重启恰好就是集群分裂的时刻。
官方文档在轮转步骤里把这条排在第一位:轮转之前先把RUSTFS_RPC_SECRET设好,同一值覆盖所有节点。这一步的作用是把节点间认证从根凭据上摘下来,之后各节点可以带着新旧不同的根凭据安全地逐个重启,RPC 密钥不再跟着变。
照这个顺序做,生产多节点就少一个麻烦。要么从一开始就写死在生产配置里,等哪天要换根凭据,不用回头补这一步;要么在计划换根凭据的窗口之前,先单独做一次配置变更把这条变量补上。后者多一轮滚动重启,好处是把派生的不确定性提前关掉。
K8s 里的同一条规则
容器和 K8s 上这个问题的形状不太一样,结论一样:不要依赖派生。
Operator 侧有专门字段。Tenant 的 spec 里可以写rpcSecret,指向一个 Secret 以及用它的哪个 key,Operator 会把这个 key 里的值映射成每个 Pod 的RUSTFS_RPC_SECRET,并在下发前校验 key 存在、值非空且不含 NUL 字节。字段留空时 Operator 不设置这条变量,Pod 自己去找根凭据派生,Operator 也不会为这个无人管理的值上报RpcAuthReady。也就是说默认配置下,K8s 集群和前面那四台裸机处于同一状态。
Kubernetes 多出来的变量是时序。Secret 更新之后已经跑着的 Pod 环境不变,只有重启过的 Pod 才拿到新值,而多个 Pod 的重启顺序不由人控制。于是"一部分 Pod 用旧值、一部分用新值"这段状态会在集群里存在一段时间,派生模式下这段时间里的节点间调用就是失败的。
比较省事的做法是把rpcSecret显式填上,让 Operator 从同一个 Secret 给所有 Pod 注入同一个值。换根凭据时保持这个值不动,官方对这条的要求是轮转管理员凭据期间让rpcSecret保持稳定,理由和上一节是同一件事。
把一致性做进部署流程
手工在四台机器上写/etc/default/rustfs,哪怕用的是同一段命令,也难保证逐字一致。一个字符的差别,比如行尾多了个空格,或者在某台机器上把引号写成了全角,结果就是那个节点的派生值和其他三个不同。
比较省事的做法是让配置文件只有一个来源。用配置管理软件时,把环境变量表放进一份模板、按节点渲染,天然保证一致;用裸机手动部署时,写完第一台之后直接 scp 到其余节点,比逐台手敲可靠得多。这一条对RUSTFS_RPC_SECRET之外的变量同样成立,任何"每台机器配置稍有不同"的部署,事后排查都会更贵。
还有一个相关但独立的点值得一起处理:RUSTFS_ADDRESS默认值是:9000,它绑定的是所有网卡上的 9000。如果机器上不止一个 IP,或者有多张网卡,绑定地址的写法会影响节点之间能不能互相访问。多网卡机器上的部署,把绑定地址显式写出来比依赖默认值少一层意外。
排查时可按这个顺序走
| 检查顺序 | 检查项 | 失败表现 |
|---|---|---|
| 1 | 凭据是否非默认 | 日志出现使用内置默认凭据的警告 |
| 2 | RUSTFS_RPC_SECRET各节点是否相同 | 节点反复重启,日志出现节点间鉴权失败 |
| 3 | 9000 端口是否可达 | 节点间连通性检查失败 |
| 4 | 环境变量文件是否逐字一致 | 配置看起来对但行为不同 |
这两项常被调换顺序,顺序本身是有理由的:凭据错了会在日志里留下明确的警告,属于一眼能看到的问题;RPC 密钥不一致的表现更隐蔽,需要多节点对比才能看出来。先排除前者能省掉不少时间。
第三项要区分"本机可达"和"本机到远端可达"。常见的失败是本机curl localhost:9000通,但从 A 节点访问 B 节点不通,原因通常是防火墙只放行了本地回环或者路由表没配。逐节点互ping 和逐节点 curl 一遍能快速区分这两种情况。
第四项的原因是环境变量文件常有局部修改历史。某一台机器上为了临时验证加过一行、后来忘了删,文件看起来和其他节点一样,实际多了一个生效的变量。用diff逐台比对能立刻看出来。
上面这些排完之后还起不来,就该去看启动了:用journalctl -u rustfs -n 200按时间顺序读一遍,通常第一条错误就是根因。RustFS 的诊断子命令rustfs diagnose --path也能分析日志文件并给出可能的原因,把它用在启动失败的场景上,比自己从头扫日志快。
这两条手段覆盖的是"起不来"。已经起来了但节点之间数据不同步的情况,得看rc admin info cluster rustfs的输出:运行时长能说明哪些节点最近重启过,网络连通性那一项能看出掉队的节点,池成员归属能确认有没有节点被排除在外。三者结合,基本能还原出故障发生时的画面。
把这几条写进部署检查单之后,多节点部署的第一次启动会顺利很多。RustFS 的这套默认值对单机试用是友好的,问题只在于没人意识到它在多节点场景下会换一套行为。
还有一句和凭据管理相关的经验:把RUSTFS_RPC_SECRET与其他凭据一起放进同一个密钥管理服务,配置注入的时机要错开。服务启动时读环境变量,如果配置注入晚于服务启动,进程拿到的是旧值而稍后重启才会读到新值。这类时序问题在多节点上表现为一部分节点正常、一部分反复重启,重启之后又一致了。