在当今的云原生计算环境中,Kubernetes已经成为容器编排的事实标准。随着微服务架构的普及,开发者们需要一种方法来管理有状态的服务,这些服务需要持久化存储、唯一网络标识和有序部署。这就是Kubernetes StatefulSet的用武之地。
StatefulSet是Kubernetes中的一种控制器,它专门设计用来管理有状态的应用。与传统的无状态应用不同,有状态应用需要跟踪它们的状态,这通常涉及到持久化存储和网络标识。StatefulSet提供了一种机制,确保每个Pod都有一个持久的标识符,并且可以以一种可预测的方式进行扩展和收缩。
以一个实验来展现一下statefulset控制器的一些特点
k8s-master rockylinux9.4系统 |
192.168.8.128 |
k8s-node1 rockylinux9.4系统 | 192.168.8.129 |
k8s-node2 rockylinux9.4系统 | 192.168.8.130 |
文章中需要的一些镜像大家都可以找一个docker主机,配置好加速器之后
用docker pull 进行拉取
例如拉取1.24版本的nginx: docker pull nginx:1.24
之后再docker save -o 打包的文件名字.tar nginx:1.24
打包完之后传到k8snode节点上面 ctr -n k8s.io image import 打包的文件名字.tar 进行导入镜像,或者直接将镜像打个harbor的ip标签传到harbor上面
一: 编写statefulset yaml文件
vim test.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: nginx-test
namespace: sts # 定义命名空间
spec:
replicas: 3 # 定义StatefulSet中Pod副本的数量
serviceName: statefulset-service # 定义StatefulSet服务的名称
selector:
matchLabels:
app: nginx # 定义选择器,用于匹配模板中定义的标签
template:
metadata:
labels:
app: nginx # 定义模板标签对应上面的matchLabels
spec:
containers:
- name: nginx
image: nginx:1.24
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
volumeMounts:
- mountPath: /usr/share/nginx/html/ # 定义挂载路径
name: nginx-volume # 定义挂载的卷名称
volumeClaimTemplates:
- metadata:
name: nginx-volume # 定义持久卷声明的名称对应上面的volumeMounts.name
spec:
storageClassName: "nfs" # 定义存储类名称
accessModes: ["ReadWriteOnce"] # 定义卷的访问模式
resources:
requests:
storage: 1Gi
persistentVolumeClaimRetentionPolicy:
whenDeleted: Delete
# 当StatefulSet被删除时,对应的PVC也会被删除
whenScaled: Retain
# 当StatefulSet缩容时,保留对应的PVC
---
apiVersion: v1
kind: Service
metadata:
name: statefulset-web
labels:
app: nginx
namespace: sts # 定义Service所在的命名空间
spec:
ports:
- port: 80
name: web
clusterIP: None # 定义Service的类型为Headless Service
selector:
app: nginx # 定义Service选择器,用于匹配StatefulSet中Pod的标签
yaml文件中的volumeClaimTemplates这个字段就是卷模板的意思,后面创建的pod挂载的卷都是基于这个模板创建出来的卷进行挂载的,这个模板中定义的就是我们上个文章的nfs storageclass
kubectl apply -f test.yaml
kubectl get svc -n sts #查看sts命名空间下的service
kubectl get pod -n sts #查看sts命名空间下的pod
可以看到sts创建的pod都是具有序号的
二:测试
statefulset控制器和deployment控制器不同:
2.1 比如我们删除一个pod nginx-test-1
kubectl -n sts delete pod nginx-test-1
重新创建出来的pod还是这个名字,这样就保持了一致性
2.2 pvc回收策略
yaml文件中定义的
测试一下
kubectl get pvc -n sts
可以看到在sts命名空间下有三个pvc,这三个pvc就是对应的sts命名空间中的那三个pod
可以看到我们删除了sts命名空间下的nginx-test这个statefulset这个控制器的资源后,对应的pvc也会被删除。
现在我们重新把pod创建出来,再测试一下pod缩容的时候会不会删除对应的pvc
可以看到pvc和pod都已经创建出来了,现在将test.yaml中的副本数修改成1:replicas: 1
再来创建一下 kubectl apply -f test.yaml
可以看到pvc没有被删除,pod已经被成功缩容到1个
三: Headless Service
我们在yaml文件中定义了 service 其中的Cluster IP是none,这个就是表示没有ip 地址通过域名的方式来访问pod
我们先创建一个有一个nslookup命令的pod
vim pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: web
labels:
app: web
namespace: sts
spec:
containers:
- name: caddy
image: docker.io/library/caddy:v1
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
默认这个caddy镜像里面就有nslookup命令,镜像可以直接用docker pull caddy拉取
可以看到通过域名解析的方式也是成功解析到了statefulset创建的三个pod的ip地址
默认的service的域名是 <servicename>.<namespace>.svc.<clusterdomain>
其中,servicename为service名称,namespace为service所处的命名空间,clusterdomain是k8s集群设计的域名后缀,默认为cluster.local。
四:更新策略
statefulset的更新策略是基于序号的方式进行更新的,就是pod后面跟着的数字
在yaml文件中加入这些内容
updateStrategy:
rollingUpdate:
maxUnavailable: 0
partition: 1
在Kubernetes的StatefulSet资源中,updateStrategy
定义了StatefulSet如何进行更新。StatefulSet支持两种更新策略:RollingUpdate
和OnDelete
。OnDelete
是默认策略,意味着StatefulSet仅在您手动删除Pod时才会更新Pod。而RollingUpdate
则允许StatefulSet控制器自动更新Pod。
maxUnavailable:0 表示系统可以处于不可用状态的最大Pod数量。这里的值0
意味着在更新过程中不允许有Pod不可用
partition:1 表示以序号为1的pod进行更新
将image换成nginx:1.26
kubectl apply -f test.yaml
kubectl get pod -n sts -w #-w表示动态查看pod
可以看到pod的更新策略生效了,没有动nginx-test-0,只更新了序号大于等于的pod
并且是等待一个Pod更新并运行成功后,才会继续更新下一个Pod。
验证一下版本
kubectl -n sts exec -it nginx-test-0 -- /bin/bash -c "nginx -v"
kubectl -n sts exec -it nginx-test-1 -- /bin/bash -c "nginx -v"
kubectl -n sts exec -it nginx-test-2 -- /bin/bash -c "nginx -v"
可以看到nginx-test-1和2都更新到了1.26版本,nginx-test-0没动还是nginx1.24版本。
再来测一下OnDelete这个更新策略
将test.yaml中updataStratrgy这里修改成这样
修改镜像
这个sjl-nginx:v1镜像里面封装的是nginx1.27版本
kubectl apply -f test.yaml
再查看
可以看到还是没有改变,是因为ondelete这个策略是在删除pod之后再进行更新的
删除一下nginx-test-0再查看版本
可以看到删除完重新创建出来的pod就会被更新成1.27版本