云计算百科
云计算领域专业知识百科平台

k8sday06深入控制器(2/3)

目录

StatefulSet(sts)

一、基础概念

二、和无状态应用控制器的区别

三、配置文件

1、举例

2、可能问题

3、验证成功

四、失败后做法

1、删除旧StatefulSet

2、删除旧PVC【可选,会导致原来数据丢失】

3、验证删除

五、扩容缩容

1、Scale命令

2、edit/patch/外部编辑器

六、更新策略

1、RollingUpdate(滚动更新)

①、简单滚动更新

②、金丝雀发布

2、OnDelete(删除更新)

​编辑

七、删除


在k8sday06的学习中我们已经学习了无状态应用常用的控制器——Deployment

在今天的学习中我将会继续学习有状态应用的控制器——StatefulSet

StatefulSet(sts)

一、基础概念

StatefulSet 是 Kubernetes 中用于管理有状态应用的控制器,它提供了有序的部署、扩展和更新机制,适用于需要持久化存储和稳定网络标识的场景。它为每个 Pod 提供稳定的网络身份和持久化存储。

  • 可对有状态服务器的DNS进行管理,实现稳定的网络标志 Headless Service

  • 可以对数据进行管理,实现持久化存储,避免数据丢失 VolumeClaim Template

  • 同时实现有序的恢复删除,伸缩扩容

二、和无状态应用控制器的区别

无状态应用不会对本地产生依赖,这样Deployment可以想删除就删除,想扩容缩容就扩容缩容,而有状态应用对本地产生依赖,依赖于上面的网络啊,文件啊等等,导致二者不同

特性StatefulSetDeployment
部署顺序 有序(pod-0 启动后才启动 pod-1) 无序(Pod 启动顺序不固定)
终止顺序 有序(pod-2 终止后才终止 pod-1) 无序(Pod 终止顺序不固定)
网络标识 稳定(如 statefulset-name-0) 动态(Pod 的 IP 地址动态分配)
持久化存储 支持(每个 Pod 有自己的数据卷 PVC) 不支持(Pod 的数据存储在临时存储中)
更新策略 支持滚动更新和分区更新 支持滚动更新
适用场景 数据库、消息队列、分布式存储 Web 服务器、微服务、无状态应用

三、配置文件

1、举例

以下给出一份基础的StatefulSet配置基本的MySQL部署的yaml创建文件,并给出每一步的解释:

注意只给出基本的配置,其他配置如探针探测啊,密文密码啊,重启策略等等,如有需要,自行配置~

apiVersion: apps/v1             # StatefulSet 的API版本
kind: StatefulSet               # 声明资源类型是 StatefulSet
metadata:                       # StatefulSet 的元信息
name: mysql-statefulset1      # 创建的 StatefulSet 的名称(自定义),注意全小写

spec:                           # StatefulSet 的规约(期望状态)
serviceName: "mysql"
 #指定一个 Headless Service 的名称(自定义),用于为每个 Pod 提供稳定的网络标识
replicas: 3                   #指定 StatefulSet 中 Pod 的副本数
selector:                     #用于选择 StatefulSet 管理的 Pod
  matchLabels:                #匹配标签
    app: mysql                #要求 Pod 的标签中包含 app: mysql(该键值对可自定义)
template:                     #定义 Pod 的模板
  metadata:                   # Pod 的元信息
    labels:                   #为 Pod 设置标签为 app: mysql
      app: mysql
  spec:                       # Pod的规约(期望状态)
    containers:
    – name: mysql             #容器的名称(自定义)
      image: mysql:latest     #容器使用的镜像,不写版本默认最新版,不推荐使用latest
      imagePullPolicy: IfNotPresent #拉取策略,本地有拉本地的,没有拉远处
      env:                    #定义环境变量
      – name: MYSQL_ROOT_PASSWORD #环境变量名称(自定义)
         #设置 MySQL 的 root 密码为 “123456”
        value: "123456"
      ports:                  #定义容器暴露的端口
      – containerPort: 3306   #容器内部端口3306
        name: mysql           #端口名称(自定义)
      volumeMounts:           #定义挂载的卷
      – name: mysql-data      #卷的名称(自定义)
        mountPath: /var/lib/mysql #卷在容器内的挂载路径
updateStrategy:               #更新策略,和 Pod 模板同级
  type: RollingUpdate         #更新策略类型,这里是滚动更新
  rollingUpdate:
    partition: 0              #滚动更新的分区,这里是从0开始更新
volumeClaimTemplates:         #定义每个 Pod 的持久化存储卷模板
– metadata:                   #数据卷模板的元信息
    name: mysql-data          #模板名称(自定义)
  spec:                       #数据卷模板的规约(期望状态)
     #定义卷的访问模式,这里是 ReadWriteOnce,表示卷只能被一个节点以读写模式挂载
    accessModes: ["ReadWriteOnce"]
    resources:                #资源配置请求与限制
      requests:               #最少需要的资源
        storage: 1Gi          #定义请求的存储大小为1GB

