💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
[TOC] ## **StatefulSet与Headless Service** 在k8s中,Deployment能够很好地用来管理无状态服务。而如果我们要发布有状态服务,则一般需要使用到另一种资源:StatefulSet。本文将以Mysql为例,来讲解StatefulSet的原理。 首先我们来看一下Mysql的一些特点: (1)Mysql有主从之分,从节点第一次启动初始化时需要连主节点,即主节点启动后,从节点才能启动。主从节点的启动顺序如何保证? 如下是一个最简单的statefulset: ``` to be added ``` 可以看出,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的定义文件: ``` to be added ``` 它与普通的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,并且会在Pod `mysql-0` 中使用这个PVC。同理,为 `mysql-1` 创建并使用的PVC为`mysqldata-mysql-1`。这样,就保证了每个Pod都能有自已单独的存储。 当mysql-0被重启后,statefulset constroller会先寻找PVC `mysqldata-mysql-0` 是否存在,如果存在则直接使用。这样,就保证了Pod被重启后,还能使用到原来的存储。 当然,实际中mysql的容器化不只是上面那么简单,下一节我们讲述mysql容器化实践。 ## **总结** 最后,我们用三个词来总结一下StatefulSet的特点 * 启动顺序固定性 * 网络标识唯一性 * 存储状态不变性 疑问:statefulset中为什么需要spec.serviceName?上面的分析中好像这个字段并没有用到?