Kubernetes—StatefulSet部署有状态应用详解(二十四)

技术公众号:后端技术解忧铺
关注微信公众号:CodingTechWork,一块儿学习进步。nginx

引言

  咱们都知道在k8s集群中,Deployment是用来部署无状态的服务,那有状态的服务是用什么资源对象来部署呢?无状态和有状态服务部署的区别是什么?有状态的pod确定须要独立的存储卷,这样才能保证故障后寻找数据就地恢复原状态,那如何实现多个pod拥有本身独立存储卷?下面咱们来看看如何演进方案。后端

演进

手动建立多个pod

  手动建立多个pod,每一个pod使用一个独立的持久卷声明,可是须要咱们手动管理这些pod,当发生故障后,须要从新手动建立这些pod,从而保证有状态恢复。
手动建立多个podtomcat

1个RS对应1个pod

  手动建立pod,确定不便于维护,咱们在每一个pod的上一层来操做,建立多个ReplicaSet,每一个ReplicaSet的副本数设置为1,这样pod和ReplicaSet是一一对应的,每一个ReplicaSet的pod模板都关联一个独立的持久卷声明。这种能够达到某个节点故障或pod误删时自动从新调度建立pod的效果,可是对于伸缩副本时,又须要手动建立或删除ReplicaSet,仍是达不到一次性建立、更新、删除后,后期自动调度的效果。
1个RS对应1个pod微信

全部pod共享同一个PV

  建立多个ReplicaSet对应多个pod,仍是会有伸缩问题,且很差维护,若是只建立一个ReplicaSet,让全部pod共享同一持久卷,但每一个pod是使用同一持久卷的不一样目录。
全部pod使用同一数据卷中不一样目录网络

建立StatefulSet

  全部pod使用同一数据卷中不一样目录要求实例之间相互协做,因为不能在一个pod模板中给全部pod作不一样目录的指定,须要让pod本身识别选择一个其余实例没有使用的目录,这种共享存储将会给集群带来性能问题。
  若每一个pod拥有本身的网络标识,就算失败了,再恢复时,仍是原有的稳定的网络标识。这就须要使用StatefulSet资源来部署这些服务,这些服务中每一个实例都是不可替代的,都有稳定的名字和网络标识。
StatefulSet部署有状态服务建立podapp

StatefulSet介绍

有状态服务集群特色

  1. 每一个节点都有固定的身份ID,经过ID可使集群内成员相互发现并通讯;
  2. 集群规模比较固定;
  3. 集群内每一个节点有状态,通常会持久化数据到永久存储中,这样失败了的实例能够经过持久化数据再恢复原有状态;
  4. 磁盘损坏,则某个节点没法正常工做,集群功能将受损受阻;

Statefulset概述

  StatefulSet是k8s从1.4版本引入的PetSet资源对象发展到1.5版本改名而来,在k8s集群中用于部署有状态服务。为什么以前叫PetSet?是由于拿宠物和牛做类比,把应用看作是宠物,给每一个实例都起一个名字,在宠物店里,若一个宠物死掉,咱们买不到一只如出一辙的,用户确定会察觉到差别,若要代替这只宠物,咱们必须找到一只属性及行为和以前彻底一致的宠物,一样的,对于应用而言,咱们须要找到状态和标识和以前一致的实例来代替以前故障实例。less

StatefulSet特色

  1. StatefulSet中每一个Pod有稳定、惟一的网络标识(用于发现集群内其余成员),Pod名称由StatefulSet名+有序数字组成,如ZK服务对应的StatefulSet名为test-zk,副本数为3,则第一个pod名为test-zk-0,第二个pod名称为test-zk-1,第三个pod名称为test-zk-2。
  2. StatefulSet所控制的pod副本启停顺序是有序的。
  3. StatefulSet中的Pod采用稳定的持久化存储卷(PV或者PVC实现),删除pod时,默认不会删除与StatefulSet相关的存储卷。
  4. StatefulSet须要和Headless Service(没有Cluster IP的Service)进行配合,通常在StatefulSet中指定spec.serviceName的名称与Service资源中的metadata.name保持一致。

稳定的网络标识

