Failed to refresh metadata 根本原因是 config server 副本集(CSRS)不可用或元数据未就绪:CSRS 无 PRIMARY、选举未完成、config.version 为 0,或新 shard 的 --configdb 配置错误、InternalAuth 不匹配、hostname 冲突导致注册失败。

sh.addShard() 报 Failed to refresh metadata 是什么情况
根本不是网络不通或权限不足,而是 config server 副本集(CSRS)本身不可用或元数据未就绪。mongos 在执行 sh.addShard() 时,会先向 config server 发起元数据刷新请求,若 config server 返回异常、选举未完成、或 config.version 仍为 0,就会直接失败并报这个错误。
常见表现:
- 执行
sh.status()报错或返回空,db.runCommand({listShards: 1})返回"ok": 0 - 任意 config server Pod 中运行
rs.status(),发现没有PRIMARY,或多数节点health: 0 - config server 日志里反复出现
not master and slaveOk=false或waiting for primary
必须先恢复 CSRS:确认所有 config server 成员能互通、keyFile 一致、TLS 配置有效,并等待一次成功选举产生 PRIMARY。否则任何 sh.addShard() 都会卡在元数据校验环节。
新 Shard 连不上 config server 导致校验中断
mongos 不是直接把地址写进 config,而是让新 shard 主动反向连接 config server 并注册自己。如果新 shard 启动参数里的 --configdb 指向错误,它压根不会出现在 config 的 shards 表中,sh.addShard() 就会超时或静默失败。
检查要点:
- 新 shard 的启动命令或配置文件中,
--configdb必须是<csrs-replset-name>/<host1>:<port>,<host2>:<port></port></host2></port></host1></csrs-replset-name>格式,不能只写一个 IP - Kubernetes 环境下,
--configdb中的 host 必须能被新 shard Pod DNS 解析——比如用config-svc.default.svc.cluster.local:27019,而非硬编码的 ClusterIP - 若启用了 TLS,新 shard 必须携带有效的
--sslCAFile和--sslPEMKeyFile,且证书 subjectAltName 包含 config server 的 DNS 名 - 新 shard 的
storage.dbPath所在磁盘剩余空间不能低于 10GB,否则初始化阶段可能因无法写入 local.oplog.rs 而卡住
InternalAuth 不匹配让 shard 被 config server 拒绝
即使新 shard 能连上 config server,如果 internalAuth(即 keyFile)配置和 config server 不一致,CSRS 会拒绝其注册请求,日志里通常只显示模糊的 auth failed 或直接断连,不会明说“keyFile 不对”。
验证方式很直接:
- 在新 shard 上运行
db.runCommand({getCmdLineOpts: 1}),确认parsed.security.keyFile存在且路径可读 - 在任一 config server 上执行同样命令,对比两个
keyFile文件的sha256sum是否完全一致 - 检查文件权限:
chmod 400是硬性要求,组/其他用户可读会导致 mongod 启动失败或降级为无认证模式 - 注意:config server 和 shard 必须共用同一份 keyFile 内容,mongos 的 keyFile 只影响 mongos→shard 通信,不影响 shard→config 注册
新 Shard 的 hostname 变了但 config 里还存着旧地址
CSRS 会把每个 shard 的注册信息持久化到 config.shards 集合中,字段是 host。如果上次加过同名 replica set(比如 rs1),后来删掉重装,Pod IP 或 hostname 变了,而 config 里还记着老地址,新 shard 就会被当成“冲突成员”拒绝加入。
快速确认方法:
- 连上 config server,执行
db.shards.findOne({ _id: "rs1" })(把rs1换成你的 replica set 名) - 看返回的
host字段是否指向当前新 shard 的实际地址;如果不是,手动db.shards.updateOne(...)改掉,或先sh.removeShard("rs1")清理再重试 - 特别注意 Kubernetes StatefulSet 场景:如果用
hostname作为host值,但 Service 没配 headless,DNS 解析可能失败;建议统一用serviceName.namespace.svc.cluster.local格式
这个问题最容易被忽略:所有日志都显示“连接成功”,rs.status() 正常,sh.status() 却始终不出现新分片——因为卡在 config server 的地址白名单校验环节,而不是网络或认证层。











