在集中式开发时代,配置文件基本足够用了,因为那时配置的管理通常不会成为一个很大的问题,简单一点来说,系统上了生产之后,如果需要修改一个配置,登录到这台生产机器上,修改这个配置文件,然后reload配置文件并不是什么很大的负担。但是在互联网时代,我们的应用都是分布式系统,部署在N台机器上,如果在线上一台一台的重启机器,会造成很大的负担和不稳定。并且对于公司来说,会有多个环境区分(测试环境和线上环境),有时还需要对同一环境中的不同集群做不同的配置。因此需要一个配置中心来集中管理不同环境、不同集群的配置,修改配置后能够实时推送到应用端。
Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景。Apollo服务端基于Spring Boot和Spring Cloud开发,打包后可以直接运行,不需要额外安装Tomcat等应用容器。
多环境支持
在有赞内部,环境的区分为:
在Apollo的抽象里,配置支持环境和集群粒度的隔离。其中环境的隔离是物理的隔离,不同环境是需要单独的数据库来支持的,集群则是逻辑隔离,同一环境的不同集群的数据库是共享的。这就导致一个问题,有赞需要四个环境的隔离,但是底层的RDS只支持三个环境。
如何在这种约束条件下支持四个环境的配置隔离,我们的做法是在PROD环境下创建PRE集群,虚拟为PRE环境,这个方案能够解决问题,但是会带来大量的兼容性成本。这里的兼容性成本包括:
而且,因为这种方案本质上破坏了环境和集群的抽象,原有的关于环境的特性以及集群的特性也丧失了,比如虚拟出来的PRE环境不能创建集群,PRE环境的应用会因为集群的降级特性读取线上环境的配置。
总结来说,多环境的支持是Apollo在有赞的实践做的最不好的地方,之所以会这样,根本原因在于对Apollo的抽象没有理解清晰,所以出现了破坏抽象的定制。
跟ops系统深度集成除了动态配置,Apollo作为配置中心的另一个重要的特性是,配置的中心化管理,将业务配置跟非业务配置隔离开来。这样隔离的好处在于,业务开发不需要关心框架和中间件的配置了,框架和中间件也能够更容易的管理各自的配置。Apollo可以解决其他中间件的配置管理问题,那Apollo自身的配置管理要怎么解决呢?我们的解决方案是通过ops系统来管理。
下面是Apollo相关的配置,通过运维系统写到每个机器上,通过读取这个文件,可以识别到当前所在的环境、机房以及其他的信息。Apollo-client就是通过读取下面的信息来识别相关信息的。
方案的好处在于,能够减少业务方错误配置带来的答疑量。
双机房支持为了带来最高的稳定性,有赞线上核心应用都是双机房部署,双机房部署能够避免单点问题,增强稳定性。Apollo作为核心的组建,也需要支持双机房部署。
双机房部署要解决的主要问题是,数据如何在两个机房间同步,因为Apollo底层使用mysql存储配置数据,所以这个问题就变为不同机房的mysql数据库如何进行数据的同步,以及某个节点不可用的情况下如何切换。
这里有必要对有赞的RDS系统做一个介绍,RDS能够自动实现mysql的主从切换,应用通过RDS proxy来跟server交互,默认情况下,RDS proxy会将写流量路由到master,读流量按照配置进行路由。在master节点故障的时候,RDS可以自动实现主从切换,并将写流量路由到新的master节点。
双机房部署图如下:
有赞不仅仅作为一家SAAS公司,也涉及云的业务,具体说来,就是有赞会将核心业务沉淀为中台,中台暴露扩展点,外部开发者可以使用这些扩展点来定制自己的SAAS软件。外部开发者的应用通常托管在有赞的SAAS云上,这类云上的应用也有动态配置的需要,Apollo上云正是为了满足这个需求。
为了满足上云的需要,Apollo需要解决安全性问题,开源的版本缺乏对安全性方面的管控,具体体现在:
我们给出的解决方案是,给应用颁发凭证,Apollo server会校验凭证以验证应用的身份。同时扩展Namespace的模型,增加scope属性,用来限制该Namespace可被访问的应用列表,比如Namespace的scope为 [“A”,“B”],表明这个Namespace可以被A应用和B应用访问。
除了安全性问题,Apollo上云还需要解决另外一个问题,如何在一个环境中部署多个Apollo环境?因为云上的Apollo面向的是开发者,开发者使用的是有赞线上的环境,然而对于开发者应用,也是需要区分测试环境和线上环境的。这就带来一个需求,就是Apollo需要在线上环境支持开发者的测试环境和线上环境的配置隔离。下面展示了线上环境,Apollo的部署情况:
如上图所示,portal部署在aembd机房,configservice和adminservice在线上环境部署两个服务(由于公司发布系统的限制,一个应用在一个环境只能部署一个服务,所以要实现这种部署的话,是需要给adminservice和configservice分别申请两个应用的),分别作为开发者的DEV环境和PROD环境。baymax是面向开发者的控制台,portal并不直接面向开发者。这种实现方案带来了很大的运维成本,体现在需要额外申请多个应用和数据库,后续数据库DDL变更的成本更高。
其实Apollo的设计里面,是支持环境和集群两个纬度的配置隔离的,所以针对这种需求场景,是可以使用集群隔离的特性的。两个纬度的配置隔离的特性也体现出Apollo设计人员的前瞻性,这里仅仅是其中一个例子,另外一个例子是资源配置的托管,这个放在后续再做介绍。
Apollo的核心抽象Namespace,能够解决多个应用共享配置的需求,能够为这类多个应用共享的配置提供统一管理的入口。在有赞内部,各个中间件和框架的配置,都是由Apollo来集中管理的,比如dubbo、分布式锁、调用链等等。集中托管能够带来很多的好处,比如减少业务方的配置成本以及因为配置错误引起的答疑量, 便于后续对配置的变更。
这里可以举一个例子,大家知道现在service-mesh的概念很火,service-mesh能够很好的解决多语言调用的问题,而有赞内部除了java以外,node也是一个主要的开发语言。所以公司开发了一套service-mesh组件,并且急需要将java语言的rpc框架替换成service-mesh的解决方案。这种例子中,rpc框架迁移到service-mesh只需要修改rpc框架的某些配置项,因为rpc框架的配置是集中管理的,所以修改很容易。值得一提的是,Apollo对于这种修改场景,为了保证稳定性,还提供了强大的灰度特性。
资源的概念可以理解为应用运行时依赖的基础组件,比如数据库、消息中间件、缓存系统等等。一个完整的运维流程包括,资源的申请、资源的配置、资源的监控、资源的回收等等。
在使用Apollo托管资源配置之前,有赞的资源配置是托管在另外一个静态配置系统的,还有另外相当大的一部分是脱离管控的,散落在应用代码中。在公司的静态配置系统中,应用对资源配置的引用是通过复制的方式,而非引用的方式,对于资源的管理者,看不到使用该资源配置的应用,对资源配置的变更也需要推动业务方去修改应用的拷贝,对于散落在应用代码中的配置,要推动改造就更加不可能了。
除了配置管理方面的问题,针对数据库的配置,有对用户隐藏的需求,直接把用户名、密码暴露出去,容易带来不可控的风险。解决这个问题的方法是使用统一资源名,业务方只需要感知统一资源名,配置中心将统一资源名跟具体的资源配置映射。
项目的整体架构图如下,在这个项目中,Apollo配置中心只是整个资源运维流程的一步,承担了资源配置的统一管理、配置脱敏、变更通知等重要功能。
有赞有一套自己的ops系统,所有应用的管理、发布,中间件的申请,数据库权限申请等都在这个平台。所以如果把Apollo控制台的功能迁移统一维护的ops系统,可以大大的增加维护和管理,提升用户体验,并且有助于后续的继续迭代。但是如果需要挨个对接之前Apollo的接口,需要很多的工作量,而且传递的参数也有可能因为不一致导致抛错。除此之外,对于有赞线上不同机房的部署,希望能在ops统一展示不同机房的名称,而Apollo默认就是default集群。
为了解决这个问题,我们在Apollo之前加了一层代理(Apollo-ops),ops系统所有的请求都会发到Apollo-ops,再由Apollo-ops统一转换成Apollo的http请求报文格式,获取请求结果。对于特殊的请求和新增操作Apollo的接口,可以Apollo-ops添加接口,这样可以减少对Apollo源码的侵入。ops控制台界面如图所示:
上图简要描述了 Apollo 的总体设计,从下往上看:
为了简化部署,我们实际上会把Config Service、Eureka和Meta Server三个逻辑角色部署在同一个JVM进程中。
Namespace是配置项的集合,类似于一个配置文件的概念。Namespace的获取权限分为两种:private(私有的)和public(公共的)。private权限的Namespace,只能被所属的应用获取到。一个应用尝试获取其它应用private的Namespace,Apollo会报“404”异常。public权限的Namespace,能被任何应用获取到。
Namespace类型有三种:
私有的Namespace具有private权限,默认的“application”的Namespace就是私有类型的。
公共类型的Namespace具有public权限。公共类型的Namespace相当于游离于应用之外的配置,且通过Namespace的名称去标志公共Namespace,所以公共的Namespace的名称必须全局唯一。
如果在不同部分需要共享配置获取中间件客户端需要共享时,可以使用公共类型的Namespace。关联类型又可以称为继承类型,关联类型具有private权限。关联类型的Namespace继承于公共类型的Namespace,用于覆盖公共的Namespace的某些配置项。
Client通过轮询的方式,从Config Service读取配置。Client的轮询包含两部分:
Apollo提供了一套的Http REST接口,使第三方应用能够管理配置。虽然Apollo系统本身提供了Portal来管理配置,但是在一些特殊的场景下,需要程序自己去管理。第三方应用负责人需要提供一些第三方应用基本信息来生成一个Token。
Token获取成功后,可以给Token绑定可以操作的特定Namespace,或者直接赋予整个应用下所有Namespace的权限。
在有赞,Apollo分为4个环境,分别是daily、qa、pre、prod,在不同环境下可以分别创建不同的集群,在不同集群下可以创建3中类型的Namespace(私有、公共、关联)。
Namespace名称全局唯一,创建需要项目管理员的权限,创建页面如下:
成功创建Namespace后,可以点击新增配置来创建配置项,创建完后,提交配置项:
配置只要在发布后才会真的被应用使用到,所以在编辑完配置后,需要发布配置。
Apollo已经是一套很成熟的开源配置管理中心软件,但是由于技术的更新,在一些技术细节上能有更好的优化。
领取专属 10元无门槛券
私享最新 技术干货