7.statefulset

StatefulSet 是 Kubernetes 中的一种控制器(Controller),用于管理有状态应用程序的部署。与Deployment 控制器不同,StatefulSet 为每个 Pod 实例分配了一个唯一的标识符,并确保这些标识符在 Pod 重新创建时保持不变。这为有状态应用程序提供了一些关键的功能和保证,例如稳定的网络标识符、有序的部署和扩展以及持久化存储。
下面是 StatefulSet 的一些基本介绍:

1、唯一标识符:每个 StatefulSet 管理的 Pod 实例都被分配了一个唯一的标识符,通常是一个整数索引。这个标识符可以用于在网络中唯一地标识每个 Pod 实例。

2、稳定的网络标识符:StatefulSet 中的每个 Pod 实例都有一个稳定的网络标识符,通常是一个DNS 名称。这使得其他应用程序可以通过名称轻松地访问特定的 Pod 实例,而不需要关注其具体的 IP地址变化。

3、有序的部署和扩展:StatefulSet 控制器确保 Pod 实例按照定义的顺序逐个启动和关闭。这对于依赖先前实例状态的应用程序非常重要。此外,扩展 StatefulSet 时,新的 Pod 实例也会按照指定的顺序逐个创建。

4、持久化存储:StatefulSet 允许每个 Pod 实例关联一个持久化卷(Persistent Volume),这使得有状态应用程序可以在 Pod 重新创建时保留其数据。这为应用程序提供了持久化存储的能力,使得数据不会丢失或重置。
备注:什么是有状态服务?

有状态服务是指在服务运行过程中需要维护和管理一些状态信息的服务。与之相对的是无状态服务,它们在处理请求时不依赖或不保存任何状态信息。

有状态服务的一个常见场景是在分布式系统中,当需要处理一系列相关的请求时,服务可能需要保留

先前请求的状态信息。例如,一个在线购物网站的结账过程,每个请求都需要知道当前用户的购物车内容、支付状态等信息,这些信息需要在整个结账过程中保持一致性。又或者,在一个聊天应用中,服务需要跟踪每个用户的聊天历史记录以提供正确的会话。
为了实现有状态服务,可以采用以下方法之一:

1、会话状态:在每个客户端和服务之间建立一个会话,将状态信息存储在会话中。客户端在每个请求中提供会话标识符,服务根据该标识符检索和更新相应的状态。常见的实现方式是使用会话标识符和键值存储(如 Redis)来存储状态信息。

2、数据库存储:将状态信息存储在数据库中。服务可以将状态数据存储在关系型数据库(如MySQL)或 NoSQL 数据库(如 MongoDB)中。通过数据库查询和更新操作,服务可以管理状态的读取和修改。

3、分布式状态管理:在分布式系统中,可以使用分布式状态管理工具(例如 ZooKeeper、etcd 或Consul)来存储和管理状态信息。这些工具提供了分布式一致性保证,允许多个服务实例之间共享和同

步状态。

编写资源清单

bash 复制代码
root@ubuntu0:/devops/pv# kubectl explain statefulset
KIND:     StatefulSet
VERSION:  apps/v1

字段说明:
• apiVersion:定义 StatefulSet 资源需要使用的 API 版本。
• kind:定义资源类型。
• metadata:元数据对象,包含 StatefulSet 的描述信息。
• spec:容器相关的信息,定义了 StatefulSet 中 Pod 的期望标识。

2、查看 StatefulSet 的 spec 字段如何定义,使用以下命令:kubectl explain
statefulset.spec
字段说明:
• podManagementPolicy:Pod 的管理策略。
• replicas:副本数,定义 StatefulSet 中 Pod 的数量。
• revisionHistoryLimit:保留的历史版本数。
• selector:标签选择器,用于选择与之关联的 Pod。
• serviceName:Headless Service 的名称。
• template:生成 Pod 的模板,用于描述将创建的 Pod 的属性。
• updateStrategy:更新策略,定义如何更新 Pod。
• volumeClaimTemplates:存储卷申请模板,定义 Pod 使用的存储卷。

