欢迎光临
我们一直在努力

【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字段

[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站点

# 编写一个包含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

image-20260129001702184

14.4 Headless Service(无头服务)总结

14.4.1 Headless Service 概述

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

关键特征

  • 配置方式

    spec:
    clusterIP: None # 关键配置:显式设置为 None

  • 与普通 Service 的区别

    特性普通 ServiceHeadless Service
    ClusterIP 分配虚拟 IP(如 10.96.123.45) 无(None)
    负载均衡 自动将请求分发到后端 Pod 不提供负载均衡
    DNS 解析 返回 Service 的 ClusterIP 直接返回后端 Pod 的 IP 列表
    适用场景 无状态应用(Deployment) 有状态应用(StatefulSet)
  • 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 等需要节点身份识别的场景

    配置分析

    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 Service 与 Service 的区别

    # 通过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 总结

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

    14.6.1 操作文件实现动态扩缩容

    # 开启watch监控观察
    watch -n1 kubectl get pods

    14.6.1.1 扩容

    # 编辑文件
    [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 缩容

    # 编辑文件
    [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

    18

    14.6.2 编辑控制器实现动态扩缩容
    14.6.2.1 扩容

    [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 缩容

    [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字段

    ]# kubectl explain sts.spec.updateStrategy
    rollingUpdate <RollingUpdateStatefulSetStrategy>
    type <string>
    # 更新策略类型(两种)
    `"OnDelete"`
    # 手动删除时更新
    `"RollingUpdate"`
    # 滚动更新

    14.7.2 sts.spec.updateStrategy.rollingUpdate字段

    ]# kubectl explain sts.spec.updateStrategy.rollingUpdate
    maxUnavailable <IntOrString>
    # 在更新过程中,允许同时不可用的 Pod 的最大数量(或百分比)

    partition <integer>
    # 用于 StatefulSet 的有序更新,指定从哪个序号开始更新 Pod
    # 大于等于这个序号的pod进行更新

    14.7.3 RollingUpdate滚动更新策略

    [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手动删除更新策略

    # 此时如果将策略改变为 `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

    赞(0)
    未经允许不得转载:171主机测评 » 【Kubernetes专项】K8s 控制器 StatefulSet 从入门到企业实战应用
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址