今天为大家带来《你好,我是EverDB!系列文章-容器化之旅。容器天生具有快速、轻量、易于调度的特点,非常适合模拟故障场景,所以EverDB容器化是我们的第一步。本文将介绍基于k8s的EverDB容器化实现方案。为什么选择k8sk8s,全称Kubernetes,是一个开源的、基于容器的分布式架构解决方案,提供自动化的容器部署、管理、编排、伸缩等能力,让应用容器化变得更简单、更高效。同时,k8s平台在故障转移、资源调度、隔离、负载均衡等方面的特点更符合EverDB自身的架构特点和测试管理需求。因此选择了基于k8s的EverDB容器化方案。部署到k8s的技术路线Helm工具Helm是k8s的包管理器,类似于我们在Ubuntu中使用的apt和在Centos中使用的yum,可以快速查找、下载和安装软件包。在Helm中,最重要的一个应用包叫做Charts,它是一个应用的定义描述,包括应用的一些元数据,以及应用的k8s资源定义模板和配置。在Charts足够完整的情况下,只需要简单的安装即可快速部署服务。OperatorideaOperator是一种以k8s原生方式管理应用的实现思路。它通过k8s扩展API,使用CRD自定义资源对象,实现相应的controller来实现应用的部署和管理。Helm和Operator在k8s应用管理上各有优势。前者的优点是可以将资源模板化,方便在不同配置下共享和重用;后者更针对复杂应用程序的自动管理。本次部署到k8s平台,综合考虑实施成本和当前需求,决定实施基于Helm的EverDB容器化方案。整体方案在k8s平台上,应用分为有状态和无状态。EverDB的数据节点MySQL、调度节点Grid、配置节点ZooKeeper都需要维护运行状态参数,对外提供稳定的服务。数据节点和配置节点也需要将数据和配置信息持久化到存储,所以部署到k8s就是StatefulSet类型的有状态应用。EverDB架构图EverDB三个组件设计中需要配置的k8s资源如下表所示:PodController类型ConfigMapNodePortHeadlessService持久化存储辅助容器MysqlStatefulSetZookeeperStatefulSet○○dbscaleStatefulSet○○MySQL是底层数据存储EverDB的引擎。部署到k8s时,MySQL除了具备数据实例配置、实例初始化、数据持久化存储、对外访问服务等功能外,还需要具备监控、备份等辅助容器;ZooKeeper作为EverDB的配置管理节点,其管理的配置信息也需要持久化存储,对外提供访问服务;Grid作为调度节点,其元数据存储在底层数据节点上,配置参数保存到远程配置节点ZooKeeper上,即使出现Pod故障,Grid也可以从ZooKeeper中拉取配置信息,所以它不需要持久的PV存储数据。服务接入EverDB需要对外提供数据库服务,内部组件也需要能够相互通信。那么每个Pod都是独立运行的,它们之间的连接由谁来建立呢?这里会在k8s的核心资源对象中引入Service。Service是一个抽象概念,它定义了Pod的逻辑集合以及访问它们的负载均衡策略。k8s的服务可以在集群内部定义一个服务访问入口地址(ClusterIP)。Service和Pod通过LabelSelector关联,应用通过这样一个入口地址访问其背后的一组Pod实例。这样当Pod销毁或重构导致PodIP发生变化时,Service可以自动感知,提供的ClusterIP不会发生变化,我们仍然可以通过Service访问后端Pod。对于EverDB集群内部节点之间的通信,需要实现一对一的通信,不受PodIP变化的影响。例如EverDB集群中的Mysql主从实例,在进行主从同步时,从实例(Slave)需要能够直接访问主实例(Master)的确切Pod,不需要负载balancing,和master同步不受影响。显然,上述Service的定位并不适合这样的场景。不用担心,k8s还设计了一种特殊的Service类型,HeadlessService(无头服务)。HeadlessService没有分配ClusterIP,访问者可以通过解析Service的DNS来获取Pod的地址,就像访问域名一样。HeadlessServie的域名一般是“{podname}.{headlessservice}.{namespace}”的形式。与Deployment类型Pod的随机podname相比,StatefulSet类型Pod的podname格式为{StatefulSetname}-{固定数字},这使得即使Pod重启、节点迁移等,域名本身也不会改变。EverDB数据节点部署到k8s示例图因此,对于EverDB集群,我们使用基于ClusterIP类型的Service,对外提供EverDB数据库服务的统一入口,内部提供多个Grid调度节点的负载均衡能力;使用HeadlessService实现EverDB集群内部节点。通信能力,不受PodIP变化影响。持久化存储EverDB作为一个有状态的应用,部署在k8s平台上需要解决存储问题,即当应用Pod被删除或重新创建时,内部数据不会丢失。PV(PersistentVolume)可以看成是k8s集群可用的存储资源,PVC(PersistentVolumeClaim)是对存储资源的需求。对于存储资源,k8s平台支持两种供给模式:静态模式(Static)和动态模式(Dynamic)。在EverDB集群中,已经支持这两种供给模式。在静态模式下,集群管理员需要手动创建PV。EverDB使用基于LocalPV方式的持久化存储。这种方式主要用于生产环境。LocalPV对应的存储介质通常是宿主机上额外挂载的。磁盘,实现“一个PV,一个磁盘”,不仅可以有效减少主机宕机造成的数据丢失,还可以增强集群存储扩展的能力。静态模式下LocalPV和PVC示意图动态模式下,EverDB采用基于HostPath的方式,主要用于开发测试环境,使用宿主机本地目录,有效避免IO开销,具有更高的读写能力写入性能。同时,为了避免单机测试的问题,结合Github开源项目LocalPathProvisioner,可以有效利用集群节点中的本地存储。通过SrorgeClass(存储类)的设置,只需要PVC声明存储类型,系统会自动完成PV。创建和绑定。动态模式下的StorageClass、PV和PVC示意图配置管理容器启动时总是需要一些参数。给容器中的应用传递参数通常有以下几种方式:1、直接将配置文件打包到镜像中;2.在定义pod时,添加自定义命令行参数,并设置args:["commandparameters"];3.使用环境变量向Pod中的应用程序传递参数来修改配置。ConfigMap的设计是将镜像与配置文件解耦,从而实现镜像的可移植性和可复用性。ConfigMap实际上是一系列配置信息的集合。ConfigMap有两种方式向容器中注入配置参数:1.直接在ConfigMap中定义环境变量。Pod启动时,使用env引用ConfigMap中定义的环境变量;2.封装一个完整的配置文件,将共享卷挂载到Pod中给应用传递参数。EverDB的三个组件的Pod在容器拉起的时候都需要配置性能参数,主从信息等。EverDB在配置容器运行参数时,对于复杂的配置文件信息,使用ConfigMap创建配置文件。Pod启动时,会挂载到容器中,一些简单的运行参数,都是通过环境变量来实现的。表单被注入到容器中。根据EverDB的上述组件对ConfigMap、StatefulSet、持久化存储、服务访问等的要求创建相应的Helm模板,将EverDB的组件打包为Chart包。在启动子组件时,只需要更改一些必要的参数设置后,即可完成定制化的EverDB集群安装。即避免了手工部署容易出错的问题,方便了集群在k8s上的定制和快速部署,稳定高效,灵活优雅。结语EverDB容器化的实现,不仅方便了混沌实验中的故障注入,也让我们在数据库云化的道路上迈出了里程碑式的一步!