pod名有序

  StatefulSet部署有状态应用时,建立出的每一个pod都有命名规则,pod名是由StatefulSet名+有序数字组成,每一个pod都有一个从0开始的顺序索引,这个顺序索引体如今pod名称、主机名以及pod对应的固定存储上(pvc名称一样会有顺序索引)。如:3节点的zk的StatefulSet集群对应的StatefulSet名称为test-zk-ss,则对应的3个pod名称为test-zk-ss-0,test-zk-ss-1,test-zk-ss-2。性能

pvc名有序

  若是k8s集群中没有StorageClass的动态存储卷,咱们也能够提早手动建立多个PV、PVC,手动建立的PVC名称必须符合以后建立的StatefulSet命名规则:$(volumeClaimTemplates.name)-$(pod_name),如Statefulset控制的3个pod对应名称为test-zk-ss-0,test-zk-ss-1,test-zk-ss-2,volumeClaimTemplates.name=test-pvc,则自动建立出来的pvc名称分别为:test-pvc-test-zk-ss-0、test-pvc-test-zk-ss-一、test-pvc-test-zk-ss-2。
StatefulSet建立稳定网络标识的pod学习

Headless Service

  Headless Service是没有Cluster IP的Service(与普通Service的区别),在Headless Service中能够看到spec.ClusterIP=None
  若解析Headless Service的DNS域名,返回的是该Service对应的所有pod的Endpoint列表,StatefulSet在Headless Service基础上为StatefulSet控制的每一个pod实例建立DNS域名,格式为$(pod_name).$(headless_service_name),全限定域名为:FQDN:$(pod_name).$(headless_service_name).$(namespace_name).svc.cluster.local
  如:3节点的zk的StatefulSet集群对应的StatefulSet名称为test-zk-ss,Headless Service名称为test-zk-svc,则StatefulSet控制的3个pod对应DNS分别为:test-zk-ss-0.test-zk-svc,test-zk-ss-1.test-zk-svc,test-zk-ss-2.test-zk-svc。若命名空间名称为test-ns,则3个pod对应的FQDN分别为test-zk-ss-0.test-zk-svc.test-ns.svc.cluster.local,test-zk-ss-1.test-zk-svc.test-ns.svc.cluster.local,test-zk-ss-2.test-zk-svc.test-ns.svc.cluster.local。spa

StatefulSet运行原理

pod启停过程

  StatefulSet控制的pod启停过程,相似于扩缩容过程。
  假设有N个副本数。
  启动时,先启动pod序号为0的,而后依次递增至N-1,操做第N个pod时,前N-1个pod已是运行并准备好的状态(Running状态)。
StatefulSet资源对象的pod启动顺序
  中止时,先中止pod序号为最大的N-1,而后依次递减至0,操做第N-1个pod时,第N个pod已是中止状态。
StatefulSet资源对象的pod中止顺序

重启pod流程

  咱们先看一下ReplicaSet管理的一个pod若是消失时,如何重启一个新的pod来替换旧的。
ReplicaSet重启pod没法保持一致的标识

  当一个StatefulSet管理的一个pod由于发生故障或被人为删除而消失后,StatefulSet能够保证再去重启一个新的pod实例去替换它,这个新pod实例与以前的pod保持彻底一致的行为(pod名、主机名)。
StatefulSet重启pod保持一致的标识

  咱们能够从ReplicaSet和StatefulSet重启pod替换消失pod的过程当中看出两种过程当中的标识是很明显的差异,也是无状态和有状态的差别。

pod扩缩容过程

pod扩容过程

  假设StatefulSet名称为A,从副本数1扩容到3。扩容一个StatefulSet时会使用下一个尚未使用到的顺序索引值进行新pod实例的建立,依次递增1。
StatefulSet扩容过程

pod缩容过程

  假设StatefulSet名称为A,从副本数3缩容到1。缩容一个StatefulSet时,StatefulSet是明确知道先删除最高索引值的实例,缩容删除哪一个pod是可控预知的,而ReplicaSet是不知道会删除哪一个实例。
  因为StatefulSet缩容时是从高索引挨个删除,每次只会操做一个pod实例,因此有状态应用的缩容过程很慢。
StatefulSet缩容过程

