【Kubernetes专项】K8s 控制器 StatefulSet 从入门到企业实战应用

十四、K8s 控制器 StatefulSet 从入门到企业实战应用

14.1 StatefulSet 控制器概述

StatefulSet是为了管理有状态服务的问题而设计的

14.1.1 有、无状态服务概念

有状态服务:StatefulSet 是有状态的集合,管理有状态的服务,它所管理的 Pod 的名称不能随意变化。数据持久化的目录也是不一样,每一个 Pod 都有自己独有的数据持久化存储目录。比如 MySQL 主从、redis 集群等。

无状态服务:RC、Deployment、DaemonSet都是管理无状态的服务,它们所管理的Pod的IP、名字,启停顺序等都是随机的。个体对整体无影响,所有pod都是共用一个数据卷的,部署的tomcat就是无状态的服务,tomcat被删除,在启动一个新的tomcat,加入到集群即可,跟tomcat的名字无关。

14.2 St atefulSet 资源清单文件编写

14.2.1 statefulset.spec字段
bash 复制代码
[root@k8s-master1 ~]# kubectl explain statefulset.spec
  minReadySeconds       <integer>
  # 指定新创建的 Pod 在被标记为就绪(Ready)后,还需等待多少秒才被视为"可用"。该值用于控制滚动更新或扩缩容时对服务可用性的影响。默认为 0。
  
  ordinals      <StatefulSetOrdinals>
  # (Kubernetes 1.26+ 引入)用于自定义 StatefulSet Pod 的起始序号(ordinal)。默认从 0 开始,可通过此字段设置起始值(如从 10 开始),适用于需要与外部系统序号对齐的场景。
  
  persistentVolumeClaimRetentionPolicy  <StatefulSetPersistentVolumeClaimRetentionPolicy>
  # 定义在 StatefulSet 被删除或缩容时,其关联的 PersistentVolumeClaim(PVC)的保留策略。可配置 whenDeleted 和 whenScaled 字段,分别控制删除 StatefulSet 或缩容时是否保留 PVC。默认行为是保留 PVC。
  
  podManagementPolicy   <string>
  # Pod 管理策略,控制 Pod 的创建、删除和更新顺序。
  	1.OrderedReady(默认):按序创建/更新(从 0 到 N-1),按逆序删除(从 N-1 到 0),且每个 Pod 必须就绪后才继续下一个。
	2.Parallel:并行创建、删除和更新所有 Pod,不等待前一个 Pod 就绪。

  replicas      <integer>
  # 副本数
  
  revisionHistoryLimit  <integer>
  # 保留的历史 ReplicaSet(或 ControllerRevision)数量,用于支持回滚操作。默认为 10。设为 0 将禁用回滚能力。
  
  selector      <LabelSelector> -required-
  # 标签选择器
  
  serviceName   <string> -required-
  # 用于提供 Pod 网络标识的 Headless Service 名称。StatefulSet 依赖此 Service 为每个 Pod 提供稳定的 DNS 记录(格式:<pod-name>.<service-name>.<namespace>.svc.cluster.local)。
  
  template      <PodTemplateSpec> -required-
  # pod资源模版
  
  updateStrategy        <StatefulSetUpdateStrategy>
  # 定义 StatefulSet 的更新策略。
  
  volumeClaimTemplates  <[]PersistentVolumeClaim>
  # 存储卷申请模板列表。每个模板会为每个 Pod 自动生成一个独立的 PVC,命名格式为 <volumeClaimTemplate.name>-<statefulset.name>-<ordinal>。这些 PVC 与 Pod 生命周期解耦,即使 Pod 被删除,PVC 和其绑定的 PV 通常仍保留(除非通过 persistentVolumeClaimRetentionPolicy 显式删除)。

14.3 StatefulSet 使用案例:部署web站点

bash 复制代码
# 编写一个包含service及statefulset的资源清单文件
[root@k8s-master1 statefulset]# vim sts.yaml
apiVersion: v1
kind: Service			# Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  clusterIP: None
  ports:
  - port: 80
    name: web
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet		# StatefulSet
metadata:
  name: web
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  serviceName: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: docker.io/library/nginx:latest
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:			# 存储卷申请模版
  - metadata:
      name: www
    spec:
      accessModes: ["ReadWriteOnce"]
      storageClassName: "nfs"	# 指定从哪个存储类申请 pv资源
      resources:
        requests:
          storage: 1Gi			# 需要1Gi的 pvc,会自动跟符合条件的 pv进行绑定

