第9关 k8s架构师课程之有状态服务StatefulSet
文件转载自博客:https://www.bilibili.com/video/BV1kK4y1U7PQ?spm_id_from=333.999.0.0
https://www.toutiao.com/a6941713886601085471/?log_from=2e24852b1d691_1630849929667
这里一定要看视频
接下来先看这篇文章,再来看视频里面的文章
https://zhuanlan.zhihu.com/p/164771138
Statefulset
StatefulSet是为了解决有状态服务的问题,对应的Deployment和ReplicaSet是为了无状态服务而设计,其应用场景包括:
- 稳定的持久化存储,即Pod重新调度后还是能访问到相同的持久化数据,基于PVC来实现
- 稳定的网络标志,即Pod重新调度后其PodName和HostName不变,基于Headless Service(即没有Cluster IP的Service)来实现
- 有序部署,有序扩展,即Pod是有顺序的,在部署或者扩展的时候要依据定义的顺序依次依次进行(即从0到N-1,在下一个Pod运行之前所有之前的Pod必须都是Running和Ready状态),基于init containers来实现
- 有序收缩,有序删除(即从N-1到0)
因为statefulset要求Pod的名称是有顺序的,每一个Pod都不能被随意取代,也就是即使Pod重建之后,名称依然不变。为后端的每一个Pod去命名。
StatefulSet:Pod控制器。RC、RS、Deployment、DS。 无状态的服务。
template(模板):根据模板创建出来的Pod,它们的状态都是一模一样的(除了名称、IP、域名之外)可以理解为:任何一个Pod,都可以被删除,然后用新生成的Pod进行替换。
有状态的服务:需要记录前一次或者多次通信中的相关时间,以作为下一次通信的分类标准。比如:MySQL等数据库服务。(Pod的名称,不能随意变化。数据持久化的目录也是不一样,每一个Pod都有自己独有的数据持久化存储目录。)
每一个Pod-----对应一个PVC-----每一个PVC对应一个PV。
以自己的名称创建一个名称空间,以下所有资源都运行在此空间中。
用statefuset资源运行一个httpd web服务,要求3个Pod,但是每个Pod的主界面内容不一样,并且都要做专有的数据持久化,尝试删除其中一个Pod,查看新生成的Pod,是否数据与之前一致。
基于NFS服务,创建NFS服务。
[root@master ~]# yum -y install nfs-utils rpcbind br/>2.[root@master ~]# mkdir /nfsdata [root@master ~]# vim /etc/exports br/>4./nfsdata *(rw,sync,no_root_squash) [root@master ~]# systemctl start nfs-server.service [root@master ~]# systemctl start rpcbind br/>7.[root@master ~]# showmount -e Export list for master: ./nfsdata *
2.创建RBAC权限
vim rbac-rolebind.yaml
apiVersion: v1
kind: Namespace
metadata:
name: lbs-test
apiVersion: v1
kind: ServiceAccount 创建rbac授权用户。及定义权限
metadata:
name: nfs-provisioner
name:lbs-test
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: nfs-provisioner-runner
name:lbs-test
rules:
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["get", "list", "watch", "create", "delete"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list", "watch", "update"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["events"]
verbs: ["watch", "create", "update", "patch"]
- apiGroups: [""]
resources: ["services", "endpoints"]
verbs: ["get","create","list", "watch","update"]
- apiGroups: ["extensions"]
resources: ["podsecuritypolicies"]
resourceNames: ["nfs-provisioner
verbs: ["use"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: run-nfs-provisioner
subjects:
- kind: ServiceAccount
name: nfs-provisioner
namespace: lbs-test 如没有名称空间需要添加这个default默认否则报错
roleRef:
kind: ClusterRole
name: nfs-provisioner-runner
apiGroup: rbac.authorization.k8s.io
执行yaml文件:
[root@master yaml]# kubectl apply -f rbac-rolebind.yaml
namespace/lbh-test created
serviceaccount/nfs-provisioner created
clusterrole.rbac.authorization.k8s.io/nfs-provisioner-runner created
clusterrolebinding.rbac.authorization.k8s.io/run-nfs-provisioner created
3.创建Deployment资源对象
[root@master yaml]# vim nfs-deployment.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nfs-client-provisioner
name:lbs-test
spec:
replicas: 1#副本数量为1
strategy:
type: Recreate#重置
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
serviceAccount: nfs-provisioner#指定账户
containers:
- name: nfs-client-provisioner
image: registry.cn-hangzhou.aliyuncs.com/open-ali/nfs-client-provisioner使用的是这个镜像。
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes#指定容器内的挂载目录
env:
- name: PROVISIONER_NAME#容器内置变量
value: bdqn#这是变量的名字
- name: NFS_SERVER
value: 192.168.1.1
- name: NFS_PATH#指定Nfs的共享目录
value: /nfsdata
volumes:#指定挂载到容器内的nfs路径与IP
- name: nfs-client-root
nfs:
server: 192.168.1.1
path: /nfsdata
执行yaml文件,查看Pod
4.创建Storageclass资源对象(sc):
root@master yaml]# vim sc.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: sc-nfs
namespace:lbs-test #名称空间 名
provisioner: lbs-test#与deployment资源的env环境变量value值相同
reclaimPolicy: Retain #回收策略
执行yaml文件,查看SC
[root@master yaml]# kubectl apply -f sc.yaml
storageclass.storage.k8s.io/sc-nfs created
[root@master yaml]# kubectl get sc -n lbs-test
NAME PROVISIONER AGE
sc-nfs lbs-test 8s
上面不清楚的请查看视频

https://space.bilibili.com/314330254/video?tid=0&page=1&keyword=&order=pubdate
接下来我们就创建stateful服务,不用收到创建pv和pvc,使用storegeclass帮我们自动创建pv和pvc
5.创建StatefulSet资源对象,自动创建PVC:
vim statefulset.yaml apiVersion: v1 kind: Service metadata: name: headless-svc namespace: lbs-test labels: app: headless-svc spec: ports: - port: 80 name: myweb selector: app: headless-pod clusterIP: None --- apiVersion: apps/v1 kind: StatefulSet metadata: name: statefulset-test namespace: lbs-test spec: serviceName: headless-svc replicas: 3 selector: matchLabels: app: headless-pod template: metadata: labels: app: headless-pod spec: containers: - image: httpd name: myhttpd ports: - containerPort: 80 name: httpd volumeMounts: - mountPath: /mnt name: test volumeClaimTemplates: 这个字段:自动创建PVC - metadata: name: test annotations: //这是指定storageclass,名称要一致 volume.beta.kubernetes.io/storage-class: sc-nfs spec: accessModes: - ReadWriteOnce resources: requests: storage: 100Mi
上面标注成黄色的都是必不可少的
无头服务创建的service的集群IP是不存在的,这里设置为null
clusterIP: None
第二个deployment编排文件的类型设置为kind: StatefulSet
第三个在创建的pod中必须收到指定serviceName:
serviceName: headless-svc
第四个我们要收到创建pvc和pv,需要使用
volumeClaimTemplates,在
volumeClaimTemplates中storage-class: sc-nfs必须和上面创建的名称保证一样
kind: StorageClass
metadata:
name: sc-nfs
执行yaml文件,查看Pod:
[root@master yaml]# kubectl apply -f statefulset.yaml
service/headless-svc created
statefulset.apps/statefulset-test created
[root@master yaml]# kubectl get pod -n lbs-test
NAME READY STATUS RESTARTS AGE
nfs-client-provisioner-5d88975f6d-wdbnc 1/1 Running 0 22m
statefulset-test-0 1/1 Running 0 8m59s
statefulset-test-1 1/1 Running 0 2m30s
statefulset-test-2 1/1 Running 0 109s
这里我们看到创建的pod都是有规则的命名的,如
statefulset-test-0、
statefulset-test-1、
statefulset-test-2
有序部署,有序扩展,即Pod是有顺序的,在部署或者扩展的时候要依据定义的顺序依次依次进行(即从0到N-1,在下一个Pod运行之前所有之前的Pod必须都是Running和Ready状态),基于init containers来实现
并且每个pod都固定访问对应的pvc,pvc是固定不变的,当pod重启之后也是访问相同的pvc,稳定的持久化存储,即Pod重新调度后还是能访问到相同的持久化数据,基于PVC来实现
查看是否自动创建PV及PVC
PV:
[root@master yaml]# kubectl get pv -n lbs-test
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pvc-0454e9ad-892f-4e39-8dcb-79664f65d1e5 100Mi RWO Delete Bound lbh-test/test-statefulset-test-2 sc-nfs 4m23s
pvc-2cb98c60-977f-4f3b-ba97-b84275f3b9e5 100Mi RWO Delete Bound lbh-test/test-statefulset-test-0 sc-nfs 11m
pvc-99137753-ccd0-4524-bf40-f3576fc97eba 100Mi RWO Delete Bound lbh-test/test-statefulset-test-1 sc-nfs 5m4s
PVC:
[root@master yaml]# kubectl get pvc -n lbs-test
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
test-statefulset-test-0 Bound pvc-2cb98c60-977f-4f3b-ba97-b84275f3b9e5 100Mi RWO sc-nfs 13m
test-statefulset-test-1 Bound pvc-99137753-ccd0-4524-bf40-f3576fc97eba 100Mi RWO sc-nfs 6m42s
test-statefulset-test-2 Bound pvc-0454e9ad-892f-4e39-8dcb-79664f65d1e5 100Mi RWO sc-nfs 6m1s
这里我们可以看到pvc的命名规则:test是volumeClaimTemplates的名称
statefulset-test是statefulset的名称,这个名称是固定的,如果要手动创建pvc必须按照这个方式命名
0 1 2是对应的pod启动的顺序
查看是否创建持久化目录:
[root@master yaml]# ls /nfsdata/
lbh-test-test-statefulset-test-0-pvc-2cb98c60-977f-4f3b-ba97-b84275f3b9e5
lbh-test-test-statefulset-test-1-pvc-99137753-ccd0-4524-bf40-f3576fc97eba
lbh-test-test-statefulset-test-2-pvc-0454e9ad-892f-4e39-8dcb-79664f65d1e5
6.在pod资源内创建数据。并访问测试。
[root@master yaml]# cd /nfsdata/
[root@master nfsdata]# echo 111 > lbs-test-test-statefulset-test-0-pvc-2cb98c60-977f-4f3b-ba97-b84275f3b9e5/index.html
[root@master nfsdata]# echo 222 > lbs-test-test-statefulset-test-1-pvc-99137753-ccd0-4524-bf40-f3576fc97eba/index.html
[root@master nfsdata]# echo 333 > lbs-test-test-statefulset-test-2-pvc-0454e9ad-892f-4e39-8dcb-79664f65d1e5/index.html
[root@master nfsdata]# kubectl get pod -o wide -n lbs-test
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nfs-client-provisioner-5d88975f6d-wdbnc 1/1 Running 0 30m 10.244.2.2 node02 <none> <none>
statefulset-test-0 1/1 Running 0 17m 10.244.1.2 node01 <none> <none>
statefulset-test-1 1/1 Running 0 10m 10.244.2.3 node02 <none> <none>
statefulset-test-2 1/1 Running 0 9m57s 10.244.1.3 node01 <none> <none>
[root@master nfsdata]# curl 10.244.1.2
111
[root@master nfsdata]# curl 10.244.2.3
222
[root@master nfsdata]# curl 10.244.1.3
333
7.删除其中一个pod,查看该pod资源的数据是否会重新创建并存在。
[root@master ~]# kubectl get pod -n lbs-test
NAME READY STATUS RESTARTS AGE
nfs-client-provisioner-5d88975f6d-wdbnc 1/1 Running 0 33m
statefulset-test-0 1/1 Running 0 20m
statefulset-test-1 1/1 Running 0 13m
statefulset-test-2 1/1 Running 0 13m
[root@master ~]# kubectl delete pod -n lbs-test statefulset-test-0
pod "statefulset-test-0" deleted
删除后会重新创建pod资源
[root@master ~]# kubectl get pod -n lbs-test -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nfs-client-provisioner-5d88975f6d-wdbnc 1/1 Running 0 35m 10.244.2.2 node02 <none> <none>
statefulset-test-0 1/1 Running 0 51s 10.244.1.4 node01 <none> <none>
statefulset-test-1 1/1 Running 0 15m 10.244.2.3 node02 <none> <none>
statefulset-test-2 1/1 Running 0 14m 10.244.1.3 node01 <none> <none>
数据依旧存在
[root@master ~]# curl 10.244.1.4
111
[root@master ~]# cat /nfsdata/lbs-test-test-statefulset-test-0-pvc-2cb98c60-977f-4f3b-ba97-b84275f3b9e5/index.html
111
StatefulSet资源对象,针对有状态的服务的数据持久化测试完成。 通过测试,即使删除Pod,重新生成调度后,依旧能访问到之前的持久化数据
相当的经典呀。
如果我们不使用storageClass自动创建pv和pvc,如果要收到的创建pv和pvc,那么pvc的命名必须满足规范,我们来看下下面的要求
不清楚的可以看博客
https://www.toutiao.com/a6941713886601085471/?log_from=2e24852b1d691_1630849929667
1、创建两个pod、创建两个pv、创建两个pvc
1、创建pv
-------------------------------------------
root@node1:~# cat web-pv.yaml
# mkdir -p /nfs_dir/{web-pv0,web-pv1}
apiVersion: v1
kind: PersistentVolume
metadata:
name: web-pv0
labels:
type: web-pv0
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: my-storage-class
nfs:
path: /nfs_dir/web-pv0
server: 10.0.1.201
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: web-pv1
labels:
type: web-pv1
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: my-storage-class
nfs:
path: /nfs_dir/web-pv1
server: 10.0.1.201
2、创建pvc(这一步可以省去让其自动创建,这里手动创建是为了让大家能更清楚在sts里面pvc的创建过程)
-------------------------------------------
这一步非常非常的关键,因为如果创建的PVC的名称和StatefulSet中的名称没有对应上,
那么StatefulSet中的Pod就肯定创建不成功.
我们在这里创建了一个叫做www-web-0和www-web-1的PVC,这个名字是不是很奇怪,
而且在这个yaml里并没有提到PV的名字,所以PV和PVC是怎么bound起来的呢?
是通过labels标签下的key:value键值对来进行匹配的,
我们在创建PV时指定了label的键值对,在PVC里通过selector可以指定label。
然后再回到这个PVC的名称定义:www-web-0,为什么叫这样一个看似有规律的名字呢,
这里需要看看下面创建StatefulSet中的yaml,
首先我们看到StatefulSet的name叫web,设置的replicas为2个,
volumeMounts和volumeClaimTemplates的name必须相同,为www,
所以StatefulSet创建的第一个Pod的name应该为web-0,第二个为web-1。
这里StatefulSet中的Pod与PVC之间的绑定关系是通过名称来匹配的,即:
PVC_name = volumeClaimTemplates_name + "-" + pod_name
www-web-0 = www + "-" + web-0
www-web-1 = www + "-" + web-1
root@node1:~# cat web-pvc.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: www-web-0
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: my-storage-class
selector:
matchLabels:
type: web-pv0
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: www-web-1
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: my-storage-class
selector:
matchLabels:
type: web-pv1
3、创建Service 和 StatefulSet
-------------------------------------------
在上一步中我们已经创建了名为www-web-0的PVC了,接下来创建一个service和statefulset,
service的名称可以随意取,但是statefulset的名称已经定死了,为web,
并且statefulset中的volumeClaimTemplates_name必须为www,volumeMounts_name也必须为www。
只有这样,statefulset中的pod才能通过命名来匹配到PVC,否则会创建失败。
root@node1:~# cat web.yaml
apiVersion: v1
kind: Service
metadata:
name: web-headless
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
selector:
matchLabels:
app: nginx # has to match .spec.template.metadata.labels
serviceName: "web-headless"
replicas: 2 # by default is 1
template:
metadata:
labels:
app: nginx # has to match .spec.selector.matchLabels
spec:
terminationGracePeriodSeconds: 10
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "my-storage-class"
resources:
requests:
storage: 1Gi
我们在做测试的时候,可以启动一个busybox的容器,在容器内部使用域名来访问无头服务
上面的创建成功之后会启动两个web容器
可以启动一个busybox的容器,在容器内部使用域名来访问无头服务下对应的pod:k8s为service资源分配了DNS名称,通过DNS名称可以访问到service对用的pod。而通过statefulset创建的pod,并依赖service headless能实现给statefulset管理的pod提供固定的DNS名称pod-name.service-headless-name.namespace.svc.cluster-domain.example,具体样例:
域名构成如下web-0是访问的pod的名称,web-headless是对应的service-headless-name的名称,web是对应的namespace,剩余的svc.cluster-domain.example默认可以不写
statefulset
posted on 2021-09-05 22:38 luzhouxiaoshuai 阅读(178) 评论(0) 编辑 收藏 举报
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
2019-09-05 spring-boot admin的使用