kubernetes云原生纪元:StatefulSet 状态守护者

介绍

​ 之前我们使用的编排对象,都是Deployment ,但是 Deployment并不能胜任所有的编排工作。之前的服务都是无状态pod无差别没有顺序性

但是实际场景并不是所有应用都具备这样的条件。特别是分布式应用,他们多个实例存在一些关系,比如主从关系 zk、etcd集群、redis主从…都有这样的特征。

对应这种多实例不对等的应用,kubernetes专门设计一种编排对象StatefulSet

StatefulSet抽象了两种有状态的场景,主要解决两个问题。

第一种:

多个 pod之间的顺序性,比如按一定顺序去启动,每个实例有自己的变化,再比如实例之间互相访问,互相通信,都可以根据顺序性特征去解决。

第二种:

持久存储区分,对应持久存储区分之前我们讲过在Deployment配置好持久存储区分的PVC,多个Pod之间就可以共享同一个目录了,一个Pod写入了文件,多个Pod都可以看到 。对于持久存储的区分是让Pod 之间对应同一个目录是保持私有的,每个pod 都有自己的数据,当然这种只针对共享存储的,如果没有共享存储的需求,天然他们就是区分开的。就不需要考虑这样的问题了。

不管你是mysql主从、redis主备、zookeeper集群、etcd集群都是依靠StatefulSet这两种特性实现的。

实践

验证顺序性

我们来看一个Service 定义,跟其他区别就是clusterIP: None,他是一个headlessService,它不会有Service的VIP,可以通过DNS的名字访问,返回的多个POD的IP列表。

headless-service.yaml

apiVersion: v1
kind: Service
metadata:
  name: springboot-web-svc
spec:
  ports:
  - port: 80
    targetPort: 8080
    protocol: TCP
  clusterIP: None
  selector:
    app: springboot-web

我们来看statefulSet 的配置,跟我们常用的Deployment区别不是很大,主要是 serviceName: springboot-web-svc,这个就是告诉StatefulSet用哪个headlessService去保证每个POD的解析

🔨 statefulset.yaml

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: springboot-web
spec:
  serviceName: springboot-web-svc # 通过Service名字指定headlessService
  replicas: 2 # 两个实例用于测试
  selector:
    matchLabels:
      app: springboot-web
  template:
    metadata:
      labels:
        app: springboot-web
    spec:
      containers:
      - name: springboot-web
        image: hub.zhang.com/kubernetes/spring-boot-demo:v1
        ports:
        - containerPort: 8080
        livenessProbe:
          tcpSocket:
            port: 8080
          initialDelaySeconds: 20
          periodSeconds: 10
          failureThreshold: 3
          successThreshold: 1
          timeoutSeconds: 5
        readinessProbe:
          httpGet: # 注意健康检查
            path: /hello?name=test
            port: 8080
            scheme: HTTP
          initialDelaySeconds: 20
          periodSeconds: 10
          failureThreshold: 1
          successThreshold: 1
          timeoutSeconds: 5

把上面的headless-servicestatefulset都创建

[root@master-001 ~]# kubectl apply -f headless-service.yaml
service/springboot-web-svc created
[root@master-001 ~]# kubectl apply -f statefulset.yaml
statefulset.apps/springboot-web created

监控查看下,先启动springboot-web-0 在启动的springboot-web-1

[root@master-001 ~]# kubectl get pod -l app=springboot-web -w
NAME               READY   STATUS    RESTARTS   AGE
springboot-web-0   1/1     Running   0          118s
springboot-web-1   1/1     Running   0          94s

Pod 命令规则

名字的区分springboot-web-0拆解开springboot-web是来自于StatefulSet的名字,-0,-1是按实例数来排序的,比如有三个实例那里就是-0-1-2,

启动顺序是先启动-0,启动成功才会创建启动-1的

image-20200202103619226

我们可以进入到springboot-web-0 和springboot-web-1容器里面会发现他们的hostname 跟他们的POD名字一样

image-20200202104110868

image-20200202104120584

我可以进入springboot-web-1容器通过名字ping springboot-web-0是否通

ping 的时候必须pod名后面要加headlessService的名字也就是springboot-web-svc

[root@node-002 ~]# docker exec -it f2 sh
/ # hostname
springboot-web-1
/ # ping springboot-web-0.springboot-web-svc
PING springboot-web-0.springboot-web-svc (192.168.143.204): 56 data bytes
64 bytes from 192.168.143.204: seq=0 ttl=63 time=0.283 ms
64 bytes from 192.168.143.204: seq=1 ttl=63 time=0.109 ms

