K8S StatefulSet 快速开始

news2025/6/6 4:56:44

其实这篇文章的梗概已经写了很久了,中间我小孩出生了,从此人间多了一份牵挂。抽出一些时间去办理新生儿相关手续。初为人父确实艰辛,就像学技术一样,都需要有极大的耐心,付出很多的时间。

一、引子

1.1、独立的存储

我们来思考一下我们曾经学过的一些分布式中间件它们具有什么特点。

一些分布式中间件它们为了避免单点故障往往采用集群的方式,部署在不同的服务器上向外提供服务。同时为了防止单节点数据文件过大,造成磁盘IO瓶颈,追求更高性能和更大的吞吐量,数据往往在每个节点分片存储。

如下图的三个节点的 Kafka 集群,主题 TopicA 就有 Partition0、Partition1、Partition2 三个数据分区。集群的每个节点,都需要有独立的存储

 1.2、稳定的网络 ID

同时,为了保障分区容错性,避免单个节点宕机后它上面的数据片完全丢失,不同节点上的数据片又会互为备份。这样不同节点之间就需要有大量的网络请求,集群中的每个节点,都需要知道彼此的存在

就比如一个三节点的 Zookeeper 集群配置就会有:

...
server.1=192.168.1.121:2888:3888
server.2=192.168.1.122:2888:3888
server.3=192.168.1.123:2888:3888

 一个三节点的 RockeMQ 集群配置就会有:

...
namesrvAddr=worker1:9876;worker2:9876;worker3:9876

 1.3、启动的先后顺序

很多分布式中间件,为了提升读取性能,往往采用主从复制的方式。主节点专注于处理写操作,而读操作则分散到多个从节点。同时,限制从节点的写操作,也能一定程度上保证数据一致性。

下图是三个节点的 Zookeeper 集群,客户端的写操作只能由主节点处理,而从节点负责同步主节点数据并处理读请求。

主从复制往往需要在节点启动的时候通过选举算法来决定谁是主,谁是从。若多个节点同时尝试成为 Leader,可能因网络延迟导致选举耗时增加或脑裂(Split Brain)问题。

若节点能顺序启动,则当前两个节点选出 Leader 后,第三个节点直接加入集群成为 Follower 就可以了。

1.4、总结

我们总结一下,分布式中间件的一些特点:

  • 集群的每个节点,都需要有独立的存储
  • 集群中的每个节点,都需要知道彼此的存在
  • 集群节点的启动,有先后顺序的要求。

前面两个是硬性的,而启动的先后顺序,并不是所有分布式系统都有的特点。

二、StatefulSet 的前世今生

从 K8S 的角度来思考如何部署这种有状态的服务。

Deployment 可以吗?

Deployment 通过⼀个 pod 模板创建多个 pod 副本。这些副本除了它们的名字和 IP 地址不同外,没有别的差异。如果 pod 模板里描述了一个关联到特定数据卷的持久卷声明,那么 Deployment  的所有副本都将共享这个持久卷声明,也就是绑定到同一个持久卷声明,也就是绑定到同⼀个持久卷。

Deployment 里的所有 pod 共享相同的持久卷声明和持久卷:

因为是在 pod 模板里关联声明的,又会依据 pod 模板创建多个 pod 副本,则不能对每个副本都指定独立的持久卷声明。所以也不能通过一个 Deployment 来运行一个每个实例都需要独立存储的分布式数据存储服务,至少通过单个 Deployment 是做不到的。

Pod 间通过 IP 访问?

我们知道,在分布式中间件中,往往需要在每个集群成员的配置文件中列出所有其他集群成员和它们的 IP地址(或主机名)。但是在 Kubernetes 中,每次重新调度⼀个 pod,这个新的 pod 就有⼀个新的主机名和IP地址,这样就要求当集群中任何⼀个成员被重新调度后,整个应用集群都需要重新配置。这显然是不现实的,这就需为每个 Pod 维护一个稳定的网络标识