[root@k8s-master1 statefulset]# kubectl apply -f sts.yaml

# 查看statefulset资源
[root@k8s-master1 statefulset]# kubectl get sts
web    2/2     5s

# 查看headless service(无头服务)
[root@k8s-master1 statefulset]# kubectl get svc | grep nginx
nginx        ClusterIP   None         <none>        80/TCP    9s

# 查看pod资源:创建出来是有序的资源
[root@k8s-master1 statefulset]# kubectl get pods | grep web
web-0                              1/1     Running   0          21s
web-1                              1/1     Running   0          19s

# 查看pvc资源
[root@k8s-master1 sc]# kubectl get pvc
www-web-0   Bound    pvc-814dc3bd-d61f-401e-997e-026cb983f47a   1Gi        RWO            nfs
www-web-1   Bound    pvc-ca801dcd-f3ca-4d52-b864-44c8af962839   1Gi        RWO            nfs

# 查看pv资源
[root@k8s-master1 sc]# kubectl get pv
pvc-814dc3bd-d61f-401e-997e-026cb983f47a   1Gi        RWO            Delete           Bound    default/www-web-0   nfs
pvc-ca801dcd-f3ca-4d52-b864-44c8af962839   1Gi        RWO            Delete           Bound    default/www-web-1   nfs

# 使用 kubectl run 运行一个提供 nslookup命令 的容器的,这个命令来自于 dnsutils包,通过对 pod主机名 执行nslookup,可以检查它们在集群内部的DNS地址:

[root@k8s-master1 sc]# kubectl run busybox --image docker.io/library/busybox:1.28  --image-pull-policy=IfNotPresent --restart=Never --rm -it busybox -- sh
If you don't see a command prompt, try pressing enter.
/ # nslookup 10.244.90.132
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

Name:      10.244.90.132
Address 1: 10.244.90.132 web-0.nginx.default.svc.cluster.local
/ #
/ #
/ # nslookup 10.244.147.202
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

Name:      10.244.147.202
Address 1: 10.244.147.202 web-1.nginx.default.svc.cluster.local
/ #
/ #
/ # nslookup web-0.nginx.default.svc.cluster.local
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-0.nginx.default.svc.cluster.local
Address 1: 10.244.90.132 web-0.nginx.default.svc.cluster.local
/ #
/ #
/ # nslookup web-1.nginx.default.svc.cluster.local
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-1.nginx.default.svc.cluster.local
Address 1: 10.244.147.202 web-1.nginx.default.svc.cluster.local

14.4 Headless Service(无头服务)总结

14.4.1 Headless Service 概述

Headless Service(无头服务) 是 Kubernetes 中一种特殊的 Service 类型,其核心特点是 不分配 ClusterIP,也不提供负载均衡功能。

