做者 | 夏梓耀
杏仁后端工程师,励志成为计算机艺术家mysql
首先简单介绍一下什么是配置中心,咱们为何须要它,为何要花力气去完善它。git
传统单体应用( monolithic apps )因种种潜在缺陷,如:随着规模的扩大,部署效率逐渐下降,团队协做效率差,系统可靠性变差,维护困难,新功能上线周期长等,迫切须要一种新的架构去解决这些问题,而微服务( microservices )架构正是当下一种正确的解法。spring
不过,解决一个问题的同时,每每会诞生出不少新的问题,故:微服务化的过程当中伴随着不少的挑战,其中一个挑战就是有关服务(应用)配置的。sql
当系统从一个单体应用,被拆分红分布式系统上一个个服务节点后,配置文件也必须更着迁移(分割),这样配置就分散了,不只如此,分散中还包含着冗余,冗余分两方面:服务与服务之间(如:有 A,B,C 三个服务调用 D 服务,那么 D 服务的地址会被复制三份,由于 A,B,C 三个服务是 share nothing 的),同服务实例之间(如:A服务的全部实例都是同样的配置,且它们在物理上颇有多是分散的,即:不在一台机器上)。数据库
在单体应用时期,咱们管理配置只须要考虑环境(develop,test,staging,producting...)这一个维度,那么如今就多了服务(应用)这个维度。 再明确一下上面说的问题:配置文件分散且冗余,映射到配置管理上就是:分散意味着须要一个个处理,冗余意味着重复操做。后端
为了解决这个问题,配置必须集中管理(逻辑上分散,物理上集中),而实现这个功能的系统组件(或基础设置)就是配置中心。安全
既然集中管理,那么就是要将应用的配置做为一个单独的服务抽离出来了(配置再也不和应用一块儿进代码仓库),同理也须要解决新的问题,好比:版本管理(为了支持回滚),权限管理,灰度发布等网络
以上讨论的还都停留在静态配置的层面上,而应用除了静态的配置(如:数据库配置,一些云服务的参数配置,服务启动后通常不会变更),还会有一些动态的配置(如:灰度开关,一些常量参数:超时时间,限流阈值等),还有理论上:架构
在一个大型的分布式系统中,你没有办法把整个分布式系统停下来,去作一个软件的、硬件的或者系统的升级app
业务需求的一些自然动态行为(如:一些运营活动,会动态调整一些参数),加之理论上必需要支持这个特性,因此,配置中心服务还得支持动态特性,即:配置热更新。
简单总结一下,在传统巨型单体应用纷纷转向细粒度微服务架构的历史进程中,服务配置中心是微服务化不可缺乏的一个系统组件,其解决的就是:分布式系统的动态配置问题。
那么咱们是怎么解决的呢?那就是 Matrix 1.0 的故事了。
的配置文件都交给 Matrix 管理,拥有环境隔离,版本控制,权限管理等功能。
配置文件是在 CI 构建阶段静态注入的,不一样环境注入相应的配置文件,对不一样的 build 工具(如:maven,sbt)都实现了配置注入的插件,来从 Matrix 上拉取配置文件
那么为何还要进行 Matrix 2.0 的工做呢?Matrix1.x 有什么缺陷吗?
在服务(应用)数很少的状况下,Matrix1.x 是彻底够用的,可是随着业务规模的发展,问题会渐渐暴露出来。
Matrix 并无解决配置冗余的问题,咱们须要相似模版的东西,将冗余部分抽取成一份并共享,下降配置维护成本。
其次,CI 打包与配置注入耦合,意味着打包与环境耦合,一个环境对应一个包(镜像),这其实违背了容器的"一份镜像处处运行"的理念,即:镜像与环境无关,应用须要什么环境的配置,在启动阶段肯定(注入)就能够了。
最后是没有配置动态(热)更新能力,前面已说过这个也是必需要支持的;只有咱们的基础设施是完美的,其之上的业务才能是完美的。
全部问题,在服务规模小(管理/运维复杂度低)时,都不是问题。
随着业务复杂度的不断提升,和微服务架构的不断深化,现有的 Matrix 1.x 版本暴露出不少缺失的关键能力(好比权限管理,配置模板,热发布等),逐渐成为产品快速迭代的瓶颈之一,咱们亟需对 Matrix 进行升级改造,打造一个更强大、更易用的微服务配置中心。
因此咱们须要 Matrix 2.0。
不过开始作以前,仍是得先静下心来评估一下,是继续走自研,仍是选择别人开源的进行二次开发?我作了一段时间的调研,显然继续自研的开发 cost 是巨大的,核心功能如:配置热更新,灰度发布,配置模版(去冗余)都得从零开始开发, 其次还要保证高性能,高可用等非功能性需求。
二次开发的选择其实也很少(这并非什么坏事),参考网络上已有的对比与讨论,可得出如下结论:
注册中心 | 配置存储 | 时效性 | 数据模型 | 维护性 | 优势 | 缺点 |
---|---|---|---|---|---|---|
disconf | zookpeer | 实时推送 | 支持传统的配置文件模式,亦支持 KV 结构数据 | 提供界面操做 | 基于分布式的 Zookeeper 来实时推送稳定性、实效性、易用性上均优于其余 | 代码复杂, 2016 年 12 月以后没有更新,功能不够强大,不能彻底知足咱们的需求 |
zookpeer | zookpeer | 实时推送 | 支持传统的配置文件模式,亦支持 KV 结构数 | 命令操做 | 实时推送稳定性、实效性 | 太底层,开发量大 |
diamond | mysql | 每隔15s拉一次全量数据 | 只支持 KV 结构的数据 | 提供界面操 | 简单、可靠、易用 | 数据模型不支持文件,使用不方便 |
Spring Cloud Config | git | 人工批量刷新 | 文件模式 | Git 操做 | 简单、可靠、易用 | 须要依赖 GIT,而且更新 GIT |
Apollo | mysql | 实时推送 + 定时拉取 | 支持传统的配置文件模式,亦支持 KV 结构数 | 提供界面操 | 架构设计和稳定性考虑比较完善,很多大厂在用,Portal 的体验不错, 更新活跃 | 总体架构略显复杂,和咱们容器环境不太一致 |
就目前看来"真正能打的"就 Apollo 了,因为自研成本较大,而且 Apollo 的代码也并不复杂(是标准的 Spring Boot 项目),其功能也基本上能覆盖咱们的需求,因此咱们最后选择基于 Apollo 进行二次开发。
对于不了解 Apollo 的同窗,下面简单介绍一下,详情可参考官方文档
Apollo(阿波罗)是携程框架部门研发的开源配置管理中心,可以集中化管理应用不一样环境、不一样集群的配置,配置修改后可以实时推送到应用端,而且具有规范的权限、流程治理等特性。
其 Portal 界面的截图以下:
目前其最新版本(1.0.0)主要提供的功能有:
能够看到 Apollo 提供了不少强大的功能,解决了 Matrix 1.0 待解决的问题;锦上添花的是其对 Spring 项目的支持很是好,这又能大大节省开发时间。
二次开发主要是作适应咱们环境的定制改造,而非添加大的新功能(至少这个阶段不会),首当其冲的就是部署模式的改变,下图是 Apollo 的架构图:
这个图是从上往下看的,第一层是 client,也就是与应用集成的 SDK,还有 Protal(包括本身的 server 和 db),它们经过一层 SLB 访问到 Meta Server,而Meta Server 是 Eureka(服务注册中心)的一层封装,用于发现 Config Service 和 Admin Service(它们启动时会向 Eureka 注册本身),也就是最后一层,这两个服务共同管理着咱们的配置,配置则存储在 ConfigDB 中。
外面还有一条 Client 到 Config Service 的箭头,就是其实时推送机制的实现原理:Client 经过(HTTP)Long Polling的方式,不停的询问 Config Service,若是配置有更新(发布)则会当即返回,无更新则返回 HTTP 状态码 304。
而咱们的服务都是部署在 Kubernetes 集群上的,Kubernetes 有本身的服务发现功能,包括 LB 功能,因此与 Eureka 重复了,须要将 Eureka 剥离出来,将 Meta Server 去掉,咱们的部署方式以下图:
全部非生产环境对应一个 portal,每一个环境独立部署 config service 和 admin service,其中生产环境在单独的 k8s 集群中(单独对生产环境作了隔离,是由于 Apollo 的权限管理还不够强大,不支持区分环境的访问权限)。
这个改造分三部分,第一部分和第二部分是分别去除 Protal 和 Client 对 Meta Server 对依赖,即:去除在 Client 和 Protal 中配置的 Meta server 地址,而是直接使用 Kubernetes 暴露的服务域名(开发和 local 环境由于须要本地访问,有公网地址)。
第三部分是去除 Config Service 上的 Eureka Server,Admin Service 上的 Eureka Client,由于 Eureka 很是成熟易用,且是个声明式(基于注解和配置)的框架,因此改动起来并不麻烦,咱们只是将 Eureka 的注解去掉了而已,没有把依赖去掉(若是要去掉依赖,那么工做量会很大,由于依赖的地方多,整个 Meta Server 都是对其 Client API 的封装)。
惟一的坑就是记得在配置文件中把spring.cloud.discovery.enabled设置为 false,由于 Protal 对 Admin Service 的健康检查是基于spring boot actuator,而它是自动的,检测到有 Eureka 的依赖,就会启动相关健康检查的 Endpoint。
若是想作的好一点,能够作个配置开关,启动时配置是否启用 Eureka,固然这样工做量会很大,或许官方会考虑(毕竟容器编排系统是趋势,大中小厂都会用)。
其次咱们还添加了 LOC 环境(针对本地开发),UT 环境(针对单元测试),这方面 Apollo 有方便自定义环境的方法提供。不过咱们并不满意官方推荐的本地开发模式,咱们但愿配置中心对本地开发是透明的,对于 Spring Boot 项目,原来是基于profile 覆盖的,那么如今仍是 profile 覆盖的方式,这是最好的。然而 SDK 是将 Apollo 上拉取的配置覆盖在 Spring 的配置上的,即 Spring 的 profile 机制会失效,经讨论后决定在 Client 启动时:若是检测到当前是 LOC 环境,则将 Apollo 和 Spring 的配置覆盖顺序倒置。
这里简单的说一下实现原理,SDK 对 Spring 项目的集成是经过往 Environment 的 propertySourceList 中以 addFirst 的方式添加本身的配置,放在了查找链的最前面,故达到了覆盖一切的目的,而咱们在检测到是 LOC 环境时以 addLast 的方式添加 Apollo 上的配置,就不会覆盖任何配置了,原来的 profile 机制依然有效。
还有一个坑,就是 SDK 的初始化时间问题,或者说是 Client 拉取配置的时间点问题;任何应用都有框架级别的配置,而有些配置是在一个很是早的时间点生效的,这是"应用启动时读取配置"这种方式必须考虑的问题,Spring Boot 项目的 Logging System 就是初始化早于 Apollo 的配置初始化(覆盖)的例子,也就是说:关于 log 的配置,不会生效。
咱们解决的方式就是在 Apollo 配置初始化以后,从新初始化这些模块,使得 Apollo 的配置生效,这种解法的副产物是:咱们使得这些模块的配置具备了动态性(咱们能够安全的在运行期去从新初始化这些模块)
为此咱们封装了本身的 SDK:vapollo(依赖于 apollo-client 的 SDK),提供定制功能。
二次开发结束后,接着面对的问题就是配置迁移问题,由于咱们选择了对 Apollo 进行二次开发,而不是对 Matrix1.x 进行扩展,故:咱们须要将配置迁移过去。
咱们编写了详细的配置迁移操做手册(主要涵盖了操做流程和配置规范),并设定了迁移计划,以后项目的配置会慢慢所有迁移过去,为了更安全的实施这个过程,咱们在实际迁移一个简单项目后,发现还能够编写工具来帮助咱们,好比配置的 diff(检查是否遗漏项,不一样项)。
Matrix2.0 的工做暂时告一段落,本文虽然是总结工做内容的,可是最后我但愿跳出眼前的工做,看一看将来,从而了解咱们如今的不足之处。
主要分两部分:Matrix2.x 和 Matrix3.0
Matrix2.x 指现阶段工做的延续,就目前来讲(实际使用一段时间后),还有不少操做上的可优化点(好比:添加更多的默认设置,让开发人员对 Apollo 更加无感知),其次是关于配置的规范也会不断的完善(什么环境必需要有那些配置,namespace 的建立和关联),甚至会有完善的配置发布流程,有了规范,就会有 review,config review(就像代码 review 那样)
世界是变化的,现阶段的工做只是符合了咱们如今的环境,以后会怎样?咱们的微服务架构还在发展,咱们还没上 service mesh,现在基础设置都在不断的下沉,咱们的配置中心须要怎样?我想也是同样的,对开发来讲:咱们对配置的存在会愈来愈无感知,我不关心这个配置项是哪来的,不关心当前是何种部署环境,不关心配置项是变化仍是不变的,我只关心用到它的业务是怎样的;另外一方面对配置的管理和维护也应该愈来愈智能,愈来愈自动。但愿 Matrix3.0 能实现这些目标。