如果镜像标签是 :latest 或未指定标签,Kubernetes 会强制设置 imagePullPolicy: Always,即使你显式指定了 IfNotPresent,导致我的拉取策略即使设置了ifnotpresent仍会去docker hub拉取,而不用本地已经拉取好的。

不推荐使用latest,可能会带来意想不到的某些问题, 正确做法是使用明确版本号(如 nginx:1.25.3)或哈希值(如 nginx@sha256:abcd1234)

2、可能问题

当然在创建的时候可能会遇到在k8sday05提到的死活就是拉取不了镜像,但是已经通过docker pull拉取镜像成功了,这时候可以模仿上次的解决方法来解决

根本原因: Kind 默认使用 containerd 作为容器运行时,而 docker images 中的镜像 不会自动同步到 containerd**,导致 Kubernetes 无法找到镜像。

解决方案:将本地 Docker 镜像加载到 Kind 集群,绕过 Docker Hub 拉取,KinD 的节点本质是 Docker 容器,kind load 会将本地镜像导入到这些节点中,使 Pod 可直接使用。

kind load docker-image mysql:latest –name <你要拉取到的集群名称>

成功后可得到类似信息:

之后通过应用yaml文件创建StatefulSet即可:

kubectl apply -f <你自己的statefulset的配置文件名称>

3、验证成功

验证使用如下方法:

# 查看已创建的 statefulset
kubectl get statefulset

# 查看已有的 Pod ,如果创建成功会多出3个
kubectl get po

# 查看 PVC
kubectl get pvc

最终可得到类似信息即创建成功:

四、失败后做法

不论是拉取镜像失败,还是创建StatefulSet失败等等,只要是最终没有成功创建完成,都可以执行以下步骤重新来过:

1、删除旧StatefulSet

kubectl delete statefulset <你创建的statefulset的名称>

2、删除旧PVC【可选,会导致原来数据丢失】

# 注意下行代码的 -l app=mysql 是在yaml文件中配置用来匹配标签的
# 所以说,如果你自己修改了,记得应该写你自己设置的标签
kubectl delete pvc -l app=mysql

默认情况下,删除 StatefulSet 不会自动删除其相关的 PVC,如果你想删除可以进行操作

确保你的 PVC 标签与 StatefulSet 的标签一致!!!!

3、验证删除

kubectl get statefulset
kubectl get pvc
# 当然也可以加一个kubectl get po来查看副本pod是否创建

最终可以看到类似效果:

五、扩容缩容

StatefulSet的扩容和缩容和Deployment几乎完全相同,根本都是对replicas进行修改,操作方式也相同:

1、Scale命令

# 扩容
kubectl scale statefulset <你的statefulset名称> –replicas=<你期待的扩容的数量>

# 缩容
kubectl scale statefulset <你的statefulset名称> –replicas=<你期待的缩容的数量>

2、edit/patch/外部编辑器

通过直接对yaml文件进行修改达到扩容和缩容的效果

# 扩容
spec:
replicas:<你期待的扩容的数量>

# 缩容
spec:
replicas:<你期待的缩容的数量>

以上提供的两个方法都可以最终实现对StatefulSet的扩容和缩容的操作,最终可通过:

# 查看具体 StatefulSet
kubectl get statefulset <你的statefulset名称>
# 或者直接查看所有 Pod
kubectl get po
# 或者直接查看所有 StatefulSet
kubectl get sts

都可以查看到现在的Pod副本数量是否成功扩容和缩容

六、更新策略

1、RollingUpdate(滚动更新)

StatefulSet的滚动更新相较于Deployment的滚动更新会多一个有序的特定,即StatefulSet的滚动更新的按照你容器的创建顺序,从容器的最高序号开始进行更新,更新完一个删除一个,之后再进行下一个的更新,而Deployment的滚动更新是随机的,无序的。

维度Deployment 滚动更新StatefulSet 滚动更新
更新单位 任意 Pod 并行替换,无顺序要求 必须从最高序号开始逆序替换(pod-N → pod-N-1 …)
Pod 名字 随机后缀,更新后完全换新 Pod 名 保持 原来的名字 不变,只是重启+新模板
网络标识 IP 会变;Service 通过 label 选中新 Pod DNS 记录 (name-ordinal.svc.cluster.local) 稳定不变
PVC 数据 重新调度时 PVC 可能被其他 Pod 复用 每个序号专属 PVC,更新后仍挂载同一份数据
并发控制 maxSurge/maxUnavailable 可同时创建多 Pod 一次只动一个 Pod(无法并行)
分区更新 不支持 支持 partition,可实现 金丝雀/蓝绿 效果

滚动更新是一次性更新,而上方表格提到的金丝雀/蓝绿效果是StatefulSet较为特殊的部分,金丝雀发布(又称灰度发布)先部署少量新副本(如 2 个),把部分流量引过去做灰度验证,确保安全后,再 逐步放大 新副本数、缩小旧副本数,直到 100% 完成,而蓝绿部署则是需要两套环境,一键切换