dns默认搜索范围参考search default.svc.cluster.local svc.cluster.local cluster.local localdomain,我们在 default命名空间下就不需要加命名空间

/ # vi /etc/resolv.conf
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local localdomain
options ndots:5

我们在任何时候都可以通过名字加编号访问任何一个实例

验证持久存储区分

持久存储区分的前提是要有共享存储的服务,也就是说必须要有存储后端和PV PVC

先删除上面的我们在测试,这个与上面唯一的区别就是 volumeClaimTemplates创建PV的模版,为什么它这里不先指定一个PVC的名字呢,那样不就是共享的了嘛。但是我们先这里是存储区分开不符合我们当前的需求

volumeClaimTemplates创建PV的模版,下面的定义业余PVC非常相似

🤠 statefulset-volume.yaml

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: springboot-web
spec:
  serviceName: springboot-web-svc
  replicas: 2
  selector:
    matchLabels:
      app: springboot-web
  template:
    metadata:
      labels:
        app: springboot-web
    spec:
      containers:
      - name: springboot-web
        image: hub.mooc.com/kubernetes/springboot-web:v1
        ports:
        - containerPort: 8080
        livenessProbe:
          tcpSocket:
            port: 8080
          initialDelaySeconds: 20
          periodSeconds: 10
          failureThreshold: 3
          successThreshold: 1
          timeoutSeconds: 5
        readinessProbe:
          httpGet:
            path: /hello?name=test
            port: 8080
            scheme: HTTP
          initialDelaySeconds: 20
          periodSeconds: 10
          failureThreshold: 1
          successThreshold: 1
          timeoutSeconds: 5
        volumeMounts:
        - name: data
          mountPath: /mooc-data
  volumeClaimTemplates: # 唯一区别 volumeClaimTemplates创建PV的模版
  - metadata:
      name: data
    spec: # 跟PVC的配置非常相似
      accessModes:
      - ReadWriteOnce #读写权限
      storageClassName: glusterfs-storage-class # storageClass名字
      resources:
        requests:
          storage: 1Gi # 使用大小

创建一下

[root@master-001 ~]# kubectl apply -f statefulset-volume.yaml

然后快速查看下PVC,可以看到已经有两个PVC了

image-20200202111320246

再看下PV,也有两个PV

image-20200202111430867

这就代表他们的存储是分开的了,不会共享自己只能访问自己的

⛵️ 测试存储的区分

我们来到spring-boot-1 的容器里面再对应数据卷挂载目录写一个hello文件

image-20200202111752086

然后来到spring-boot-0 容器查看对应的数据卷挂载目录,很显然没有,这就符合存储区分的需求了

image-20200202111849066

同理在spring-boot-0 创建一个文件也不会在spring-boot-1有,每个人都访问自己的文件空间,文件不会共享

🥕 测试文件持久性

我们把这个两个容器删除,然后重新创建

[root@master-001 ~]# kubectl delete  -f statefulset-volume.yaml
[root@master-001 ~]# kubectl apply -f statefulset-volume.yaml

监控一下创建的状态-w

image-20200202112555025

我再进入spring-boot-1 的容器,进入数据卷挂载目录查看一下我们测试存储区分创建的文件还在吗?,很显然它在,证明文件是有持久化的。

image-20200202112756757

最后测试一个我们删除statefulset也就是statefulset-volume.yaml,看是否PV和PVC是否也被删除。

并没有删除PV和PVC,他只会自定生成PVC 并不会自动删除,如果确实不需要我们可以手动清除🆑掉。

image-20200202113101736

总结

Statefulset 管理POD,他不像deployment,它下面每个实例都是独立的个体有自己的编号有自己的hostname,并且kubernetes 通过 headless-service为这些有编号的pod生成同样DNS 的记录,只要POD名字不变DNS记录也不会变,可能变的只是PODIP,所以不能通过POD访问它。

如果POD对存储有持久性并且有独占的需要,就需要用到volumeClaimTemplates他会为每个POD自动生成一个PVC,保证每个POD都有自己独立的volume。

真实场景中用到肯定比现在复杂的多个,即用到顺序性也用到存储区分。关于Statefulset就说这么多。

更多推荐