目录

拓扑状态

存储状态

总结


上一篇文章中,我们讲了deployment的编排技术,也提到了这种编排技术只能编排无状态的pod。但是在我们实际生产环境中,系统复杂很多。比如分布式系统,pod之间往往有依赖关系。再比如mysql数据库,主从节点需要通过binlog同步数据,读写请可能要求发送到不同节点上。对这种有状态的应用,kubernete的解决方案是StatefulSet。

StatefulSet的解决方案是把有状态应用的状态抽象成2种状态,拓扑状态和存储状态,把这些状态记录下来,pod重新创建后,帮助新pod恢复出这些状态。拓扑状态主要是就是有类似主从关系的应用,在启动或者升级发布的时候,pod的启动顺序都要固定,比如主节点先启动,从节点后启动。存储状态就是指不同应用的存储数据要固定,比如多个pod的服务重启后各自读取到的存储数据跟重启之前一样。

拓扑状态

在之前的文章《kubernete架构体系介绍》中提到过,kubernete为每一个pod绑定一个service服务,service服务作为pod的代理访问入口,配置的IP等地址信息是固定不变的,这样即使pod重启后IP地址发生了变化,只要service的ip地址不变,可以不用关心。
外部通过service代理访问pod,必须先访问service,这无非就是2种方式,直接访问service的ip地址或通过域名访问。如下图所示:

StatefulSet在编排上的一个创新是外部应用访问pod的时候,不用再通过访问service的ip地址或者域名,而是直接访问pod的域名来访问pod,而service的名字绑定在这个pod中对pod的启动顺序进行控制。域名的格式如下:

<pod-name>.<svc-name>.<namespace>.svc.cluster.local

这种不提供域名或ip地址的service被称作Headless Service,本质就是yaml中定义的clusterIP是None,yaml文件(HeadlessService.yaml)如下:

apiVersion: v1
kind: Service
metadata:name: bootservicelabels:app: bootservice
spec:ports:- port: 80name: webclusterIP: Noneselector:app: springboot

上面的service就会控制selector选择出的携带标签app: springboot的pod。而我们重新改写之前基于deployment的yaml文件,改为基于StatefulSet的文件,文件名springboot-mybatis-statefulset.yaml内容如下:

apiVersion: apps/v1
kind: StatefulSet
metadata:name: bootstatefulset
spec:serviceName: "bootservice"selector:matchLabels:app: springbootreplicas: 2template:metadata:labels:app: springbootspec:containers:- name: spingboot-mybatisimagePullPolicy: IfNotPresentimage: zjj2006forever/springboot-mybatis:1.3ports:- containerPort: 8300

相比于之前基于deployment的文件,除了修改了kind为StatefulSet和pod名称之外,它多了一个serviceName字段,就是指定代理自己的headless service的名字。
我们首先执行下面命令创建这个service

kubectl create -f HeadlessService.yaml 

创建成功后可以查看这个service,CLUSTER-IP为None

[root@master k8s]# kubectl get service
NAME          TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
bootservice   ClusterIP   None         <none>        80/TCP    11s
kubernetes    ClusterIP   10.96.0.1    <none>        443/TCP   14d

接着我们创建StatefulSet,命令如下:

kubectl create -f springboot-mybatis-statefulset.yaml

成功后我们查看刚刚创建的StatefulSet

[root@master k8s]# kubectl get StatefulSet
NAME              READY   AGE
bootstatefulset   2/2     7s

注意,创建StatefulSet的时候,我们可以执行下面的命令查看pod创建过程,从输出可以看到,StatefulSet会先创建出一个pod并且编号为0,启动成功后才会开始创建第二个pod,编号为1,这样就固定了pod的创建顺序

