statefulset 更适合 mongodb 副本集,因其提供有序部署、固定 pod 名称(mongo-0/mongo-1)、稳定 dns(需 headless service 配合)、独立 pvc 挂载及奇数副本防脑裂;初始化需手动执行 rs.initiate(),且必须使用完整 fqdn。

StatefulSet 为什么比 Deployment 更适合 MongoDB 副本集
MongoDB 副本集要求每个节点有稳定、可预测的网络标识(如 mongo-0.mongo-svc.default.svc.cluster.local)和持久化存储,而 Deployment 创建的 Pod 是无状态、可随意替换且 DNS 名称不固定的。StatefulSet 天然提供:有序部署/扩缩容、固定 Pod 名称(mongo-0、mongo-1)、稳定 DNS 记录(配合 Headless Service),以及每个 Pod 独立挂载的 PersistentVolumeClaim 模板。
关键点:
- 必须用
serviceName字段关联一个 Headless Service(即clusterIP: None),否则 Pod 不会获得可解析的 DNS 名称 -
volumeClaimTemplates中定义的 PVC 名称不能动态拼接,K8s 会自动为每个 Pod 生成mongo-data-mongo-0这类名字,模板里只写基础名(如mongo-data) - 副本数(
replicas)建议设为奇数(3 或 5),避免选举分裂(split-brain)
Headless Service 必须关闭 clusterIP 并显式设置 serviceName
Headless Service 的作用不是负载均衡,而是让每个 Pod 拥有独立、稳定的 DNS A 记录。如果漏掉 clusterIP: None,K8s 会分配一个 ClusterIP,DNS 就只会返回该 IP(指向随机后端),彻底破坏副本集成员发现机制。
正确写法示例:
apiVersion: v1
kind: Service
metadata:
name: mongo-svc
spec:
clusterIP: None # ← 这一行绝对不能少
selector:
app: mongo
ports:
- port: 27017
targetPort: 27017
常见错误:
- Service 名字(
name)没和 StatefulSet 的serviceName字段保持一致 → Pod 启动后无法解析mongo-0.mongo-svc - Service 的
selector标签和 StatefulSet 的matchLabels不匹配 → Pod 不被纳入 DNS 记录 - 在同一个命名空间下用了多个 Headless Service 指向同一组 Pod → DNS 解析行为不可控,MongoDB 初始化可能连错地址
初始化副本集必须由客户端主动触发,K8s 不会自动执行 rs.initiate()
K8s 只负责拉起容器并挂载存储,MongoDB 进程启动后仍是单节点模式。必须在某个 Pod(通常是 mongo-0)内手动执行 rs.initiate(),然后逐个添加其他成员。跳过这步,所有节点日志里都会反复报 no replset config has been received。
推荐做法(在集群就绪后执行一次):
kubectl exec -it mongo-0 -- mongosh --eval "
rs.initiate({
_id: 'rs0',
members: [
{ _id: 0, host: 'mongo-0.mongo-svc.default.svc.cluster.local:27017' },
{ _id: 1, host: 'mongo-1.mongo-svc.default.svc.cluster.local:27017' },
{ _id: 2, host: 'mongo-2.mongo-svc.default.svc.cluster.local:27017' }
]
})"
注意:
- 主机名必须用完整 FQDN(含命名空间和 svc.cluster.local),不能用
mongo-0:27017—— 容器内默认 search 域可能不包含当前 ns - 执行前确认所有 Pod 的
READY状态为1/1且STATUS为Running;任一 Pod 处于Pending或CrashLoopBackOff都会导致初始化失败 - 后续扩缩容时,新增节点需用
rs.add()加入,删节点要用rs.remove(),不能直接删 Pod
Pod 启动时连接其他成员失败?检查 readinessProbe 和启动顺序
MongoDB 副本集成员启动时会尝试连接配置中的其他节点。如果 mongo-0 先起来、但 mongo-1 和 mongo-2 还没就绪,它可能因连接超时反复崩溃重启(CrashLoopBackOff)。这不是配置错误,而是启动竞争问题。
缓解方式:
- 给容器加
readinessProbe,用mongosh --eval "db.runCommand({ping:1})"检查服务是否真正可响应,而不是只看端口通不通 - 避免在
initContainer中做耗时操作(比如下载大文件),否则会阻塞主容器启动 - 首次部署建议先设
replicas: 1,等mongo-0初始化完副本集再扩到 3;生产环境可用podManagementPolicy: OrderedReady强制串行启动(但会拖慢恢复速度)
最常被忽略的一点:mongosh 在镜像中路径可能不是 /usr/bin/mongosh(尤其旧版 mongo镜像用 mongo),务必进容器 which mongosh 确认路径,否则 readinessProbe 会永远失败。











