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

StatefulSet示例-1

应用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