关注微信公众号:CodingTechWork,一块儿学习进步。nginx
咱们都知道在k8s集群中,Deployment是用来部署无状态的服务,那有状态的服务是用什么资源对象来部署呢?无状态和有状态服务部署的区别是什么?有状态的pod确定须要独立的存储卷,这样才能保证故障后寻找数据就地恢复原状态,那如何实现多个pod拥有本身独立存储卷?下面咱们来看看如何演进方案。后端
手动建立多个pod,每一个pod使用一个独立的持久卷声明,可是须要咱们手动管理这些pod,当发生故障后,须要从新手动建立这些pod,从而保证有状态恢复。
tomcat
手动建立pod,确定不便于维护,咱们在每一个pod的上一层来操做,建立多个ReplicaSet,每一个ReplicaSet的副本数设置为1,这样pod和ReplicaSet是一一对应的,每一个ReplicaSet的pod模板都关联一个独立的持久卷声明。这种能够达到某个节点故障或pod误删时自动从新调度建立pod的效果,可是对于伸缩副本时,又须要手动建立或删除ReplicaSet,仍是达不到一次性建立、更新、删除后,后期自动调度的效果。
微信
建立多个ReplicaSet对应多个pod,仍是会有伸缩问题,且很差维护,若是只建立一个ReplicaSet,让全部pod共享同一持久卷,但每一个pod是使用同一持久卷的不一样目录。
网络
全部pod使用同一数据卷中不一样目录要求实例之间相互协做,因为不能在一个pod模板中给全部pod作不一样目录的指定,须要让pod本身识别选择一个其余实例没有使用的目录,这种共享存储将会给集群带来性能问题。
若每一个pod拥有本身的网络标识,就算失败了,再恢复时,仍是原有的稳定的网络标识。这就须要使用StatefulSet资源来部署这些服务,这些服务中每一个实例都是不可替代的,都有稳定的名字和网络标识。
app
StatefulSet是k8s从1.4版本引入的PetSet
资源对象发展到1.5版本改名而来,在k8s集群中用于部署有状态服务
。为什么以前叫PetSet?是由于拿宠物和牛做类比,把应用看作是宠物,给每一个实例都起一个名字,在宠物店里,若一个宠物死掉,咱们买不到一只如出一辙的,用户确定会察觉到差别,若要代替这只宠物,咱们必须找到一只属性及行为和以前彻底一致的宠物,一样的,对于应用而言,咱们须要找到状态和标识和以前一致的实例来代替以前故障实例。less
Headless Service
(没有Cluster IP的Service)进行配合,通常在StatefulSet中指定spec.serviceName
的名称与Service资源中的metadata.name
保持一致。 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。性能
若是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。
学习
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启停过程,相似于扩缩容过程。
假设有N个副本数。
启动时,先启动pod序号为0的,而后依次递增至N-1,操做第N个pod时,前N-1个pod已是运行并准备好的状态(Running状态)。
中止时,先中止pod序号为最大的N-1,而后依次递减至0,操做第N-1个pod时,第N个pod已是中止状态。
咱们先看一下ReplicaSet管理的一个pod若是消失时,如何重启一个新的pod来替换旧的。
当一个StatefulSet管理的一个pod由于发生故障或被人为删除而消失后,StatefulSet能够保证再去重启一个新的pod实例去替换它,这个新pod实例与以前的pod保持彻底一致的行为(pod名、主机名)。
咱们能够从ReplicaSet和StatefulSet重启pod替换消失pod的过程当中看出两种过程当中的标识是很明显的差异,也是无状态和有状态的差别。
假设StatefulSet名称为A,从副本数1扩容到3。扩容一个StatefulSet时会使用下一个尚未使用到的顺序索引值进行新pod实例的建立,依次递增1。
假设StatefulSet名称为A,从副本数3缩容到1。缩容一个StatefulSet时,StatefulSet是明确知道先删除最高索引值的实例,缩容删除哪一个pod是可控预知的,而ReplicaSet是不知道会删除哪一个实例。
因为StatefulSet缩容时是从高索引挨个删除,每次只会操做一个pod实例,因此有状态应用的缩容过程很慢。
在副本数缩容再扩容时,若是k8s没有保障机制,很容易出现正在缩容的pod还在运行,又新建一个同样标识的pod进行pvc绑定,这会带来问题。
对于ReplicaSet的pod来讲,会以一个随机的标识来建立pod,不会存在两个相同标识的进程同时运行。而StatefulSet是必须在准确确认一个pod再也不运行后,才会去建立替换的pod
,从而保证两个拥有相同标记和绑定相同PVC的有状态的pod实例不会同时运行,这即是at-most-one
的语义。
一个有状态的pod须要有本身专属的存储,该pod被从新调度室,新的pod与旧pod保持一致的标识,且新的pod实例挂载相同的存储。
如何在同一个pod模板中为全部pod实例关联不一样的持久卷?
Statefulset在pod模板中添加了卷声明模板,自动建立的pvc名称将会符合规则:$(volumeClaimTemplates.metadata.name).$(pod_name)
当StatefulSet增长一个副本时,会建立对应的pvc持久卷声明。建立N个副本,就会有N个PVC与之对应。
当StatefulSet减小副本时,会从高索引值的pod名开始删除pod,可是PVC不会被删除。若是须要释放特定的持久卷,须要手动删除对应的持久卷声明
。
当先缩容,再扩容时,因为旧pod对应的pvc不会被自动删除,扩容重建的pod实例会绑定到对应序号的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的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
无状态:
有状态
这里假设有N个pod;