StatefulSet

StatefulSet中的Stateful是有状态的意思。其中的状态即指:StatefulSet接管的每个Pod有稳定的网络标识(包括Pod的名称和主机名,是顺序索引值)、稳定的专属存储。进一步解释:

  1. StatefulSet替换旧Pod时,新Pod与其有相同的名称、网络标识和状态;

  2. StatefulSet管理的Pod可以有各自独立的存储卷;

  3. StatefulSet创建的Pod的名字是有规律的,不是随机生成的;

StatefulSet接管下的Pod的架构可以用下图表示:

StatefulSet接管的Pod架构图
circle-info

Tips: 从图中可以看出,StatefulSet创建的Pod的名称(和主机名)以及PVC的名称都是按顺序索引的,这是它的特征之一。

triangle-exclamation

StatefulSet与ReplicaSet对比

把StatefulSet比作宠物,ReplicaSet比作

对于有状态应用,即StatefulSet接管的Pod,就像宠物一样,若一只宠物死掉,若要找一个替换它,除非找到完全一模一样的(现实中当然是不可能的),否则用户不可能感知不到。

对于无状态应用,即ReplicaSet接管的Pod,就像农场中的牛一样,若一只牛死掉,农场主完全可以找另一个牛来替代,不用一模一样,对于用户(食客)没有什么差别。

稳定的网络标识

在前面提到过,StatefulSet创建的Pod的名称和主机名都是固定的(因此称之为稳定的网络标识),因此在使用StatefulSet时通常都会创建一个用来记录每个Pod网络标识headless Service(即Service资源的spec.clusterIP=None。通过这个Service资源对象,每个Pod都有自己专属的DNS记录,这样分布式应用中的其他Pod可以方便的通过主机名找到该Pod。例如,一个Pod的名称(主机名)为A-0,Service的名字为foo,属于default这个命名空间,那么a-0.foo.default.svc.cluster.local就是这个Pod的专属域名。

紧跟着上面这个例子,我们通过访问foo.default.svc.cluster.local也可以获得其对应的所有SRV记录,从而获得了StatefulSet管理的所有Pod的名称和主机名。

circle-info

Tips: headless Service通过将spec.clusterIP设置为None,这样访问Service名.命名空间.svc.cluster.local获得的就不会是Service的clusterIP了,而是被该Service监听的所有Pod的IP。

headlessService的目的是为了为分布式应用中的各Pod提供通过主机名就可以访问其他Pod的能力。

若要将应用的API服务请求地址映射出去,通常会创建另外一个Service来达到该目的。

稳定的专属存储

StatefulSet创建的Pod都有自己的专属存储,这是依赖PVC(持久卷声明)实现的,PVC与PV是一一对应的关系,而Pod与PVC是一一对应的关系,即PVC会在Pod创建之前创建出来,并绑定至Pod上。

当StatefulSet删除Pod时,会保留PVC和PV,当创建新的同规格的Pod时,会将该保留的PVC再绑定至该Pod上。

扩缩容

扩缩容的方式可见修改资源方式中的方式。

扩容

扩容一个StatefulSet会使用下一个还没用到的顺序索引值创建一个新的Pod。比如,现有2个Pod,它们的索引值为0和1,则新创建的Pod的索引值为2。

在扩容时,会根据Pod的PVC模板创建它专属的PV。

缩容

缩容一个StatefulSet会最先删除最高索引值,因此StatefulSet的缩容结果是可预见的。比如,现有3个Pod,则最先被删除的Pod的索引值为2。

StatefulSet的缩容是线性的(即删除完一个,再删下一个),所以在有Pod不健康的情况下,是不允许做缩容操作的。

在缩容时,Pod会被直接删除,但是PV和PVC不会被删除。

circle-info

Tips: 这里解释一下线性缩容的原因。假设一个StatefulSet中有2个副本Pod,它们均为应用提供数据存储功能,若两个Pod缩容时同时被删除(一个是在缩容时删除,另一个则因Node故障而删除),那么应用的存储功能相当于完全崩溃了(虽然它们的PV还在,但是由于没有Pod通过该PV提供服务,所以应用此刻是无法存储数据的)。因此设计成线性缩容的方式,这样在其中一个Pod处于删除过程中时,另一个Pod不能被删除,从而正常为应用提供存储功能。

创建StatefulSet

通过StatefulSet部署应用时,总共需要部署两个(或三个)不同的资源类型对象:

  1. 持久卷(PersistentVolume):存储应用的数据文件(若集群不支持持久卷动态供应时,才需要手动创建)

  2. headless Service:为Pod提供依靠主机名访问应用中其他Pod的能力(通常访问应用不会通过该Service,而是另外再建一个Service将应用的API接口地址暴露出去);

  3. StatefulSet本身;

部署持久卷

部署headless Service

部署StatefulSet

部署结果

部署结果如下:

circle-info

Tips:

  1. StatefulSet创建Pod同样是线性创建的,会令第一个Pod就绪之后,才会创建第二个Pod;

  2. 创建出的PVC的名字结构为pvc模板中指定的名字-Pod的名称

  3. 创建出的PVC、Pod的名字都是有序的;

发现应用中其他Pod节点

在之前提到过,在部署StatefuSet资源对象时,需要创建一个headless Service资源对象,该对象可以为应用中的Pod提供发现其他Pod的能力。

假设在default命名空间中,有一个名为kubia的headless Service,那么应用中的其他Pod可以使用dig命令,访问kubia.default.svc.cluster.local就可以通过SRV记录,获得该Service的所有后端的Pod:

kubia-0.kubia.default.svc.cluster.local是一个Pod的DNS域名,其地址为172.17.0.8

kubia-1.kubia.default.svc.cluster.local是另一个Pod的DNS域名,其地址为172.17.0.9

通过该机制,可以得到一个简易的分布式数据存储服务的操作流程,如下图所示,图中的每一个Pod都有自己的存储空间,用户的请求会随机打到任意一个Pod上,该Pod会从其他Pod上获取数据,并与自己本地数据汇总,然后再返回给用户:

简易的分布式数据存储服务的操作流程

处理Node失效

由于StatefulSet需要保证不会拥有两个相同标记和存储的Pod同时运行,因此在明确知道一个Pod不再运行之前,它不会去创建一个替换Pod。

当Node失效时,需要管理员手动通知Kubernetes删除失效的Pod,StatefulSet才会去创建替换Pod。

circle-exclamation

Last updated

Was this helpful?