运维知识
悠悠
2026年10月4日

10个运维9个踩过的坑:Deployment 和 StatefulSet 到底有什么区别?

凌晨 2 点告警炸锅,线上数据库 Pod 挂了挂挂停停,手忙脚乱地把它挂在 Deployment 下面,以为配置完了就万事大吉。
结果 Pod 重启后不仅数据找不到,集群网络还直接死锁,业务大面积报错。
今天我把 Deployment 与 StatefulSet 的底层区别、生产选型和踩坑避坑讲透,看完你也能彻底告别这个“低级”故障。

说实话,很多人做了两三年运维,每天就是 kubectl apply -f 部署写好的 YAML 模板。
一提到:“做过 K8s 吧?Deployment 和 StatefulSet 有什么区别?”
嘴上能顺溜地背出“一个无状态、一个有状态”,但真到了生产架构设计或者故障排查的时候,照样一踩一个坑。
甚至有人直接把 MySQL 扔到 Deployment 里,跑着跑着数据丢了都不知道找谁背锅。
其实技术没有那么高深莫测,关键是你的技术思维有没有建立起标准 Checklist。

运维的核心不是抢修救火,是“不让火发生”。
Deployment 与 StatefulSet 表面上是两个控制器,本质上是对“Pod 身份与数据生命周期”截然不同的两种处理哲学。

1. Pod 标识符管理:随机 Hash vs 顺序固定

Deployment 创建 Pod 的时候,它只关心总数对不对(ReplicaSet 维持副本数)。
Pod 的名字都是随机生成的哈希后缀(比如 web-app-585489-x9z2p)。
一旦某个 Pod 挂了,Deployment 会瞬间启动一个新的,但名字又变了,网络 IP 也会变,没有任何秩序可言。

StatefulSet 完全不同,它是强顺序的。
Pod 名字直接带序号,比如 web-0、web-1、web-2。
只要 web-0 重启,重建出来的依然叫 web-0。
配合 Headless Service(无头服务),它能提供固定的 DNS 解析(如 web-0.nginx.default.svc.cluster.local),确保每个节点都有唯一且稳定的网络标识符。

2. 存储卷绑定:挂载即走 vs 绑定持久化

Deployment 通常配合动态存储卷使用,或者压根不挂数据卷,存储是临时的。
Pod 挂了重新拉起,旧的临时卷被释放,新 Pod 挂上新的存储,或者干脆空手套白狼。

StatefulSet 则使用了 volumeClaimTemplates。
它会给每个 Pod(比如 web-0)单独绑定并生成一个专属的 Persistent Volume Claim (PVC)。
如果 web-0 被调度到了另一个 Node 节点,它依然能精确挂载之前专属它的那个 PVC 和 PV,数据绝对不会窜乱。

3. 启动与更新机制:并发伸缩 vs 严格按序

对 Deployment 来说,所有 Pod 是平等且可互换的。
扩容的时候可以并发启动 10 个,缩容的时候也可以随机杀掉 5 个。
更新时直接走滚动更新(Rolling Update),一边建新的,一边删旧的。

StatefulSet 则有着严格的尊卑长幼(序号 $0 \dots N-1$)。
启动时必须等 web-0 健康之后,才去启动 web-1。
缩容时必须逆序,先关 web-N-1。
更新时也是一个 Pod 一个 Pod 按顺序更新,前一个 Ready 了才更新下一个,确保主从节点或者集群状态在升级过程中不会崩溃。

记得刚转做 K8s 运维那会儿,有次业务线上部署 Redis 主从集群。
新手小兄弟为了省事,直接抄了一段 Deployment 的 YAML 跑了上去,设置了 3 个副本。
结果底层某个 Node 节点出问题漂移,Pod 重启之后直接重新生成了新的名字和 IP,集群节点之间通过固定名字寻址失败,Redis 槽位直接乱套,数据写入失败导致全线报错。

后来紧急抢修,先拉停服务,重新编写 StatefulSet 配置:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis-cluster
spec:
  serviceName: "redis-service"
  replicas: 3
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
      - name: redis
        image: redis:6.2-alpine
        ports:
        - containerPort: 6379
          name: redis-port
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 10Gi

通过指定 serviceName 搭配 Headless Service,加上 volumeClaimTemplates 为每个 Redis 实例独占持久卷,才让这个集群稳定跑了下来。这一夜教训真的记忆深刻。

  • 千万不要把数据库、ZooKeeper、Kafka 这种有状态组件放在普通 Deployment 下。
  • 千万不要以为 StatefulSet 删掉了 Pod 会顺手帮清理 PVC,PVC 是需要运维手动确认删掉的,不然磁盘空间会被偷摸吃满。
  • 千万不要只监控 CPU 和内存,对于 StatefulSet,存储挂载状态和 Headless Service 的 DNS 解析成功率才是核心命门。
  • 千万不要在生产环境对 StatefulSet 随意调整顺序序号,可能导致分布式共识协议(如 Paxos/Raft)状态异常。

平时做故障排查或架构演练,推荐这几个命令行和工具实操备用:

  • 排查 StatefulSet 状态: kubectl get sts -o wide
  • 查看绑定的 PVC 映射: kubectl get pvc -l app=redis
  • 测试无头服务 DNS 解析: kubectl run -i --tty --rm debug --image=busybox --restart=Never -- nslookup redis-cluster-0.redis-service

说白了,搞懂了 Deployment 和 StatefulSet,你才算真正迈进了 Kubernetes 架构设计的门槛。运维不只是敲代码和配置,更是对系统底层稳定性的极致掌控。

如果这篇文章对你有帮助,别忘了点赞转发支持一下!想了解更多运维实战经验和技术干货,记得关注微信公众号@运维躬行录,领取学习大礼包!!!我会持续分享更多接地气的运维知识和踩坑经验。让我们一起在运维这条路上互相学习,共同进步!

公众号:运维躬行录
个人博客:躬行笔记

文章目录

博主介绍

热爱技术的云计算运维工程师,Python全栈工程师,分享开发经验与生活感悟。
欢迎关注我的微信公众号@运维躬行录,领取海量学习资料

微信二维码