Pod 间通过 Service 访问?

那 Pod 之间能不能通过 Service 互相访问呢?

我们知道可以通过 Service 的 FQDN(Fully Qualified Domain Name,完全限定域名)来访问它所代理的后端 Pod,而且这个 FQDN 是不变的。

这个方案显然也不太现实。通过 Service 访问时,请求会被随机转发到任意后端 Pod,无法指定特定 Pod。

PodA 发出去的请求,甚至都可能发回给自己。

那能不能给每个 Pod 一个固定的 FQDN?

带着这些疑问,我们进入今天的 StatefulSet 的学习。

三、StatefulSet 是什么

StatefulSet 是 Kubernetes(K8s)中用于管理有状态应用的资源对象,是继 Deployment(无状态应用管理)之后的重要补充。它主要解决有状态应用的稳定性、唯一性、有序性数据持久化等问题,适用于需要唯一标识、顺序启动、可靠存储的场景(如数据库、消息队列等)。

# StatefulSet 规范
kubectl explain sts.spec
属性名类型必填说明
replicas <integer>副本数
revisionHistoryLimit<integer>保留的历史版本数
selector<LabelSelector> required标签选择器,
选择它所关联的 Pod
serviceName<string>requiredHeadless Service 的名字
template<PodTemplateSpec>required生成 Pod 的模板
volumeClaimTemplates<[]PersistentVolumeClaim>存储卷申领模板
minReadySeconds<integer>最小就绪秒数
updateStrategy<StatefulSetUpdateStrategy>更新策略

四、实战案例

我们先来看一个官网的例子:

1)创建 Headless Service

创建无头服务以便为 Pod 提供网络标识

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
  • 创建一个名为 nginx 的无头服务(Headless Service) 用来控制网络域名。
  • 所谓无头服务是 clusterIP 属性指定为 None。
  • 标签选择器查找具有 app:nginx 标签的 Pod。

 2)创建 StatefulSet

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysts
spec:
  selector:
    matchLabels:
      app: nginx # 必须匹配 .spec.template.metadata.labels
  serviceName: "nginx"
  replicas: 3 # 默认值是 1
  minReadySeconds: 10 # 默认值是 0
  template:
    metadata:
      labels:
        app: nginx # 必须匹配 .spec.selector.matchLabels
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: nginx
        image: nginx:1.14.2
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: vct
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: vct
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "managed-nfs-storage"
      resources:
        requests:
          storage: 100Mi
  • 创建一个名为 mysts 的 StatefulSet 控制器,在独立的 3 个 Pod 副本中启动 nginx 容器。
  • serviceName 指定前面创建的 Headless Service 的名字,即 nginx。
  • volumeClaimTemplates 为每个 Pod 提供持久卷申领(PVC)的模板。
  • accessModes 持久卷访问模式,ReadWriteOnce 卷可以被一个节点以读写方式挂载。因为每个 Pod 需要有独立的存储,所以官方建议用 ReadWriteOncePod 更准确。
  • storageClassName 动态供应的存储类名称。像我的系统中就安装了 NFS 的动态供应商。

  • containers.volumeMounts 容器的持久卷需挂载 volumeClaimTemplates 申领模板。
  • 查看 StatefulSet
# sts 是 StatefulSet 缩写
kubectl get sts

  • 查看 Pod
kubectl get pod -l app=nginx -owide

StatefulSet 会为关联的 Pod 保持一个不变的 Pod Name。

对于包含 N 个 副本的 StatefulSet,当部署 Pod 时,它们是依次创建的,顺序为 {0..N-1}。

Pod 主机名格式:$(StatefulSet 名称)-$(序号)  序号从 0 开始。

for i in {0..2}; do kubectl exec mysts-$i -- sh -c 'hostname';done

3)Pod FQDN