维度蓝绿部署(Blue/Green)金丝雀发布(Canary)
核心思想 两套完全独立的环境(蓝/绿),流量一次性或瞬时切换 把新版本先暴露给少量用户,逐步放大流量比例
资源成本 高:需要 2 份全量资源 低:仅用增量副本
切换速度 秒级切换(改 Service Selector 或 Ingress 权重) 分钟级~小时级,按阶段放量
回滚难度 一键切回旧环境 调低流量比例或直接删除金丝雀副本即可
适用场景 对业务连续性要求极高、资源充足的系统 需要灰度验证、逐步放量的互联网服务
①、简单滚动更新

Pod 模板任何字段(env、volume、资源限制、标签等)发生变化,StatefulSet 就会按 有序滚动 策略逐个替换 Pod

template:
# ……
spec:
# ……
env:                    #定义环境变量
      – name: MYSQL_ROOT_PASSWORD #环境变量名称(自定义)
         #设置 MySQL 的 root 密码
        value: "123"         #"123456" ——> "123"

通过以下方法可查看有序滚动:

# 实时监控,记住修改完后就马上使用,或双开虚拟机查看
kubectl get pods -l app=mysql -w

# 可查看日志Events
kubectl describe sts <你自己的statefulset名称>

# 只想查看最终结果状态
kubectl rollout status statefulset/<你自己的statefulset名称>

# 或者直接查看是否全部READY
kubectl get po

部分结果类似如下图:

②、金丝雀发布

现在只更新序号大于等于partition的Pod

template:
# ……
spec:
# ……
env:                    #定义环境变量
      – name: MYSQL_ROOT_PASSWORD #环境变量名称(自定义)
         #设置 MySQL 的 root 密码
        value: "123456"         #"123" ——> "123456"
         
updateStrategy:                #更新策略,和 Pod 模板同级
  type: RollingUpdate        #更新策略类型,这里是滚动更新
  rollingUpdate:
    partition: 1             #"0" ——> "1"

之后保存并查看更新效果:

# 应用yaml文件
kubectl apply -f mysql-statefulset1.yaml

# 实时监控
kubectl get pods -l app=mysql -w

可得到类似效果:(当然由于我忘记双开,没有在刚应用文件的时候就查看,所以只监控到了部分)

当然也可以通过查看3个 Pod 容器的MYSQL的密码来验证是否只更新了>=partition(1)的容器:

# 查看第一个更新的容器
# 注意这里的mysql-statefulset1-2是我的最高序号的容器,你应该填写自己的
kubectl describe po mysql-statefulset1-2
# 查看剩下的容器
kubectl describe po mysql-statefulset1-1
kubectl describe po mysql-statefulset1-0

结果如图:

由此可见:是逆序滚动更新,且金丝雀发布只更新大于等于partition的容器

蓝绿部署现在不演示,在之后的内容再展开

2、OnDelete(删除更新)

StatefulSet的OnDelete表示:按正常来说Pod 模板任何字段(env、volume、资源限制、标签等)发生变化,StatefulSet 就会进行滚动更新,但是如果你将更新策略改为OnDelete,那么即使吗修改了Pod模板并保存yaml文件,你的容器也不会更新。只有当你删除容器,被你删除的容器就会进行更新并且立马重建。

在上一级的RollingUpdate中我已经更新了序号2和1的容器,序号为0的容器的MySQL密码还是123,接下来我将使用OnDelete来更新序号为0的容器,使它的MySQL密码变为123456

updateStrategy:               #更新策略,和 Pod 模板同级
  type: OnDelete            #更新策略类型,这里是删除更新

之后应用文件并删除序号为0的容器,你会发现,容器被删除后马上创建,再次进入容器,发现密码是123456

# 应用文件
kubectl apply -f mysql-statefulset1.yaml

# 删除序号为0的容器
# 注意这里的mysql-statefulset1-9是我的序号为0的容器,你应该填写自己的
kubectl delete po mysql-statefulset1-0

七、删除

删除大致可分为两种:「级联删除」(Cascading) 和 「非级联删除」(Orphan) 决定了“删控制器时,它管理的 Pod 容器怎么办”

维度级联删除(Cascading)非级联删除(Orphan / 非级联)
默认行为 默认(不额外加参数) 需要显式加 –cascade=orphan 或 –cascade=false
删除结果 控制器 + 所有 Pod 一并删除 只删控制器,Pod 变成孤儿(Orphan)继续运行
PVC/Service 不受影响;PVC 默认保留 同上
典型命令 kubectl delete sts mysql kubectl delete sts mysql –cascade=orphan
适用场景 快速清理整个应用 只想删控制器,保留业务 Pod 做调试或迁移
赞(0)
未经允许不得转载:网硕互联帮助中心 » k8sday06深入控制器(2/3)
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!