
系列专栏《深入剖析 Kubernetes》· 基于张磊极客时间专栏思想整理 · 适合 CSDN 发布06 | StatefulSet有状态应用的拓扑与存储状态在前面几篇里我们反复强调 Deployment 管理的是无状态应用stateless workload任何一个 Pod 都是可替代的删掉 web-0 和删掉 web-1 没有区别扩容缩容只是副本数的加减。但现实里大量业务是有状态的——MySQL 主从、Redis Cluster、Kafka、ZooKeeper、Elasticsearch。它们的每一个实例都记得自己是谁而且通常还记得自己存了什么。Deployment 解决不了这个问题于是 K8s 设计了StatefulSet有状态副本集。本文把 StatefulSet 的核心机制拆成两件事来讲拓扑状态和存储状态。一、StatefulSet 与 Deployment 的根本区别Deployment 的本质是N 个一模一样的 PodK8s 不关心、也无法区分它们。StatefulSet 则强制给每个 Pod 一个固定的、可预测的标识且保证这些标识在 Pod 被重建后依然不变。维度DeploymentStatefulSetPod 名称随机后缀web-7d8f9c-abcde有序固定web-0, web-1…网络标识每次重建都变固定 DNS重建不变存储通常使用共享/空存储每 Pod 独享 PVC随 Pod 迁移扩缩顺序并行严格顺序从大到小/从小到大适用场景无状态服务DB、中间件集群一句话总结Deployment 保证数量StatefulSet 额外保证身份和数据归属。二、稳定的网络标识Headless Service 固定 Pod 名要让 Pod 有稳定身份光靠固定的 Pod 名还不够——Pod 的 IP 重建后必然变化。K8s 的做法是用一个Headless Service无头服务把Pod 名解析成 DNS 记录让访问方通过域名而不是 IP 找到对端。Headless Service 的关键字段是clusterIP: None它告诉 K8s不要给我分配一个 VIP虚拟 IP直接把后端 Pod 的 IP 列表返回给 DNS 查询。web-0 重建后 IP 变了但域名不变 | ----------------------------------- | Headless SVC (clusterIP: None) | | name: web - DNS 解析 | ----------------------------------- | | web-0.web web-1.web .default.svc .default.svc .cluster.local .cluster.local | | -------- -------- | Pod web-0| | Pod web-1| | IP: 10.1 | | IP: 10.2 | --------- ---------访问规则非常规整对于名为web、属于default命名空间的 StatefulSet第 N 个 Pod 的稳定 DNS 为web-N.web.default.svc.cluster.local注意 DNS 记录是稳定的无论 web-0 被调度到哪个节点、IP 怎么变这个域名在 StatefulSet 生命周期内永远指向编号为 0 的那个实例。这就是所谓拓扑状态topological state——实例之间的位置关系被固化下来集群成员可以互相发现彼此。三、稳定的存储volumeClaimTemplates 随 Pod 迁移有状态应用的另一半需求是存储状态storage state每个实例写入的数据必须在这个实例被重建后依然能找回来。StatefulSet 通过volumeClaimTemplatesPVC 模板实现这一点。它在创建 Pod 时自动为每个 Pod 生成一张专属 PVCPVC 名形如data-web-0、data-web-1。这张 PVC 与 Pod 编号严格绑定创建顺序扩容: web-0 - >四、拓扑状态 vs 存储状态把两个概念对比清楚就理解了 StatefulSet 的设计意图拓扑状态Topological State实例之间的身份与位置关系。由Headless Service 有序 Pod 名保证解决我是谁、我的同伴是谁。存储状态Storage State实例持久化的数据归属。由volumeClaimTemplates 不删 PVC 机制保证解决我写过的数据去哪了。StatefulSet 的滚动更新/扩缩容顺序正是为了同时维护这两种状态扩容从web-0开始逐个递增缩容从最大编号开始逐个递减且下一个 Pod 必须等上一个进入 Running/Ready 才启动——这保证了有状态集群先主后从先停从再停主的稳妥顺序。滚动扩容 (scale 2 - 3) web-0 Running -- web-1 Running -- web-2 Pending-Running (先就绪才启下一个) 滚动缩容 (scale 3 - 2) web-2 Terminating (先停最大编号) | web-1, web-0 保持不动,>五、有状态实践MySQL 主从示例下面用最简化的 MySQL 一主一从说明 StatefulSet 用法生产请配齐探针、Secret、资源配置。# 1) Headless Service提供稳定 DNSapiVersion:v1kind:Servicemetadata:name:mysqlspec:clusterIP:None# 关键无头服务不分配 VIPselector:app:mysqlports:-port:3306name:mysql---# 2) StatefulSetapiVersion:apps/v1kind:StatefulSetmetadata:name:mysqlspec:serviceName:mysql# 必须指向上面的 Headless Servicereplicas:2selector:matchLabels:app:mysqltemplate:metadata:labels:app:mysqlspec:containers:-name:mysqlimage:mysql:8.0env:-name:MYSQL_ROOT_PASSWORDvalue:changeitports:-containerPort:3306volumeMounts:-name:datamountPath:/var/lib/mysql# MySQL 数据目录# 3) PVC 模板每个 Pod 一张独享盘volumeClaimTemplates:-metadata:name:dataspec:accessModes:[ReadWriteOnce]# 单节点读写RWOresources:requests:storage:10Gi# 每个实例 10G部署后验证稳定标识与存储# 查看有序 Pod 名kubectl get pods-lappmysql# mysql-0 mysql-1 固定编号# 验证稳定 DNS在集群内某 Pod 中解析nslookupmysql-0.mysql.default.svc.cluster.local# 查看随 Pod 生成的专属 PVCkubectl get pvc#>#># 模拟重建删掉 mysql-0再看 PVC 是否还在kubectl delete pod mysql-0 kubectl get pvc># 依然是 Bound数据不丢参数解释serviceNameStatefulSet 必须关联一个 Headless Service否则固定 DNS 不生效。ReadWriteOnceRWOPVC 同时只能被一个节点以读写方式挂载契合每实例独占盘。volumeClaimTemplates与template同级是 StatefulSet 专属字段Deployment/ReplicaSet 没有。小结 / 核心要点StatefulSet 相比 Deployment额外保证拓扑状态固定身份/网络标识和存储状态数据归属。拓扑状态由Headless ServiceclusterIP: None 有序 Pod 名web-0/web-1稳定 DNS共同实现。存储状态由volumeClaimTemplates实现每个 Pod 一张专属 PVC缩容只删 Pod 不删 PVC重建后数据自动回归。扩缩容严格有序、逐个就绪契合有状态集群先主后从的运维顺序。适用场景数据库、消息队列、分布式存储等每个实例都不可被随意替换的中间件。