在运行数据库、消息队列这类需要持久存储和固定身份的服务时,用Deployment往往不太够用。比如你部署三个MySQL实例,Deployment创建的Pod每次重启都可能换名字、换IP,数据也跟着乱套。这时候就得上StatefulSet了。
为什么需要StatefulSet?
简单说,StatefulSet是为有状态ref="/tag/47/" style="color:#B2A89E;font-weight:bold;">应用设计的工作负载控制器。它能保证每个Pod有唯一的、稳定的网络标识和存储卷,即使Pod被删除重建,名字和挂载的磁盘还能对得上。比如你给一个MongoDB副本集做集群,每个节点都要记住自己的角色和数据位置,这就离不开StatefulSet的支持。
核心特性一:稳定的网络身份
StatefulSet里的Pod不是随便叫名字的。比如你的应用叫mysql,那第一个Pod就是mysql-0,第二个是mysql-1,依次类推。这个编号不会变,对应的DNS记录也是固定的,服务之间调用可以直接用主机名访问,比如mysql-0.mysql.default.svc.cluster.local,特别适合主从复制这种场景。
核心特性二:独立的持久化存储
每个Pod启动时会绑定一个独立的PersistentVolumeClaim,删掉Pod再重建,新Pod还会挂回原来那个磁盘。这样数据就不会丢。比如你跑一个ZooKeeper集群,每个节点的数据目录必须保持一致,StatefulSet就能确保这一点。
实际使用示例
下面是一个简单的StatefulSet配置,用来部署一个带持久存储的Nginx服务:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
serviceName: "nginx"
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:stable
ports:
- containerPort: 80
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi
适用场景举例
常见的用法包括部署Elasticsearch、Kafka、etcd这些分布式系统。它们对节点身份、启动顺序、数据一致性都有要求,用StatefulSet可以很好地满足。比如Kafka的Broker需要知道自己的broker.id,通过Pod序号生成唯一ID就很方便。
需要注意的地方
StatefulSet默认按顺序启动和删除Pod,比如web-0没起来前,web-1不会开始创建。升级策略也可以设置成滚动更新,但是一次只动一个实例,避免集体宕机。另外,缩容的时候是从高序号往低序号删,比如从web-2开始删,保留前面的节点稳定运行。