StatefulSet的at-most-one语义

  在副本数缩容再扩容时,若是k8s没有保障机制,很容易出现正在缩容的pod还在运行,又新建一个同样标识的pod进行pvc绑定,这会带来问题。
  对于ReplicaSet的pod来讲,会以一个随机的标识来建立pod,不会存在两个相同标识的进程同时运行。而StatefulSet是必须在准确确认一个pod再也不运行后,才会去建立替换的pod,从而保证两个拥有相同标记和绑定相同PVC的有状态的pod实例不会同时运行,这即是at-most-one的语义。

StatefulSet提供稳定的独立存储

  一个有状态的pod须要有本身专属的存储,该pod被从新调度室,新的pod与旧pod保持一致的标识,且新的pod实例挂载相同的存储。

持久卷声明模板

如何在同一个pod模板中为全部pod实例关联不一样的持久卷?
  Statefulset在pod模板中添加了卷声明模板,自动建立的pvc名称将会符合规则:$(volumeClaimTemplates.metadata.name).$(pod_name)
StatefulSet为每一个pod提供稳定的独立存储

持久卷建立和删除

  当StatefulSet增长一个副本时,会建立对应的pvc持久卷声明。建立N个副本,就会有N个PVC与之对应。
  当StatefulSet减小副本时,会从高索引值的pod名开始删除pod,可是PVC不会被删除。若是须要释放特定的持久卷,须要手动删除对应的持久卷声明
  当先缩容,再扩容时,因为旧pod对应的pvc不会被自动删除,扩容重建的pod实例会绑定到对应序号的pvc上。
StatefulSet缩容再扩容时从新挂载pvc
  从上图咱们能够看出,StatefulSet A先从副本2缩容为副本1,对应的Pod A-1会自动删除,可是PVC A-1仍然保留不删除。当StatefulSet A从副本1扩容到副本2时,自动建立新的Pod A-1与旧pod保持一致的标识,PVC A-1被从新挂载到Pod A-1上。

StatefulSet使用命令

假设某个应用的StatefulSet的yaml模板为test-zk-ss.yaml,StatefulSet名称为test-zk-ss,副本数为3个。更新后的模板为test-zk-ss-new.yaml

建立

基于模板建立
kubectl create -f test-zk-ss.yaml

删除

  • 基于模板删除:
    kubectl delete -f test-zk-ss.yaml
  • 基于名称删除:
    kubectl delete statefulset test-zk-ss

更新

  • 基于模板更新:
    kubectl apply -f test-zk-ss-new.yaml
  • 基于名称更新:
    kubectl edit statefulset test-zk-ss

查询

  • 基于模板查看:
    kubectl get statefulset test-zk-ss -o yaml
  • 基于名称查看:
    kubectl describe statefulset test-zk-ss

Q&A

k8s中无状态服务和有状态服务部署的区别?

无状态

  1. pod命名:pod名由资源名+随机的字符串组成;
  2. 数据存储:多个实例pod能够共享相同的持久化数据,存储不是必要条件;
  3. 扩缩容:能够随意扩缩容某个pod,不会指定某个pod进行扩缩容;
  4. 启停顺序:由于pod名的序号是随机串,无启停顺序之分;
  5. 无状态k8s资源:ReplicaSet、ReplicationController、Deployment、DaemonSet、Job等资源;
  6. 无状态服务:tomcat、nginx等;


有状态
这里假设有N个pod;

  1. pod命名:pod名由statefulset资源名+有序的数字组成(0,1,2...N-1),且pod有特定的网络标识;
  2. 数据存储:有状态的服务对应实例须要有本身的独立持久卷存储;
  3. 扩缩容:扩缩容不可随意,缩容是从数字最大的开始递减,扩容是在原有pod序号基础上递增1。
  4. 启停顺序:pod启停是有顺序的,启动时,先启动pod序号为0的,而后依次递增至N-1;中止时,先中止pod序号为最大的N-1,而后依次递减至0;
  5. 有状态k8s资源:StatefulSet资源;
  6. 有状态服务:Kafka、ZooKeeper、MySql、MongoDB以及一些须要保存日志的应用等服务;
相关文章
相关标签/搜索