有状态服务
《StatefulSet与Headless Service》
在k8s中,Deployment能够很好地用来管理无状态服务。而如果我们要发布有状态服务,则一般需要使用到另一种资源:StatefulSet。本文将以Mysql为例,来讲解StatefulSet的原理。
首先我们来看一下Mysql的一些特点:
(1)Mysql有主从之分,从节点第一次启动初始化时需要连主节点,即主节点启动后,从节点才能启动。主从节点的启动顺序如何保证?
如下是一个最简单的statefulset:
可以看出,StatefulSet的定义与Deployment几乎没什么差别,除了多了一个spec.serviceName
字段。这个字段这里先不讲,本节后面会讲到。
上面的StatefulSet会创建三个Pod,与Deployment不同是,三个Pod的命名是固定的,分别为mysql-0
、mysql-1
、mysql-2
,且三个Pod的启动是有顺序的,只有mysql-0
启动成功后,mysql-1
才会开始创建。而且,任何一个Pod被重启后,其命名不会改变
所以,我们可以用一个statefulset来创建一个mysql集群,mysql-0
作为主节点,mysql-1
和mysql-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-1
与mysql-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?