StatefulSet-在Kubernetes中部署和管理有状态应用的强大利器 (statefarm)
简介
StatefulSet 是 Kubernetes 中一种用于部署有状态应用的资源对象。与无状态应用不同,有状态应用在部署和管理时需要考虑一些额外的因素。StatefulSet 为我们提供了一种有效的解决方案。无状态与有状态应用
无状态应用:每个 Pod 都是相同的,没有顺序要求,并且可以在任何节点上运行,可以自由地进行伸缩和扩展。对于容器化部署来说,这种应用比较简单,可以使用 Deployment 进行管理。 有状态应用:需要更多关注点,例如数据库和缓存。 独立的存储:即使一个 Pod 出现故障,我们也可以从其他节点启动一个同名的 Pod,并将原来 Pod 的存储挂载到新的 Pod 上,以保持服务的状态。 启动顺序和唯一性:每个 Pod 需要按照一定的顺序启动,并且每个 Pod 都具有唯一的网络标识符。StatefulSet 的功能
为了满足这些要求,我们可以使用 StatefulSet 来部署有状态应用。StatefulSet 是 Kubernetes 提供的一种控制器,它可以: 管理一组有状态的 Pod 为每个 Pod 分配一个唯一的标识符 分配独立的存储卷 提供有序的扩缩容机制 有状态服务的域名解析使用 StatefulSet
1. 定义一个有状态应用的模板:包括 Pod 的配置信息,如容器镜像、资源限制、环境变量等。 2. 创建和管理有状态 Pod:通过定义一个或多个副本的方式。 3. Kubernetes 将自动分配一个唯一的标识符和创建一个稳定的网络域名。优点
StatefulSet 是一种有用的资源对象,可以帮助我们部署和管理有状态应用。它提供了以下特性: 独立的存储 唯一的网络标识符 有序的扩缩容 有状态服务的域名解析总结
StatefulSet 为我们提供了一种有效的方式来部署和管理有状态应用。它使得这些应用能够在容器化环境中得到良好的支持和扩展。kubernetes——StatefulSet详解
RC、Deployment、DaemonSet都是面向无状态的服务,它们所管理的Pod的IP、名字,启停顺序等都是随机的,而StatefulSet是什么?顾名思义,有状态的集合,管理所有有状态的服务,比如MySQL、MongoDB集群等。 StatefulSet本质上是Deployment的一种变体,在v1.9版本中已成为GA版本,它为了解决有状态服务的问题,它所管理的Pod拥有固定的Pod名称,启停顺序,在StatefulSet中,Pod名字称为网络标识(hostname),还必须要用到共享存储。
在Deployment中,与之对应的服务是service,而在StatefulSet中与之对应的headless service,headless service,即无头服务,与service的区别就是它没有Cluster IP,解析它的名称时将返回该Headless Service对应的全部Pod的Endpoint列表。
除此之外,StatefulSet在Headless Service的基础上又为StatefulSet控制的每个Pod副本创建了一个DNS域名,这个域名的格式为:
接下来看一些示例,展示下上面所说的特性,以加深理解
通过该配置文件,可看出StatefulSet的三个组成部分:
创建:
看下这三个Pod创建过程:
根据volumeClaimTemplates自动创建的PVC
Statefulset名称为web 三个Pod副本: web-0,web-1,web-2,volumeClaimTemplates名称为:www,那么自动创建出来的PVC名称为www-web[0-2],为每个Pod创建一个PVC。
匹配Pod name(网络标识)的模式为:(序号),比如上面的示例:web-0,web-1,web-2。
StatefulSet为每个Pod副本创建了一个DNS域名,这个域名的格式为: $(podname).(headless server name),也就意味着服务间是通过Pod域名来通信而非Pod IP,因为当Pod所在Node发生故障时,Pod会被飘移到其它Node上,Pod IP会发生变化,但是Pod域名不会有变化。
StatefulSet使用Headless服务来控制Pod的域名,这个域名的FQDN为:(namespace),其中,“”指的是集群的域名。
根据volumeClaimTemplates,为每个Pod创建一个pvc,pvc的命名规则匹配模式:( )-(pod_name ),比如上面的 =www , Pod name=web-[0-2],因此创建出来的PVC是www-web-0、www-web-1、www-web-2。
删除Pod不会删除其pvc,手动删除pvc将自动释放pv。 关于Cluster Domain、headless service名称、StatefulSet 名称如何影响StatefulSet的Pod的DNS域名的示例:
在v1.7以后,通过允许修改Pod排序策略,同时通过字段确保其身份的唯一性。
在Kubernetes 1.7及更高版本中,通过字段允许配置或禁用Pod、labels、source request/limits、annotations自动滚动更新功能。
StatefulSet控制器将删除并重新创建StatefulSet中的每个Pod。它将以Pod终止(从最大序数到最小序数)的顺序进行,一次更新每个Pod。在更新下一个Pod之前,必须等待这个Pod Running and Ready。
参考:
10.2 了解Statefulset
可以创建一个Statefulset资源替代ReplicaSet来运行这类pod。它是专门定制的一类应用,这类应用中每一个实例都是不可替代的个体,都拥有稳定的名字和状态。
要很好地理解Statefulset的用途,最好先与ReplicaSet或ReplicationControllers对比一下。首先拿一个通用的类比来解释它们。
通过宠物与牛的类比来理解有状态
你可能已经听说过宠物与牛的类比。如果没有,先简单介绍一下。可以把我们的应用看作宠物或牛。
我们倾向于把应用看作宠物,给每个实例起一个名字,细心照顾每个实例。但是也许把它们看成牛更为合适,并不需要对单独的实例有太多关心。这样就可以非常方便地替换掉不健康的实例,就跟农场主替换掉一头生病的牛一样。
对于无状态的应用实例来说,行为非常像农场里的牛。一个实例挂掉后并没什么影响,可以创建一个新实例,而让用户完全无感知。
另一方面,有状态的应用的一个实例更像一个宠物。若一只宠物死掉,不能买到一只完全一样的,而不让用户感知到。若要替换掉这只宠物,需要找到一只行为举止与之完全一致的宠物。对应用来说,意味着新的实例需要拥有跟旧的案例完全一致的状态和标识。
Statefulset与ReplicaSet或ReplicationController的对比
RelicaSet或ReplicationController管理的pod副本比较像牛,这是因为它们都是无状态的,任何时候它们都可以被一个全新的pod替换。然而有状态的pod需要不同的方法,当一个有状态的pod挂掉后(或者它所在的节点故障),这个pod实例需要在别的节点上重建,但是新的实例必须与被替换的实例拥有相同的名称、网络标识和状态。这就是StatefulSet如何管理pod的。
Statefulset保证了pod在重新调度后保留它们的标识和状态。它让你方便地扩容、缩容。与ReplicaSet类似,Statefulset也会指定期望的副本个数,它决定了在同一时间内运行的宠物的数量。与ReplicaSet类似,pod也是依据Statefulset的pod模板创建的(想象一下曲奇饼干模板)。与ReplicaSet不同的是,Statefulset创建的pod副本并不是完全一样的。每个pod都可以拥有一组独立的数据卷(持久化状态)而有所区别。另外“宠物”pod的名字都是规律的(固定的),而不是每个新pod都随机获取一个名字。
一个Statefulset创建的每个pod都有一个从零开始的顺序索引,这个会体现在pod的名称和主机名上,同样还会体现在pod对应的固定存储上。这些pod的名称则是可预知的,因为它是由Statefulset的名称加该实例的顺序索引值组成的。不同于pod随机生成一个名称,这样有规则的pod名称是很方便管理的,如图10.5所示。
[图片上传失败...(image-bf81d6-14)]
图10.5 与ReplicaSet不同,由Statefulset创建的pod拥有规则的名称(和主机名)
控制服务介绍
让pod拥有可预知的名称和主机名并不是全部,与普通的pod不一样的是,有状态的pod有时候需要通过其主机名来定位,而无状态的pod则不需要,因为每个无状态的pod都是一样的,在需要的时候随便选择一个即可。但对于有状态的pod来说,因为它们都是彼此不同的(比如拥有不同的状态),通常希望操作的是其中特定的一个。
基于以上原因,一个Statefulset通常要求你创建一个用来记录每个pod网络标记的headless Service。通过这个Service,每个pod将拥有独立的DNS记录,这样集群里它的伙伴或者客户端可以通过主机名方便地找到它。比如说,一个属于default命名空间,名为foo的控制服务,它的一个pod名称为A-0,那么可以通过下面的完整域名来访问它。而在ReplicaSet中这样是行不通的。
另外,也可以通过DNS服务,查找对应的所有SRV记录,获取一个Statefulset中所有pod的名称。我们将在10.4节中介绍SRV记录,解释如何通过它来发现一个Statefulset中的所有成员。
替换消失的宠物
当一个Statefulset管理的一个pod实例消失后(pod所在节点发生故障,或有人手动删除pod),Statefulset会保证重启一个新的pod实例替换它,这与ReplicaSet类似。但与ReplicaSet不同的是,新的pod会拥有与之前pod完全一致的名称和主机名(ReplicaSet和Statefulset的差异如图10.6所示)。
图10.6 Statefulset使用标识完全一致的新的pod替换,ReplicaSet则是使用一个不相干的新的pod替换
如你之前了解的那样,pod运行在哪个节点上并不重要,新的pod并不一定会调度到相同的节点上。对于有状态的pod来说也是这样,即使新的pod被调度到一个不同的节点,也同样可以通过主机名来访问。
扩缩容Statefulset
扩容一个Statefulset会使用下一个还没用到的顺序索引值创建一个新的pod实例。比如,要把一个Statefulset从两个实例扩容到三个实例,那么新实例的索引值就会是2(现有实例使用的索引值为0和1)。
当缩容一个Statefulset时,比较好的是很明确哪个pod将要被删除。作为对比,ReplicaSet的缩容操作则不同,不知道哪个实例会被删除,也不能指定先删除哪个实例(也许这个功能会在将来实现)。缩容一个Statefulset将会最先删除最高索引值的实例(如图10.7所示),所以缩容的结果是可预知的。
[图片上传失败...(image-d8c3f0-14)]image
图10.7 缩容一个Statefulset将会最先删除最高索引值的实例
因为Statefulset缩容任何时候只会操作一个pod实例,所以有状态应用的缩容不会很迅速。举例来说,一个分布式存储应用若同时下线多个节点,则可能导致其数据丢失。比如说一个数据项副本数设置为2的数据存储应用,若同时有两个节点下线,一份数据记录就会丢失,如果它正好保存在这两个节点上。若缩容是线性的,则分布式存储应用就有时间把丢失的副本复制到其他节点,保证数据不会丢失。
基于以上原因,Statefulset在有实例不健康的情况下是不允许做缩容操作的。若一个实例是不健康的,而这时再缩容一个实例的话,也就意味着你实际上同时失去了两个集群成员。
你已经知道了Statefulset如何保证一个有状态的pod拥有稳定的标识,那存储呢?一个有状态的pod需要拥有自己的存储,即使该有状态的pod被重新调度(新的pod与之前pod的标识完全一致),新的实例也必须挂载着相同的存储。那Statefulset是如何做到这一点的呢?
很明显,有状态的pod的存储必须是持久的,并且与pod解耦。在第6章中学习了持久卷和持久卷声明,通过在pod中关联一个持久卷声明的名称,就可以为pod提供持久化存储。因为持久卷声明与持久卷是一对一的关系,所以每个Statefulset的pod都需要关联到不同的持久卷声明,与独自的持久卷相对应。因为所有的pod实例都是依据一个相同的pod模板创建的,那它们是如何关联到不同的持久卷是的呢?并且由谁来创建这些持久卷是呢?当然你肯定不想手在动创建Statefulset之前,依据pod的个数创建相同数量的持久卷量。当然不用这么做!
在pod模板中添加卷声明模板
像Statefulset创建pod一样,Statefulset也需要创建持久卷声明。所以一个Statefulset可以拥有一个或多个卷声明模板,这些持久卷声明会在创建pod前创建出来,绑定到一个pod实例上(如图10.8所示)。
图10.8 一个Statefulset创建pod和持久卷声明
声明的持久卷既可以通过administrator用户预先创建出来,也可以如第6章所述,由持久卷的动态供应机制实时创建出来。
持久卷的创建和删除
扩容StatefulSet增加一个副本数时,会创建两个或更多的API对象(一个pod和与之关联的一个或多个持久卷声明)。但是对缩容来说,则只会删除一个pod,而遗留下之前创建的声明。当你知道一个声明被删除会发生什么的话,你就明白为什么这么做了。当一个声明被删除后,与之绑定的持久卷就会被回收或删除,则其上面的数据就会丢失。
因为有状态的pod是用来运行有状态应用的,所以其在数据卷上存储的数据非常重要,在Statefulset缩容时删除这个声明将是灾难性的,特别是对于Statefulset来说,缩容就像减少其replicas数值一样简单。基于这个原因,当你需要释放特定的持久卷时,需要手动删除对应的持久卷声明。
重新挂载持久卷声明到相同pod的新实例上
因为缩容Statefulset时会保留持久卷声明,所以在随后的扩容操作中,新的pod实例会使用绑定在持久卷上的相同声明和其上的数据(如图10.9所示)。当你因为误操作而缩容一个Statefulset后,可以做一次扩容来弥补自己的过失,新的pod实例会运行到与之前完全一致的状态(名字也是一样的)。
图10.9 Statefulset缩容时不删除持久卷声明,扩容时会重新挂载上
如之前描述的,Statefulset的行为与ReplicaSet是不一样的。Statefulset不仅拥有稳定的标记和独立的存储,它的pod还有其他的一些保障。
稳定标识和独立存储的影响
通常来说,无状态的pod是可以替代的,而有状态的pod则不行。我们之前已经描述了一个有状态的pod总是会被一个完全一致的pod替换(两者有相同的名称、主机名和存储等)。这个替换发生在Kubernetes发现旧的pod不存在时(例如手动删除这个pod)。
那么当Kubernetes不能确定一个pod的状态时呢?如果它创建一个完全一致的pod,那系统中就会有两个完全一致的pod在同时运行。这两个pod会绑定到相同的存储,所以这两个相同标记的进程会同时写相同的文件。对于ReplicaSet的pod来说,这不是问题,因为应用本来就是设计为在相同的文件上工作的。并且我们知道ReplicaSet会以一个随机的标识来创建pod,所以不可能存在两个相同标识的进程同时运行。
介绍Statefulset的at-most-one的语义
Kubernetes必须保证两个拥有相同标记和绑定相同持久卷声明的有状态的pod实例不会同时运行。一个Statefulset必须保证有状态的pod实例的at-most-one语义。
也就是说一个Statefulset必须在准确确认一个pod不再运行后,才会去创建它的替换pod。这对如何处理节点故障有很大的影响,我们会在本章后面详细介绍。在我们做这些之前,需要先创建一个Statefulset,看看它是如何工作的。在这个过程中,你会学到更多的知识。
免责声明:本文转载或采集自网络,版权归原作者所有。本网站刊发此文旨在传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及版权、内容等问题,请联系本网,我们将在第一时间删除。同时,本网站不对所刊发内容的准确性、真实性、完整性、及时性、原创性等进行保证,请读者仅作参考,并请自行核实相关内容。对于因使用或依赖本文内容所产生的任何直接或间接损失,本网站不承担任何责任。