关键特征

  1. 配置方式

    yaml 复制代码
    spec:
      clusterIP: None  # 关键配置:显式设置为 None
  2. 与普通 Service 的区别

    特性 普通 Service Headless Service
    ClusterIP 分配虚拟 IP(如 10.96.123.45) 无(None)
    负载均衡 自动将请求分发到后端 Pod 不提供负载均衡
    DNS 解析 返回 Service 的 ClusterIP 直接返回后端 Pod 的 IP 列表
    适用场景 无状态应用(Deployment) 有状态应用(StatefulSet)
  3. DNS 行为差异

    • 普通 Service:nginx.default.svc.cluster.local → 解析为 ClusterIP
    • Headless Service:nginx.default.svc.cluster.local → 直接解析为所有匹配 Pod 的 IP 列表(如 10.244.1.10, 10.244.2.15
    • 对于 StatefulSet Pod:支持通过稳定 DNS 记录访问单个 Pod,例如:
      • web-0.nginx.default.svc.cluster.local → 解析为 web-0 Pod 的 IP
      • web-1.nginx.default.svc.cluster.local → 解析为 web-1 Pod 的 IP

在 StatefulSet 中的作用

StatefulSet 依赖 Headless Service 实现:

  • 稳定的网络标识 :每个 Pod 拥有固定的 DNS 名称(如 web-0.nginx
  • Pod 间直接通信:应用可直接通过 Pod 的 DNS 名称互相访问,无需经过 Service 负载均衡
  • 有状态应用需求:适用于数据库集群(如 MySQL MGR、MongoDB 副本集)、ZooKeeper 等需要节点身份识别的场景

配置分析

yaml 复制代码
apiVersion: v1
kind: Service
metadata:
  name: nginx
spec:
  clusterIP: None  # ← 这正是 Headless Service 的标志
  selector:
    app: nginx

此配置正确创建了 Headless Service,为 StatefulSet 的每个 Pod(web-0、web-1)提供独立的 DNS 记录,满足有状态应用的网络需求。当前 StatefulSet 无法创建 Pod 的问题与 Headless Service 无关,而是由 mountPath 未配置导致。

Headless service不分配clusterIP,headless service可以通过解析service的DNS,返回所有Pod的dns和ip地址 (statefulSet部署的Pod才有DNS),普通的service,只能通过解析service的DNS返回service的ClusterIP。

headless service会为service分配一个域名

<service name>.$<namespace name>.svc.cluster.local

14.4.2 举例说明:Headless ServiceService 的区别
bash 复制代码
# 通过deployment创建pod,pod前端创建一个service
[root@k8s-master1 sc]# vim deploy_svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  labels:
    run: my-nginx
spec:
  type: ClusterIP
  ports:
  - port: 80			# service的端口,暴露给k8s集群内部服务访问
    protocol: TCP
    targetPort: 80		# pod容器中定义的端口
  selector:
    run: my-nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      run: my-nginx
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: docker.io/library/busybox:1.28
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80
        command: ["sleep","3600"]
        
[root@k8s-master1 sc]# kubectl apply -f deploy_svc.yaml

# 查看service资源
[root@k8s-master1 sc]# kubectl get svc | grep my-nginx
my-nginx     ClusterIP   10.107.206.98   <none>        80/TCP    4m47s

# 查看pod资源:发现通过deploy创建出pod是随机生成的,而不是规范顺序的
[root@k8s-master1 sc]# kubectl get pods -owide | grep my-nginx
my-nginx-56946449b9-97qh2          1/1     Running   10.244.147.203   k8s-node1.kaser.org
my-nginx-56946449b9-wwdrg          1/1     Running   10.244.90.135    k8s-node2.kaser.org

14.5 StatefulSet 总结

  1. Statefulset 管理的 pod,pod 名字是有序的,由 statefulset 的名字-0、1、2这种格式组成
  2. 创建 statefulset 资源的时候,必须事先创建好一个 service
    • 如果创建的 service 没有 ip,那对这个 service 做 dns解析,会找到它所关联的 pod ip
    • 如果创建的 service 有 ip,那对这个 service 做 dns解析,会解析到 service 本身ip
  3. statefulset 管理的pod,删除pod,新创建的pod名字跟删除的pod名字是一样的
  4. statefulset 具有 volumeclaimtemplate 这个字段,这个是卷申请模板,会自动创建 pv
    • pvc 也会自动生成,跟 pv 进行绑定
    • 那如果创建的 statefulset 使用了 volumeclaimtemplate 这个字段,那创建 pod,数据目录是独享的
  5. ststefulset 创建的 pod 域名
    • 域名组成:pod-name.svc-name.svc-namespace.svc.cluster.local

14.6 StatefulSet 实现 pod 资源扩缩容

14.6.1 操作文件实现动态扩缩容
bash 复制代码
# 开启watch监控观察
watch -n1 kubectl get pods 
14.6.1.1 扩容
bash 复制代码
# 编辑文件
[root@k8s-master1 statefulset]# vim sts.yaml
.......
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  replicas: 4		# 2 --> 4
.......

[root@k8s-master1 statefulset]# kubectl apply -f sts.yaml
[root@k8s-master1 statefulset]# kubectl get pods | grep web
web-0                              1/1     Running
web-1                              1/1     Running
web-2                              1/1     Running
web-3                              1/1     Running
14.6.1.2 缩容
bash 复制代码
# 编辑文件
[root@k8s-master1 statefulset]# vim sts.yaml
.......
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  replicas: 2		# 4 --> 2
.......

[root@k8s-master1 statefulset]# kubectl apply -f sts.yaml
[root@k8s-master1 statefulset]# kubectl get pods | grep web
web-0                              1/1     Running
web-1                              1/1     Running
14.6.2 编辑控制器实现动态扩缩容
14.6.2.1 扩容
bash 复制代码
[root@k8s-master1 statefulset]# kubectl edit sts web
22   replicas: 5		# 2 -- 5

# 不需要更新资源文件
[root@k8s-master1 statefulset]# kubectl get pods | grep web
web-0                              1/1     Running
web-1                              1/1     Running
web-2                              1/1     Running
web-3                              1/1     Running
web-4                              1/1     Running
14.6.2.2 缩容
bash 复制代码
[root@k8s-master1 statefulset]# kubectl edit sts web
22   replicas: 2		# 5 -- 2

# 不需要更新资源文件
[root@k8s-master1 statefulset]# kubectl get pods | grep web
web-0                              1/1     Running
web-1                              1/1     Running

14.7 StatefulSet 资源更新策略

14.7.1 sts.spec.updateStrategy字段
bash 复制代码
]# kubectl explain sts.spec.updateStrategy
  rollingUpdate <RollingUpdateStatefulSetStrategy>  
  type  <string>
  # 更新策略类型(两种)
     - `"OnDelete"`
     	# 手动删除时更新
     - `"RollingUpdate"`
		# 滚动更新
14.7.2 sts.spec.updateStrategy.rollingUpdate字段
bash 复制代码
]# kubectl explain sts.spec.updateStrategy.rollingUpdate
  maxUnavailable        <IntOrString>
  # 在更新过程中,允许同时不可用的 Pod 的最大数量(或百分比)
  
  partition     <integer>
  # 用于 StatefulSet 的有序更新,指定从哪个序号开始更新 Pod
  # 大于等于这个序号的pod进行更新