[root@master k8s]# kubectl get pods -w -l app=springboot
NAME                READY   STATUS    RESTARTS   AGE
bootstatefulset-0   0/1     Pending   0          0s
bootstatefulset-0   0/1     Pending   0          0s
bootstatefulset-0   0/1     ContainerCreating   0          0s
bootstatefulset-0   1/1     Running             0          3s
bootstatefulset-1   0/1     Pending             0          0s
bootstatefulset-1   0/1     Pending             0          0s
bootstatefulset-1   0/1     ContainerCreating   0          0s
bootstatefulset-1   1/1     Running             0          2s

我们进入pod中查看hostname可以发现跟pod名字一样

[root@master k8s]# kubectl exec -it bootstatefulset-0 -- /bin/sh
/ # hostname
bootstatefulset-0

我们ping上面提到的域名,输出正是容器中的IP地址

/ # ping bootstatefulset-0.bootservice.default.svc.cluster.local
PING bootstatefulset-0.bootservice.default.svc.cluster.local (10.244.1.41): 56 data bytes
64 bytes from 10.244.1.41: seq=0 ttl=64 time=0.419 ms
64 bytes from 10.244.1.41: seq=1 ttl=64 time=0.065 ms
64 bytes from 10.244.1.41: seq=2 ttl=64 time=0.071 ms

nslookup看一下,解析正常,结果如下:

/ # nslookup  bootstatefulset-0.bootservice.default.svc.cluster.local
Name:      bootstatefulset-0.bootservice.default.svc.cluster.local
Address 1: 10.244.1.41 bootstatefulset-0.bootservice.default.svc.cluster.local

从上面的输出可以看出,stateful为pod生成的域名生效了。之后如果我们删除第一个bootstatefulset-0会发生什么呢?
执行下面删除命令后查看pod变化状态

kubectl delete pod bootstatefulset-0

可以看到bootstatefulset-0这个pod被删除后重新创建出来,并且编号没有变化。

[root@master kubernetes]# kubectl get pods -w -l app=springboot
NAME                READY   STATUS    RESTARTS   AGE
bootstatefulset-0   1/1     Running   0          44m
bootstatefulset-1   1/1     Running   0          44m
bootstatefulset-0   1/1     Terminating   0          44m
bootstatefulset-0   0/1     Terminating   0          44m
bootstatefulset-0   0/1     Terminating   0          44m
bootstatefulset-0   0/1     Terminating   0          44m
bootstatefulset-0   0/1     Pending       0          0s
bootstatefulset-0   0/1     Pending       0          0s
bootstatefulset-0   0/1     ContainerCreating   0          0s
bootstatefulset-0   1/1     Running             0          1s

再次进入bootstatefulset-0中ping域名,发现pod的ip地址发生了变化,但是域名正常访问,这也说明访问pod是必须使用域名而不能直接用ip地址访问

[root@master k8s]# kubectl exec -it bootstatefulset-0 -- /bin/sh
/ # ping bootstatefulset-0.bootservice.default.svc.cluster.local
PING bootstatefulset-0.bootservice.default.svc.cluster.local (10.244.1.46): 56 data bytes
64 bytes from 10.244.1.46: seq=0 ttl=64 time=0.158 ms
64 bytes from 10.244.1.46: seq=1 ttl=64 time=0.543 ms

这样StatefulSet就用pod name+编号的方式为pod的启动和升级发布固定了顺序,在主从关系情况下也能保证主节点先启动,从节点后启动。同时节点暴露的域名是固定的,外部服务需要通过域名访问。

存储状态

StatefulSet对存储的解决方案,是引入了Persistent Volume Claim和Persistent Volume,简称PVC和PV。
下面就是一个PVC的定义,这个PVC定义了一个Volume大小占用256M,挂载方式是可以读写。

kind: PersistentVolumeClaim
apiVersion: v1
metadata:name: pv-claim
spec:accessModes:- ReadWriteOnceresources:requests:storage: 256Mi

注:下面是来自官网的accessModes

