有状态服务

《StatefulSet与Headless Service》

在k8s中,Deployment能够很好地用来管理无状态服务。而如果我们要发布有状态服务,则一般需要使用到另一种资源:StatefulSet。本文将以Mysql为例,来讲解StatefulSet的原理。

首先我们来看一下Mysql的一些特点:

(1)Mysql有主从之分,从节点第一次启动初始化时需要连主节点,即主节点启动后,从节点才能启动。主从节点的启动顺序如何保证?

如下是一个最简单的statefulset:

可以看出,StatefulSet的定义与Deployment几乎没什么差别,除了多了一个spec.serviceName字段。这个字段这里先不讲,本节后面会讲到。

上面的StatefulSet会创建三个Pod,与Deployment不同是,三个Pod的命名是固定的,分别为mysql-0mysql-1mysql-2,且三个Pod的启动是有顺序的,只有mysql-0启动成功后,mysql-1才会开始创建。而且,任何一个Pod被重启后,其命名不会改变

所以,我们可以用一个statefulset来创建一个mysql集群,mysql-0作为主节点,mysql-1mysql-2作为从节点

(2)mysql的从节点需要连接主节点,主节点mysql-0的访问方式是什么,如何保持不变?

这里,我们就需要介绍到k8s的另一个对象:Headless Service。如下是一个Headless Service的定义文件:

它与普通的Service不同的是,spec.clusterIP明确指定为了None。也就是说,这个Service将不会有一个ClusterIP。

我们知道,对于普通的Service,DNS插件会生成一条A记录,记录<serviceName>.<namespace>.svc.cluster.local这个域名的IP地址为clusterIP

而对于Headless Service,DNS插件会为Service的每个Pod都生成一条A记录。这条A记录的域名为<podName>.<ServiceName>.<namespace>.svc.cluster.local,A记录的IP为Pod的IP。

所以,当从节点mysql-1mysql-2去访问主节点时,使用域名mysql-0.mysql.default.svc.cluster.local就可以了,不管mysql-0这个Pod的IP是多少。

(3)mysql的数据如何持久化?

我们直接给出statefulset持久化数据的方案,如下:

上面的statefulset中,我们使用了一个volumeClaimTemplates。在Pod的volumeMounts中,使用了mysql这个Template。

那么,statefulset controller在创建Podmysql-0的时候,同时会根据这个Template创建一个名字为mysqldata-mysql-0的PVC,并且会在Podmysql-0中使用这个PVC。同理,为mysql-1创建并使用的PVC为mysqldata-mysql-1。这样,就保证了每个Pod都能有自已单独的存储。

mysql-0被重启后,statefulset constroller会先寻找PVCmysqldata-mysql-0是否存在,如果存在则直接使用。这样,就保证了Pod被重启后,还能使用到原来的存储。

当然,实际中mysql的容器化不只是上面那么简单,下一节我们讲述mysql容器化实践。

总结

最后,我们用三个词来总结一下StatefulSet的特点

  • 启动顺序固定性

  • 网络标识唯一性

  • 存储状态不变性

疑问:statefulset中为什么需要spec.serviceName?上面的分析中好像这个字段并没有用到?

Last updated

Was this helpful?