3、查看 StatefulSet 的 spec.template 字段如何定义,使用以下命令:kubectl explain
statefulset.spec.template
字段说明:
• metadata:元数据对象,包含 Pod 模板的描述信息。
• spec:定义 Pod 的属性和配置。




首先创建一个存储类
root@ubuntu0:/devops/statfulset# cat class-web.yaml 
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs-web
provisioner: example.com/nfs
reclaimPolicy: Retain  #回收策略

root@ubuntu0:/devops/statfulset# kubectl get storageclasses.storage.k8s.io 
NAME      PROVISIONER       RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
nfs       example.com/nfs   Delete          Immediate           false                  2d10h
nfs-web   example.com/nfs   Retain          Immediate           false                  67s

把 nginx 的离线压缩包 nginx.tar.gz 上传到 ubuntu1、ubuntu2 上,手动解压:

root@ubuntu0:/devops/statfulset# cat nginx.yaml 
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  serviceName: "my-web"
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.23
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/htm
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes:  ["ReadWriteMany"]
      storageClassName:  "nfs-web"
      resources:
        requests:
          storage: 1Gi



---
apiVersion: v1
kind: Service
metadata:
  name: my-web
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
      #protocol: tcp
      #  targetPort: 80
  clusterIP: None
  selector:
    app: nginx

root@ubuntu0:/devops/statfulset# kubectl apply -f nginx.yaml 
statefulset.apps/nginx created
service/my-web created
root@ubuntu0:/devops/statfulset# kubectl get storageclasses.storage.k8s.io 
NAME      PROVISIONER       RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
nfs-web   example.com/nfs   Retain          Immediate           false                  47h
root@ubuntu0:/devops/statfulset# kubectl get statefulsets.apps             
NAME    READY   AGE
nginx   1/1     14s
root@ubuntu0:/devops/statfulset# kubectl get statefulsets.apps -owide
NAME    READY   AGE   CONTAINERS   IMAGES
nginx   1/1     18s   nginx        nginx:1.23
root@ubuntu0:/devops/statfulset# kubectl get pods -owide
NAME                  READY   STATUS    RESTARTS      AGE     IP            NODE      NOMINATED NODE   READINESS GATES
nginx-0               1/1     Running   0             51s     10.100.1.11   ubuntu1   <none>           <none>
xpp-c44d68dcf-456kt   1/1     Running   1 (22h ago)   4d10h   10.100.1.10   ubuntu1   <none>           <none>

为什么是nginx-0
在 Kubernetes StatefulSet 中,Pod 的命名规则是 固定的命名模式:
从 0 开始的序号:

pod中的名字是根据statefulset中的名字而来
root@ubuntu0:/devops/statfulset# kubectl get statefulsets.apps       
NAME    READY   AGE
nginx   1/1     42m
第一个 Pod:nginx-0
第二个 Pod:nginx-1
第三个 Pod:nginx-2

稳定性和唯一性:
即使 Pod 重启或重新调度,名称保持不变
每个序号对应一个稳定的网络标识和存储卷
Pod 主机名:nginx-0
DNS 名称:nginx-0.my-web.default.svc.cluster.local
nginx-0.my-web.default.svc.cluster.local
└──┬── └──┬── └──┬── └─┬─ └──┬────┘
   │      │      │     │     │
   │      │      │     │     └─ 集群域名后缀(可配置)
   │      │      │     └─ 服务类型(Service)
   │      │      └─ 命名空间(Namespace)
   │      └─ 服务名(Service Name)
   └─ Pod 名称




可以通过 Service 访问:kubectl run -it --rm --image=busybox:1.28 test -- sh
bash
# 在 busybox 容器中测试
wget -O- http://nginx-0.my-web

root@ubuntu0:/devops/statfulset# kubectl exec -it nginx-0 -- sh
# 
# 
# hostname
nginx-0
# hostname -f
nginx-0.my-web.default.svc.xp.com
# cat /etc/hosts
# Kubernetes-managed hosts file.
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::0	ip6-localnet
fe00::0	ip6-mcastprefix
fe00::1	ip6-allnodes
fe00::2	ip6-allrouters
10.100.1.11	nginx-0.my-web.default.svc.xp.com	nginx-0