ReadWriteOnce -- the volume can be mounted as read-write by a single node
ReadOnlyMany -- the volume can be mounted read-only by many nodes
ReadWriteMany -- the volume can be mounted as read-write by many nodes

我们可以在之前的StatefulSet声明中,可以使用这个PVC,如下:

apiVersion: apps/v1
kind: StatefulSet
metadata:name: bootstatefulset
spec:serviceName: "bootservice"selector:matchLabels:app: springbootreplicas: 2template:metadata:labels:app: springbootspec:containers:- name: spingboot-mybatisimagePullPolicy: IfNotPresentimage: zjj2006forever/springboot-mybatis:1.3ports:- containerPort: 8300volumeMounts:- mountPath: "/usr/share/"name: pvstoragevolumeClaimTemplates:- metadata:name: pvstoragespec:accessModes:- ReadWriteOnceresources:requests:storage: 256Mi

像上面这样,我们只需要在模板中声明一个PVC的名字,就可以使用这个存储了。这需要kubernete为这个PVC绑定一个pv,用这个这个pv来声明volume,下面我们看一个PV的定义:

kind: PersistentVolume
apiVersion: v1
metadata:name: pv-volumelabels:type: local
spec:capacity:storage: 512MiaccessModes:- ReadWriteOncerbd:monitors:- '10.244.1.158:6789'- '10.244.1.159:6789'pool: kubeimage: foofsType: ext4readOnly: trueuser: adminkeyring: /etc/ceph/keyring

注:上面monitors是使用kubectl get pods -n rook-ceph -o wide看到的rook-ceph-mon-开头ip地址

接着创建上面的StatefulSet后,就会生成2个pvc,名字格式是PVC名字-StatefulSet名字-编号
执行创建命令

kubectl create -f springboot-mybatis-statefulset.yaml 

查看pod

[root@master k8s]# kubectl get pods
NAME                READY   STATUS    RESTARTS   AGE
bootstatefulset-0   1/1     Running   0          2m4s
bootstatefulset-1   1/1     Running   0          2m1s

查看pvc

kubectl get pvc -l app=springboot
NAME                          STATUS    VOLUME                                     CAPACITY   ACCESSMODES   AGE
pvstorage-bootstatefulset-0   Bound     pvc-12c125c7-b507-11e6-932f-5210a500005   256Mi      RWO           29s
pvstorage-bootstatefulset-1   Bound     pvc-12c136c7-b507-11e6-932f-5210a500005   256Mi      RWO           29s   

上面创建了这个带编号的pvc后,pod会按照编号来绑定pvc,如上bootstatefulset-0会使用pvstorage-bootstatefulset-0这个pvc,我们在每个pod中创建一个文件,然后删除pod后等待重新创建,文件依然存在。

这是因为pod被删除后,pv和pvc并没有被删除,而pod被创建出来后,因为StatefulSet的控制,pod会严格按照之前的编号顺序创建出来,而它们会重新绑定相同编号的pvc,从而绑定pvc对应的pv来获取volume里面的数据。

总结

StatefulSet也是一种Deployment,只是它的每一个pod都携带了一个唯一并且固定的编号。这个编号非常重要,因为这个编号固定了pod的拓扑关系(比如主从),固定了pod的DNS记录,有了这个序号,当pod重建时,就不会丢失之前的状态了。pvc则固定了pod的存储状态,它与pv进行绑定从而使用pv中声明的volume存储。这样pod重启后数据就不会丢失了。

微信公众号,所有文章都是个人原创,欢迎关注