14.7.3 RollingUpdate滚动更新策略
bash 复制代码
[root@k8s-master1 statefulset]# vim sts.yaml
......
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  serviceName: nginx
  updateStrategy:			# 策略
  	type: RollingUpdate		# 默认就为滚动更新,可以不写
    rollingUpdate:			# 滚动更新
      maxUnavailable: 0
      partition: 1			# 从序号1之后(包括1)开始进行更新
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: docker.io/ikubernetes/myapp:v1	# 更新镜像(nginx --> myapp)
        imagePullPolicy: IfNotPresent
......

[root@k8s-master1 statefulset]# kubectl apply -f sts.yaml

# 另外打开终端观察
# 发现,从序号1且包含1之后开始更新
]# watch -n1 kubectl get pods -owide \| grep web 


# web-0 web-1 进行测试,查看镜像是否一样
[root@k8s-master1 statefulset]# kubectl exec -it web-0 -- /bin/bash
root@web-0:/# cd /usr/share/nginx/html/
root@web-0:/usr/share/nginx/html# ls

[root@k8s-master1 statefulset]# kubectl exec -it web-1 -- /bin/sh
/ # ls -l /usr/share/nginx/html/
total 0
14.7.4 OnDelete手动删除更新策略
bash 复制代码
# 此时如果将策略改变为 `OnDelete`,会发生什么呢?
[root@k8s-master1 statefulset]# vim sts.yaml
......
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  serviceName: nginx
  updateStrategy:			# 策略
  	type: OnDelete
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers: 
      - name: nginx
        image: docker.io/library/nginx:latest	# 更新镜像(myapp --> nginx)
        imagePullPolicy: IfNotPresent
......

[root@k8s-master1 statefulset]# kubectl apply -f sts.yaml

# 另外打开终端观察,发现没有变化
[root@k8s-master1 statefulset]# watch -n1 kubectl get pods -owide \| grep web 

# 手动删除之后,才会发现进行变化
[root@k8s-master1 statefulset]# kubectl delete pods web-0 web-1 web-2
相关推荐
风流倜傥唐伯虎2 小时前
Windows 版 Docker 的 Linux 环境(docker-desktop)与 builder-jammy-base:latest 镜像核心区别
linux·docker·容器
Ha_To2 小时前
2026.1.30 搭建docker仓库
运维·docker·容器
lpfasd1232 小时前
Docker Desktop 在国内使用的囧境:镜像拉取失败、加速器失效与破局之道
运维·docker·容器
江湖有缘2 小时前
Docker部署SurveyKing调查问卷系统和考试系统
运维·docker·容器
江畔何人初16 小时前
pod的定义以及创建过程
linux·运维·云原生
等什么君!17 小时前
docker -数据卷技术
运维·docker·容器
花酒锄作田18 小时前
Debian 13基于kubeadm和containerd部署单节点kubernetes
kubernetes·containerd·cilium
上天_去_做颗惺星 EVE_BLUE18 小时前
Docker高效使用指南:从基础到实战模板
开发语言·ubuntu·docker·容器·mac·虚拟环境
Gary董20 小时前
高并发的微服务架构如何设计
微服务·云原生·架构