目录
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可以想删除就删除,想扩容缩容就扩容缩容,而有状态应用对本地产生依赖,依赖于上面的网络啊,文件啊等等,导致二者不同
部署顺序 | 有序(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的滚动更新是随机的,无序的。
更新单位 | 任意 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% 完成,而蓝绿部署则是需要两套环境,一键切换
核心思想 | 两套完全独立的环境(蓝/绿),流量一次性或瞬时切换 | 把新版本先暴露给少量用户,逐步放大流量比例 |
资源成本 | 高:需要 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 容器怎么办”
默认行为 | 默认(不额外加参数) | 需要显式加 –cascade=orphan 或 –cascade=false |
删除结果 | 控制器 + 所有 Pod 一并删除 | 只删控制器,Pod 变成孤儿(Orphan)继续运行 |
PVC/Service | 不受影响;PVC 默认保留 | 同上 |
典型命令 | kubectl delete sts mysql | kubectl delete sts mysql –cascade=orphan |
适用场景 | 快速清理整个应用 | 只想删控制器,保留业务 Pod 做调试或迁移 |
评论前必须登录!
注册