kubernete编排技术三:StatefulSet相关推荐

  1. 容器编排技术 -- Kubernetes StatefulSet基本使用

    容器编排技术 -- Kubernetes StatefulSet基本使用 1 Objectives 2 Before you begin 2.1 顺序创建 Pod 3 Pods in a Statef ...

  2. kubernete编排技术六:RBAC权限控制

    目录 定义Role和ClusterRole 角色的绑定RoleBinding 主体或被作用者subject 进行试验 总结 这是kubernete编排技术的第六篇,本文主要讲一下RBAC.之前讲过,k ...

  3. kubernete编排技术二:deployment

    目录 deployment简介 pod水平扩展和收缩 滚动更新 失败回滚 总结 kubernete中的控制器模式,是指用一种对象来控制另一种对象,这个控制器是由组件kube-controller-ma ...

  4. kubernete编排技术五:DaemonSet

    目录 yaml文件 调度策略 创建.升级和回滚 总结 这篇文章我们来介绍kubernete的一个编排对象,叫DaemonSet,从名字上就能看出,这是一个守护进程.它的作用是在kubernete集群的 ...

  5. kubernete编排技术一:pod

    目录 在kubernete上创建pod pod的本质 pod的关键属性 pod的健康检查 总结 在之前的文章<kubernete中的原子调度单位:pod>中提到过,如果把kubernete ...

  6. 容器编排技术 -- Kubernetes StatefulSets

    容器编排技术 -- Kubernetes StatefulSets 1 使用StatefulSets 2 限制 3 组件 4 部署和扩展 4.1 Pod管理 4.1.1 OrderedReady Po ...

  7. 容器编排技术 -- Kubernetes 示例:使用 Stateful Sets 部署 Cassandra

    容器编排技术 -- Kubernetes 示例:使用 Stateful Sets 部署 Cassandra 1 Objectives 2 Before you begin 2.1 Minikube 附 ...

  8. 容器编排技术 -- Init 容器

    容器编排技术 -- Init 容器 1 理解 Init 容器 1.1 与普通容器的不同之处 2 Init 容器能做什么? 2.1 示例 2.2 使用 Init 容器 3 具体行为 3.1 资源 3.2 ...

  9. 容器编排技术 -- Kubernetes Nodes

    容器编排技术 -- Kubernetes Nodes 1 Node是什么? 2 Node Status 2.1 Addresses 2.2 Phase 2.3 Condition 2.4 Capaci ...

最新文章

  1. JS 函数 函数递归
  2. !Important:CSS中!important的作用用于Ie6.0 与Ie7.0、firefox
  3. 《大型网站技术架构》1:概述
  4. 云闪付单个红包最高2018,这是要打败支付宝的节奏吗?
  5. 字符串类型str方法
  6. java 18.9_Oracle: Java 11 (18.9 LTS) 正式上线!
  7. simplified build configuration
  8. vue element container 子路由
  9. Python3按编号创建文件夹并在文件夹下创建对应编号的txt文件
  10. 分支定界 matlab,分支定界法matlab程序
  11. java 纯真ip 乱码_UTF-8使用纯真IP数据库乱码问题
  12. 创建销售订单的BAPI
  13. 静态路由 动态路由 默认路由 默认网关
  14. 当彗星划过天空,那好像梦幻一般的景色,真是无与伦比,美到极致,只能让人一味眺望着那无法言喻的美。
  15. norflash的基本操作2
  16. java 15k_月薪15K的Java工程师必备的十大技能
  17. Redux原理(逐句解析)!!!
  18. elk笔记13--Queries-compound queries
  19. java使用aes加密文件内容
  20. 分离潜变量自动编码器超分辨率网络 SLAESR

热门文章

  1. 大闸蟹的架构师之路-基础架构组篇
  2. java 沙箱_java沙箱绕过
  3. 织梦html地图源码,织梦网站XML地图制作_织梦网站HTML地图制作
  4. linux移动一个桌面的文件夹,linux实用命令之如何移动文件夹及文件下
  5. 聊聊常见的加密与JWT
  6. react项目引入scss
  7. ORACLE SQL 多表查询
  8. golang学习笔记之--Go语言内建容器
  9. 常州PHP就业情况,看完2018年平均工资数据分析后,我觉悟了...
  10. 时间戳.getTime为什么有的时候是0点,有时候是八点