本节以MongoDB为例,使用StatefulSet完成MongoDB集群的创建,为每个MongoDB实例在共享存储(本例使用GlusterFS)中都申请一片存储空间,以实现一个无单点故障、高可用、可动态扩展的MongoDB集群。该部署架构如图3.14所示。

图3.14 使用StatefulSet部署MongoDB集群的架构
在创建StatefulSet之前,需要确保在Kubernetes集群中管理员已经创建好共享存储,并能够与StorageClass对接,以实现动态存储供应的模式。本节的示例将使用GlusterFS作为共享存储(GlusterFS的部署方法参见8.3节的说明)。
为了完成MongoDB集群的搭建,需要部署以下三个资源对象。
◎ 一个StorageClass:用于StatefulSet自动为各个应用Pod申请PVC。
◎ 一个Headless Service:用于设置MongoDB实例的域名。
◎ 一个StatefulSet。
首先,创建一个StorageClass对象。storageclass-fast.yaml文件的内容如下:


运行kubectl create命令创建该StorageClass:

接下来,创建对应的Headless Service。mongo-sidecar作为MongoDB集群的管理者,将使用此Headless Service来维护各个MongoDB实例之间的集群关系,以及集群规模变化时的自动更新。mongo-headless-service.yaml文件的内容如下:

运行kubectl create命令创建该Headless Service:

最后,创建MongoDB StatefulSet。statefulset-mongo.yaml文件的内容如下:


对其中的主要配置说明如下。
(1)在该StatefulSet的定义中包括两个容器:mongo和mongo-sidecar。mongo是主服务程序,mongo-sidecar是将多个mongo实例进行集群设置的工具。mongo-sidecar中的环境变量如下。
◎ MONGO_SIDECAR_POD_LABELS:设置为mongo容器的标签,用于sidecar查询它所要管理的MongoDB集群实例。
◎ KUBERNETES_MONGO_SERVICE_NAME:它的值为mongo,表示sidecar将使用mongo这个服务名来完成MongoDB集群的设置。
(2)replicas=3表示这个MongoDB集群由3个mongo实例组成。
(3)volumeClaimTemplates是StatefulSet最重要的存储设置。在annotations段设置volume.beta.kubernetes.io/storage-class="fast"表示使用名为fast的StorageClass自动为每个mongo Pod实例都分配后端存储。resources.requests.storage=100Gi表示为每个mongo实例都分配100GiB的磁盘空间。
使用kubectl create命令创建这个StatefulSet:

最终可以看到StatefulSet依次创建并启动了3个mongo Pod实例,它们的名字依次为mongo-0、mongo-1、mongo-2:

StatefulSet会用volumeClaimTemplates中的定义为每个Pod副本都创建一个PVC实例,每个PVC实例的名称都由StatefulSet定义中volumeClaimTemplates的名称和Pod副本的名称组合而成,查看系统中的PVC便可以验证这一点:

下面是mongo-0这个Pod中的Volume设置,可以看到系统自动为其挂载了对应的PVC:

至此,一个由3个实例组成的MongoDB集群就创建完成了,其中的每个实例都拥有稳定的名称(DNS域名格式)和独立的存储空间。
登录任意一个mongo Pod,在mongo命令行界面用rs.status()命令查看MongoDB集群的状态,可以看到mongo集群已通过sidecar完成了创建。在集群中包含3个节点,每个节点的名称都是StatefulSet设置的DNS域名格式的网络标识名称:
◎ mongo-0.mongo.default.svc.cluster.local;
◎ mongo-1.mongo.default.svc.cluster.local;
◎ mongo-2.mongo.default.svc.cluster.local。
同时,可以看到3个mongo实例各自的角色(PRIMARY或SECONDARY)也都进行了正确的设置:




对于需要访问这个mongo集群的Kubernetes集群内部的客户端来说,可以通过Headless Service“mongo”获取后端的所有Endpoints列表,并组合为数据库链接串,例如“mongodb://mongo-0.mongo,mongo-1.mongo,mongo-2.mongo:27017/dbname_?”。
下面对MongoDB集群常见的两种场景进行操作,说明StatefulSet对有状态应用的自动化管理功能。
假设在系统运行过程中,3个mongo实例不足以满足业务的要求,这时就需要对mongo集群进行扩容。仅需要通过对StatefulSet进行scale操作,就能实现在mongo集群中自动添加新的mongo节点。
使用kubectl scale命令将StatefulSet设置为4个实例:

等待一会儿,看到第4个实例mongo-3创建成功:

进入某个实例查看mongo集群的状态,可以看到第4个节点已经加入:


同时,系统也为mongo-3分配了一个新的PVC用于保存数据,此处不再赘述,有兴趣的读者可自行查看系统为mongo-3绑定的Volume设置和后端GlusterFS共享存储的资源分配情况。
假设在系统运行过程中,某个mongo实例或其所在主机发生故障,则StatefulSet将会自动重建该mongo实例,并保证其身份(ID)和使用的数据(PVC)不变。
以mongo-0实例发生故障为例,StatefulSet将会自动重建mongo-0实例,并为其挂载之前分配的PVC“mongo-persistent-storage-mongo-0”。“mongo-0”服务在重新启动后,原数据库中的数据不会丢失,可继续使用。

进入某个实例查看mongo集群的状态,mongo-0发生故障前在集群中的角色为PRIMARY,在其脱离集群后,mongo集群会自动选出一个SECONDARY节点提升为PRIMARY节点(本例中为mongo-2)。重启后的mongo-0则会成为一个新的SECONDARY节点:

从上面的例子中可以看出,Kubernetes使用StatefulSet来搭建有状态的应用集群(MongoDB、MySQL等),同部署无状态的应用一样简便。Kubernetes能够保证StatefulSet中各应用实例在创建和运行的过程中,都具有固定的身份标识和独立的后端存储;还支持在运行时对集群规模进行扩容、保障集群的高可用等非常重要的功能。