StatefulSet 创建的 Pod 会得到一个 DNS 子域,格式为: $(pod 名称).$(所属服务的 DNS 域名)。

例如,上面无头服务 nginx 的完全限定域名是 nginx.default.svc.cluster.local,则 Pod 的 DNS 为mysts-{0..2}.nginx.default.svc.cluster.local。

不同的中间件节点之间,就可以用这个 Pod DNS 进行通信。

4)无头服务与普通服务的区别

假设现在有这么一个 ClusterIP 的 Service:

对服务名进行解析,会得到 Service 的 ClusterIP:

 而对无头服务名进行解析,会得到所有关联的 Pod IP:

5)自动创建 PVC-PV

使用 StatefulSet 会自动帮我们创建 PVC 与 PV,并完成它们的绑定。

# 查看 PVC
kubectl get pvc

  • pvc 的名称格式 $(volumeClaimTemplates 名称)-$(pod 名称)
    # 查看 PV
    kubectl get pv | grep pvc

    • pv 的名称格式 pvc-$(PVC 的 uid)

    6)独立的存储

    # 分别给三个 nginx 设置首页
    kubectl exec -it mysts-0 -- sh -c "echo 'mysts-0' > /usr/share/nginx/html/index.html"
    kubectl exec -it mysts-1 -- sh -c "echo 'mysts-1' > /usr/share/nginx/html/index.html"
    kubectl exec -it mysts-2 -- sh -c "echo 'mysts-2' > /usr/share/nginx/html/index.html"

     进入到 NFS 存储路径下,会发现三个文件夹,里面的 index.html 内容各不相同。

     六、动态扩容与缩容

    1)缩容

    StatefulSet 的扩缩容和 Demployment 一样,只需要修改 replica 副本数量就可以。

    就比如我们现在 mysts 是 3 个副本数,我们把它改成 2 个。

    kubectl edit sts mysts
    replica: 2 

    • 当删除 Pod 时,它们是逆序终止的,顺序为 {N-1..0}。所以 mysts-2 最先被删除。
    • 当删除 Pod 时,存储资源默认是保留的。PV、PVC 以及磁盘数据都会被保留。

    2)扩容

    将 replica 由 2 改为 3

    kubectl edit sts mysts
    replica: 3 

    新的 Pod 名称没变,但是 IP 已经变了。原来 10.244.9.62。

    访问这个 IP,之前创建的数据都在,而且能正确地挂载进来。

    七、更新策略

    kubectl explain sts.spec.updateStrategy

    有两种策略:RollingUpdate(默认)、OnDelete。

    1)RollingUpdate 滚动更新策略

    RollingUpdate 更新策略会更新一个 StatefulSet 中的所有 Pod,采用与序号索引相反的顺序并遵循 StatefulSet 的保证。你可以通过指定 .spec.updateStrategy.rollingUpdate.partition 将使用 RollingUpdate 策略的 StatefulSet 的更新拆分为多个分区。

    • 镜像准备
    ctr -n=k8s.io images pull docker.1ms.run/library/nginx:1.16.1
    ctr -n=k8s.io images tag docker.1ms.run/library/nginx:1.16.1 docker.io/library/nginx:1.16.1

    在一个终端窗口中对 mysts StatefulSet 执行 patch 操作来改变容器镜像:

    # 将 nginx:1.14.2 更新为 nginx:1.16.1
    kubectl patch statefulset mysts --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"nginx:1.16.1"}]'

    在另一个终端监控 StatefulSet 中的 Pod:

    kubectl get pod -l app=nginx --watch

    StatefulSet 里的 Pod 采用和序号相反的顺序更新。在更新下一个 Pod 前,StatefulSet 控制器终止每个 Pod 并等待它们变成 Running 和 Ready。 请注意,虽然在顺序后继者变成 Running 和 Ready 之前 StatefulSet 控制器不会更新下一个 Pod,但它仍然会重建任何在更新过程中发生故障的 Pod,使用的是它们现有的版本。

    •  查看容器镜像
    for p in 0 1 2; do kubectl get pod "mysts-$p" --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'; echo; done

    镜像版本都已被替换为 nginx:1.16.1。

    •  分区更新

    你可以使用 .spec.updateStrategy.rollingUpdate 中的 partition 字段对 StatefulSet 执行更新的分段操作。

     在一个终端窗口中对 mysts StatefulSet 执行 patch 操作来改变分区:

    # 分区从默认值 0->2
    kubectl patch sts mysts -p '{"spec":{"updateStrategy":{"rollingUpdate":{"maxUnavailable":0,"partition":2}}}}'

    再次 Patch StatefulSet 来改变此 StatefulSet 使用的容器镜像:

    # 将 nginx:1.16.1 变回 nginx:1.14.2
    kubectl patch statefulset mysts --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"nginx:1.14.2"}]'

    在另一个终端监控 StatefulSet 中的 Pod:

    kubectl get pod -l app=nginx --watch

    Pod 在更新的时候,只是更新了 mysts-2 这个 Pod。

    partition: 2 表示更新的时候把 Pod 序号大于等于 2 的进行更新。

    •   查看容器镜像
    for p in 0 1 2; do kubectl get pod "mysts-$p" --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'; echo; done

    •  StatefulSet 的金丝雀发布

     可以使用分区更新来实现 StatefulSet 简单的金丝雀发布,先把 partition 设置为 replica-1,然后更新镜像,这个时候序号最大的 Pod 就会被更新,同时观察这个 Pod 的日志和监控情况,如果运行平稳,再继续修改 partition 为 0:

    kubectl patch sts mysts -p '{"spec":{"updateStrategy":{"rollingUpdate":{"maxUnavailable":0,"partition":0}}}}'

    此时会触发其余 Pod 的滚动更新: 

    • 查看容器镜像
    for p in 0 1 2; do kubectl get pod "mysts-$p" --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'; echo; done

    此时所有的镜像版本都更新为 1.14.2 了。

    2)OnDelete 策略

    在一个终端窗口中对 mysts StatefulSet 执行 patch 操作,以使用 OnDelete 更新策略

    kubectl patch statefulset mysts -p '{"spec":{"updateStrategy":{"type":"OnDelete", "rollingUpdate": null}}}'

     再次 Patch StatefulSet 来改变此 StatefulSet 使用的容器镜像:

    # 将 nginx:1.14.2 更新为 nginx:1.16.1 
    kubectl patch statefulset mysts --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"nginx:1.16.1"}]'

     在另一个终端监控 StatefulSet 中的 Pod:

    此时终端没有任何变化。

    如果更新策略是 OnDelete,那不会自动更新 Pod,需要手动删除 Pod,重新创建的 Pod 才会实现更新。

    •  删除 mysts-2:
    kubectl delete pod mysts-2

    此时 StatefulSet 帮我们又重新创建了一个 mysts-2:

    此时 mysts-2 的镜像版本,已经更新为 1.16.1:

     其他两个,还是 1.14.2:

    本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2399106.html

    如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

    相关文章

    nav2笔记-250603

    合作背景&#xff1a; AMD与Open Navigation在过去几个月里进行了合作&#xff0c;旨在向ROS 2社区展示AMD强大的Ryzen AI、Embedded和Kria能力。 演示内容&#xff1a; 帖子提到&#xff0c;他们已经开始展示如何使用Ryzen AI为自主机器人产品提供动力&#xff0c;在各种现实世…

    指纹识别+精准化POC攻击

    开发目的 解决漏洞扫描器的痛点 第一就是扫描量太大&#xff0c;对一个站点扫描了大量的无用 POC&#xff0c;浪费时间 指纹识别后还需要根据对应的指纹去进行 payload 扫描&#xff0c;非常的麻烦 开发思路 我们的思路分为大体分为指纹POC扫描 所以思路大概从这几个方面…

    mac环境下的python、pycharm和pip安装使用

    Python安装 Mac环境下的python安装 下载地址&#xff1a;https://www.jetbrains.com.cn/pycharm/ 一直点击下一步即可完成 在应用程序中会多了两个图标 IDLE 和 Python launcher IDLE支持在窗口中直接敲python命令并立即执行&#xff0c;双击即可打开 Python launcher双击打…

    BUUCTF[极客大挑战 2019]Havefun 1题解

    BUUCTF[极客大挑战 2019]Havefun 1题解 题目分析解题理解代码逻辑&#xff1a;构造Payload&#xff1a; 总结 题目分析 生成靶机&#xff0c;进入网址&#xff1a; 首页几乎没有任何信息&#xff0c;公式化F12打开源码&#xff0c;发现一段被注释的源码&#xff1a; 下面我们…

    Tomcat优化篇

    目录 一、Tomcat自身配置 1.Tomcat管理页面 2. 禁用AJP服务 3.Executor优化 4.三种运行模式 5.web.xml 6.Host标签 7.Context标签 8.启动速度优化 9.其他方面 二、JMeter测试 笔者推荐 一、Tomcat自身配置 1.Tomcat管理页面 我们可以打开Tomcat的管理页面&#xff…

    Temporal Fusion Transformer(TFT)扩散模型时间序列预测模型

    1. TFT 简介 Temporal Fusion Transformer&#xff08;TFT&#xff09;模型是一种专为时间序列预测设计的高级深度学习模型。它结合了神经网络的多种机制处理时间序列数据中的复杂关系。TFT 由 Lim et al. 于 2019年提出&#xff0c;旨在处理时间序列中的不确定性和多尺度的依…

    【LangServe部署流程】5 分钟部署你的 AI 服务

    目录 一、LangServe简介 二、环境准备 1. 安装必要依赖 2. 编写一个 LangChain 可运行链&#xff08;Runnable&#xff09; 3. 启动 LangServe 服务 4. 启动服务 5. 使用 API 进行调用 三、可选&#xff1a;访问交互式 Swagger 文档 四、基于 LangServe 的 RAG 应用部…

    攻防世界-unseping

    进入环境 在获得的场景中发现PHP代码并进行分析 编写PHP编码 得到 Tzo0OiJlYXNlIjoyOntzOjEyOiIAZWFzZQBtZXRob2QiO3M6NDoicGluZyI7czoxMDoiAGVhc2UAYXJncyI7YToxOntpOjA7czozOiJwd2QiO319 将其传入 想执行ls&#xff0c;但是发现被过滤掉了 使用环境变量进行绕过 $a new…

    [yolov11改进系列]基于yolov11使用FasterNet替换backbone用于轻量化网络的python源码+训练源码

    【FasterNet介绍】 为了设计快速神经网络&#xff0c;许多工作都集中在减少浮点运算的数量&#xff08;FLOPs&#xff09;上。 然而&#xff0c;我们观察到FLOPs的减少并不一定会导致延迟的类似程度的减少。 这主要源于低效率的每秒浮点运算(FLOPS)。 为了实现更快的网络&#…

    一周学会Pandas2之Python数据处理与分析-Pandas2数据绘图与可视化

    锋哥原创的Pandas2 Python数据处理与分析 视频教程&#xff1a; 2025版 Pandas2 Python数据处理与分析 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili Pandas 集成了 Matplotlib&#xff0c;提供了简单高效的绘图接口&#xff0c;使数据可视化变得直观便捷。本指南将详…

    企业级安全实践:SSL/TLS 加密与权限管理(一)

    引言 ** 在数字化转型的浪潮中&#xff0c;企业对网络的依赖程度与日俱增&#xff0c;从日常办公到核心业务的开展&#xff0c;都离不开网络的支持。与此同时&#xff0c;网络安全问题也日益严峻&#xff0c;成为企业发展过程中不可忽视的重要挑战。 一旦企业遭遇网络安全事…

    2025——》VSCode Windows 最新安装指南/VSCode安装完成后如何验证是否成功?2025最新VSCode安装配置全攻略

    1.VSCode Windows 最新安装指南: 以下是 2025 年 Windows 系统下安装 Visual Studio Code(VSCode)的最新指南,结合官方文档与实际操作经验整理而成: 一、下载官方安装包: 1.访问官网: 打开浏览器,进入 VSCode 官方下载页面https://code.visualstudio.com/Download 2…

    【MATLAB代码】制导——三点法,二维平面下的例程|运动目标制导,附完整源代码

    三点法制导是一种导弹制导策略,主要用于确保导弹能够准确追踪并击中移动目标。该方法通过计算导弹、目标和制导站之间的相对位置关系,实现对目标的有效制导。 本文给出MATLAB下的三点法例程,模拟平面上捕获运动目标的情况订阅专栏后可直接查看源代码,粘贴到MATLAB空脚本中即…

    如何爬取google应用商店的应用分类呢?

    以下是爬取Google Play商店应用包名(package name)和对应分类的完整解决方案&#xff0c;采用ScrapyPlaywright组合应对动态渲染页面&#xff0c;并处理反爬机制&#xff1a; 完整爬虫实现 1. 安装必要库 # 卸载现有安装pip uninstall playwright scrapy-playwright -y# 重新…

    SQL Relational Algebra(数据库关系代数)

    目录 What is an “Algebra” What is Relational Algebra? Core Relational Algebra Selection Projection Extended Projection Product&#xff08;笛卡尔积&#xff09; Theta-Join Natural Join Renaming Building Complex Expressions Sequences of Assignm…

    智能工业时代:工业场景下的 AI 大模型体系架构与应用探索

    自工业革命以来&#xff0c;工业生产先后经历了机械化、电气化、自动化、信息化的演进&#xff0c;正从数字化向智能化迈进&#xff0c;人工智能技术是新一轮科技革命和产业变革的重要驱动力量&#xff0c;AI 大模型以其强大的学习计算能力掀开了人工智能通用化的序幕&#xff…

    易语言使用OCR

    易语言使用OCR 用易语言写个脚本&#xff0c;需要用到OCR&#xff0c;因此我自己封装了一个OCR到DLL。 http://lkinfer.1it.top/ 视频演示&#xff1a;https://www.bilibili.com/video/BV1Zg7az2Eq3/ 支持易语言、c、c#使用&#xff0c;平台限制&#xff1a;window 10 介绍…

    C++和C#界面开发方式的全面对比

    文章目录 C界面开发方式1. **MFC&#xff08;Microsoft Foundation Classes&#xff09;**2. **Qt**3. **WTL&#xff08;Windows Template Library&#xff09;**4. **wxWidgets**5. **DirectUI** C#界面开发方式1. **WPF&#xff08;Windows Presentation Foundation&#xf…

    算法-集合的使用

    1、set常用操作 set<int> q; //以int型为例 默认按键值升序 set<int,greater<int>> p; //降序排列 int x; q.insert(x); //将x插入q中 q.erase(x); //删除q中的x元素,返回0或1,0表示set中不存在x q.clear(); //清空q q.empty(); //判断q是否为空&a…

    性能优化 - 理论篇:CPU、内存、I/O诊断手段

    文章目录 Pre引言1. CPU 性能瓶颈1.1 top 命令 —— 多维度 CPU 使用率指标1.2 负载&#xff08;load&#xff09;——任务排队情况1.3 vmstat 命令 —— CPU 繁忙与等待 2. 内存性能瓶颈2.1 操作系统层面的内存分布2.2 top 命令 —— VIRT / RES / SHR 三个关键列2.3 CPU 缓存…