查看svc
root@ubuntu0:/devops/statfulset#  kubectl get svc -l app=nginx
NAME     TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
my-web   ClusterIP   None         <none>        80/TCP    7m18s

root@ubuntu0:/devops/statfulset# kubectl describe svc my-web 
Name:              my-web
Namespace:         default
Labels:            app=nginx
Annotations:       <none>
Selector:          app=nginx
Type:              ClusterIP
IP Family Policy:  SingleStack
IP Families:       IPv4
IP:                None
IPs:               None
Port:              web  80/TCP
TargetPort:        80/TCP
Endpoints:         10.100.1.11:80
Session Affinity:  None
Events:            <none>


此时查看pv,pvc
root@ubuntu0:/devops/statfulset# kubectl get pv,pvc 
NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                 STORAGECLASS   REASON   AGE
persistentvolume/pvc-ea67db55-19e6-4be0-ab7b-726198ca2712   1Gi        RWX            Retain           Bound    default/www-nginx-0   nfs-web                 7m49s

NAME                                STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/www-nginx-0   Bound    pvc-ea67db55-19e6-4be0-ab7b-726198ca2712   1Gi        RWX            nfs-web        7m50s


对于无头的svc,需要访问pod
root@ubuntu0:/devops/statfulset# curl nginx-0.my-web.default.svc.xp.com
<html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
</body>
</html>

举例说明 service 和 headless service 区别:

bash 复制代码
1、通过 deployment 创建 pod,pod 前端创建一个 service,这个 svc 是有 ip 的
[root@xuegod63 ~]# cat deploy-service.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 #选择拥有 run=my-nginx 标签的 pod
---
apiVersion: apps/v1
kind: Deployment
metadata:
 name: my-nginx
spec:
 selector:
   matchLabels:
     run: my-nginx
 replicas: 2
 template:
 metadata:
   labels:
     run: my-nginx
 spec:
 containers:
 - name: my-nginx
   image: busybox
   ports:
   - containerPort: 80
     command:
     - sleep
     - "3600"
#更新资源清单文件
[root@xuegod63 ~]# kubectl apply -f deploy-service.yaml


#查看 service
[root@xuegod63 ~]# kubectl get svc -l run=my-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) 
my-nginx ClusterIP 10.100.89.90 <none> 80/TCP 
#查看 pod
[root@xuegod63 ~]# kubectl get pods -l run=my-nginx
NAME READY STATUS RESTARTS AGE
my-nginx-58f74fc5b6-jzbvk 1/1 Running 0 70s
my-nginx-58f74fc5b6-n9lqv 1/1 Running 0 53s 
#通过上面可以看到 deployment 创建的 pod 是随机生成的
[root@xuegod63 ~]#kubectl run busybox --image busybox:1.28 --restart=Never --rm -
it busybox -- sh
root@web-1:/# nslookup my-nginx.default.svc.cluster.local
Server: 10.96.0.10
Address: 10.96.0.10#53
Name: my-nginx.default.svc.cluster.local
Address: 10.100.89.90 
#解析的是 service 的 ip 地址

有 ip 的 service 和没有 ip 的 service 有啥区别:

bash 复制代码
从效率的角度来看,解析带有 IP 的 SVC 和解析没有 IP 的 SVC,这两种方式的效率可能会有所
不同
1. 解析带有 IP 的 SVC:
• 解析到 SVC 的 IP 地址可以直接将请求发送到指定的 IP 地址,省略了额外的 DNS 
查询步骤,因此通常具有较高的效率。
• 由于 SVC 具有 IP 地址,请求可以直接路由到指定的 IP 地址上,跳过了额外的
DNS 解析和负载均衡步骤。
• 这种方式适用于从集群外部或其他命名空间访问 SVC,或者需要直接使用 SVC 的 IP 
地址进行访问的场景。

2. 解析没有 IP 的 SVC:
 • 解析到没有 IP 的 SVC,DNS 解析会将 SVC 名称解析为与之关联的后端 Pod 的
IP 地址。这涉及到额外的 DNS 查询和解析步骤,可能会引入一定的延迟。
• 解析到后端 Pod 的 IP 地址后,请求需要通过负载均衡机制(如轮询或基于权重的负
载均衡)选择一个后端 Pod 来处理请求,这可能会增加一定的延迟。
• 这种方式适用于集群内部访问 SVC 的场景,其中请求在集群内部的服务间进行通信。

