StatefulSet介绍¶
Pod根据是否有数据存储分为有状态和无状态:
- 无状态:指的Pod运行期间不会产生重要数据,即使有数据产生,这些数据丢失了也不影响整个应用。比如Nginx、Tomcat等应用适合无状态。
- 有状态:指的是Pod运行期间会产生重要的数据,这些数据必须要做持久化,比如MySQL、Redis、RabbitMQ等。
Deployment和Daemonset适合做无状态,而有状态也有一个对应的资源,那就是Statefulset(简称sts)。
说明:由于StatefulSet涉及到了数据持久化,用到了StorageClass,需要先创建一个基于NFS的StorageClass
对于Sts的Pod,有如下特点:
① Pod名固定有序,后缀从0开始;
② “域名”固定,这个“域名”组成: Pod名.Svc名,例如 redis-sts-0.redis-svc;
③ 每个Pod对应的PVC也是固定的;
关于Sts里的多个Pod之间的数据同步
K8s并不负责Sts里的Pod间数据同步, 具体的数据同步和一致性策略取决于我们部署的有状态应用程序。不同的应用程序可能使用不同的数据同步和一致性策略。例如,关系型数据库(如 MySQL)可能使用主-从复制,而分布式数据库(如 MongoDB)可能使用一种基于分区和副本的数据同步机制。
StatefulSet示例¶
准备工作
额外开一台虚拟机,搭建NFS服务。假设NFS服务器IP地址为192.168.1.34,共享目录为/data/nfs
1、每台机器安装NFS客户端
$ yum install nfs-utils -y
2、在k8s-node01(192.168.1.34)启动nfs
[root@k8s-node01 ~]# systemctl start nfs-server
在k8s-node01(192.168.1.34)查看nfs支持的版本
[root@k8s-node01 ~]# cat /proc/fs/nfsd/versions
-2 +3 +4 +4.1 +4.2
3、在k8s-node01(192.168.1.34)上创建一个共享目录
[root@k8s-node01 ~]# mkdir -p /data/nfs
4、在k8s-node01(192.168.134)编辑授权文件,这里网段根据自己主机来定,我这里网段是192.168.1.0/24
[root@k8s-node01 ~]# vim /etc/exports
/data/nfs/ 192.168.1.0/24(rw,sync,no_subtree_check,no_root_squash)
5、在k8s-node01(192.168.1.34)配置生效
[root@k8s-node01 ~]# exportfs -r
6、在k8s-node01(192.168.1.34)重新加载NFS
[root@k8s-node01 ~]# systemctl reload nfs-server
示例演示
要想使用NFS的sc,还需要安装一个NFS provisioner,它的作用是自动创建NFS的pv
1、下载源码
$ git clone https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner
2、修改命名空间为kube-system并创建rbac授权
$ cd nfs-subdir-external-provisioner/deploy
$ sed -i 's/namespace: default/namespace: kube-system/' rbac.yaml
$ kubectl apply -f rbac.yaml
查看创建情况
$ k get sa -n kube-system | grep nfs
nfs-client-provisioner 1 44s
3、修改并应用deployment.yaml
修改命名空间为kube-system
$ cd nfs-subdir-external-provisioner/deploy
$ sed -i 's/namespace: default/namespace: kube-system/' deployment.yaml
修改nfs服务器地址、nfs共享目录和镜像地址
$ cd nfs-subdir-external-provisioner/deploy
$ vim deployment.yaml

应用deployment.yaml
$ cd nfs-subdir-external-provisioner/deploy
$ k apply -f deployment.yaml,class.yaml
验证查看
$ k get deploy -n kube-system | grep nfs
nfs-client-provisioner 1/1 1 1 119s
$ k get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
nfs-client k8s-sigs.io/nfs-subdir-external-provisioner Delete Immediate false 3m30s
4、创建Sts和对应的service,并应用
创建Sts
$ vim redis-sts.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis-sts
namespace: kube-system
spec:
serviceName: redis-svc ##这里要有一个serviceName,Sts必须和service关联
volumeClaimTemplates:
- metadata:
name: redis-pvc
spec:
storageClassName: nfs-client
accessModes:
- ReadWriteMany
resources:
requests:
storage: 500Mi
replicas: 2
selector:
matchLabels:
app: redis-sts
template:
metadata:
labels:
app: redis-sts
spec:
containers:
- image: redis:6.2
name: redis
ports:
- containerPort: 6379
volumeMounts:
- name: redis-pvc
mountPath: /data
创建Sts对应的service
$ vim redis-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: redis-svc
namespace: kube-system
spec:
selector:
app: redis-sts
ports:
- port: 6379
protocol: TCP
targetPort: 6379
应用两个YAML文件
$ k apply -f redis-sts.yaml,redis-svc.yaml
验证查看
$ k get sts,svc -n kube-system
NAME READY AGE
statefulset.apps/redis-sts 2/2 41m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/redis-svc ClusterIP 10.0.47.3 <none> 6379/TCP 41m
5、测试
ping域名
$ k exec -it redis-sts-0 -- bash
$ apt-get update
$ apt-get install iputils-ping
$ ping redis-sts-0.redis-svc
$ ping redis-sts-1.redis-svc
创建key
$ k exec -it redis-sts-0 -n kube-system -- redis-cli
127.0.0.1:6379> set k1 'abc'
OK
127.0.0.1:6379> set k2 'bcd'
OK
127.0.0.1:6379> exit
模拟故障,删除后,它会自动重新创建同名Pod
$ k delete pod redis-sts-0 -n kube-system
再次进入查看redis key
$ k exec -it redis-sts-0 -n kube-system -- redis-cli
127.0.0.1:6379> get k1
"abc"
127.0.0.1:6379> get k2
"bcd"
6、恢复
$ cd /root/k8s
$ k delete -f redis-sts.yaml,redis-svc.yaml
$ cd /root/nfs-subdir-external-provisioner/deploy
$ k delete -f deployment.yaml,class.yaml