实战:Statefulset 管理 pod-扩容、缩容、更新

bash 复制代码
Statefulset 实现 pod 的动态扩容
root@ubuntu0:/devops/statfulset# grep 2 nginx.yaml 
  replicas: 2
        image: nginx:1.23
root@ubuntu0:/devops/statfulset# kubectl apply -f nginx.yaml 
statefulset.apps/nginx configured
service/my-web unchanged
root@ubuntu0:/devops/statfulset# 
root@ubuntu0:/devops/statfulset# 
root@ubuntu0:/devops/statfulset# kubectl get statefulsets.apps 
NAME    READY   AGE
nginx   2/2     13h
root@ubuntu0:/devops/statfulset# kubectl get pod
NAME                  READY   STATUS    RESTARTS      AGE
nginx-0               1/1     Running   0             13h
nginx-1               1/1     Running   0             15s

#也可以直接编辑控制器实现扩容
root@ubuntu0:/devops/statfulset# kubectl edit statefulsets.apps nginx
spec:
  podManagementPolicy: OrderedReady
  replicas: 2


Statefulset 实现 pod 的动态缩容
如果我们觉得 4 个 Pod 副本太多了,想要减少,只需要修改配置文件 statefulset.yaml 里的
replicas 的值即可,把 replicaset:4 变成 replicas: 2,修改之后,执行如下命令更新:


Statefulset 实现 pod 的更新
1.上传myapp.tar.gz

2.更改镜像
root@ubuntu0:/devops/statfulset# grep ikubernetes/myapp:v2 nginx.yaml 
        image: ikubernetes/myapp:v2
root@ubuntu0:/devops/statfulset# kubectl apply -f nginx.yaml 
statefulset.apps/nginx configured
service/my-web unchanged
root@ubuntu0:/devops/statfulset# kubectl get pods -o wide -l app=nginx
NAME      READY   STATUS    RESTARTS   AGE   IP            NODE      NOMINATED NODE   READINESS GATES
nginx-0   1/1     Running   0          6s    10.100.1.12   ubuntu1   <none>           <none>
nginx-1   1/1     Running   0          9s    10.100.2.19   ubuntu2   <none>           <none>
root@ubuntu0:/devops/statfulset# kubectl describe pods nginx-0|grep image 
  Normal  Pulled     22s   kubelet            Container image "ikubernetes/myapp:v2" already present on machine


通过上面可以看到 pod 已经使用刚才更新的镜像 ikubernetes/myapp:v2 了

root@ubuntu0:/devops/statfulset# kubectl describe statefulsets.apps 
Name:               nginx
Namespace:          default
CreationTimestamp:  Thu, 01 Jan 2026 21:47:51 +0800
Selector:           app=nginx
Labels:             <none>
Annotations:        <none>
Replicas:           2 desired | 2 total
Update Strategy:    RollingUpdate
  Partition:        0   #partition=1 表示创建的 pod 序号>=1 的才会被更新
Pods Status:        2 Running / 0 Waiting / 0 Succeeded / 0 Failed
相关推荐
Eugene__Chen1 分钟前
Java关键字(曼波版)
java·开发语言
无望__wsk28 分钟前
Python第一次作业
开发语言·python·算法
Word码35 分钟前
[C++语法]-vector(用法详解及实现)
开发语言·c++
代码雕刻家42 分钟前
4.3.多线程&JUC-多线程的实现方式
java·开发语言
梦65043 分钟前
网络传输七层协议
开发语言·网络·php
南 阳1 小时前
Python从入门到精通day16
开发语言·python·算法
李少兄1 小时前
Java 后端开发中 Service 层依赖注入的最佳实践:Mapper 还是其他 Service?
java·开发语言
不会c+1 小时前
@Controller和@RequestMapping以及映射
java·开发语言
難釋懷1 小时前
解决状态登录刷新问题
java·开发语言·javascript
ytttr8731 小时前
基于MATLAB的三维装箱程序实现(遗传算法+模拟退火优化)
开发语言·matlab