一. 背景和 Apollo 简介
在集中式开发时代,配置文件基本足够用了,因为那时配置的管理通常不会成为一个很大的问题,简单一点来说,系统上了生产之后,如果需要修改一个配置,登录到这台生产机器上,修改这个配置文件,然后 reload 配置文件并不是什么很大的负担。但是在互联网时代,我们的应用都是分布式系统,部署在 N 台机器上,如果在线上一台一台的重启机器,会造成很大的负担和不稳定。并且对于公司来说,会有多个环境区分(测试环境和线上环境),有时还需要对同一环境中的不同集群做不同的配置。因此需要一个配置中心来集中管理不同环境、不同集群的配置,修改配置后能够实时推送到应用端。
Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景。Apollo 服务端基于 Spring Boot 和 Spring Cloud 开发,打包后可以直接运行,不需要额外安装 Tomcat 等应用容器。
二. Apollo 在有赞的实践
2.1 部署方案
多环境支持
在有赞内部,环境的区分为:
DAILY:日常环境(供开发自测的环境)
QA:测试基准环境
PRE:预发环境(上线前的验证)
PROD:线上环境
在 Apollo 的抽象里,配置支持环境和集群粒度的隔离。其中环境的隔离是物理的隔离,不同环境是需要单独的数据库来支持的,集群则是逻辑隔离,同一环境的不同集群的数据库是共享的。这就导致一个问题,有赞需要四个环境的隔离,但是底层的 RDS 只支持三个环境。
如何在这种约束条件下支持四个环境的配置隔离,我们的做法是在 PROD 环境下创建 PRE 集群,虚拟为 PRE 环境,这个方案能够解决问题,但是会带来大量的兼容性成本。这里的兼容性成本包括:
client 识别环境和机房的逻辑需要兼容
控制台的 UI 需要做调整
开放平台的 sdk 需要写兼容逻辑
而且,因为这种方案本质上破坏了环境和集群的抽象,原有的关于环境的特性以及集群的特性也丧失了,比如虚拟出来的 PRE 环境不能创建集群,PRE 环境的应用会因为集群的降级特性读取线上环境的配置。
总结来说,多环境的支持是 Apollo 在有赞的实践做的最不好的地方,之所以会这样,根本原因在于对 Apollo 的抽象没有理解清晰,所以出现了破坏抽象的定制。
跟 ops 系统深度集成除了动态配置,Apollo 作为配置中心的另一个重要的特性是,配置的中心化管理,将业务配置跟非业务配置隔离开来。这样隔离的好处在于,业务开发不需要关心框架和中间件的配置了,框架和中间件也能够更容易的管理各自的配置。Apollo 可以解决其他中间件的配置管理问题,那 Apollo 自身的配置管理要怎么解决呢?我们的解决方案是通过 ops 系统来管理。
下面是 Apollo 相关的配置,通过运维系统写到每个机器上,通过读取这个文件,可以识别到当前所在的环境、机房以及其他的信息。Apollo-client 就是通过读取下面的信息来识别相关信息的。
APPLICATION-STANDARD-ENV=qa
APPLICATION-SERVICE-CHAIN=qa
APPLICATION_NAME=carmen-console-ng
APPLICATION_IDC=qabb
APOLLO_METASERVER=apollo-metaserver-qa.s.qima-inc.co
方案的好处在于,能够减少业务方错误配置带来的答疑量。
双机房支持为了带来最高的稳定性,有赞线上核心应用都是双机房部署,双机房部署能够避免单点问题,增强稳定性。Apollo 作为核心的组建,也需要支持双机房部署。
双机房部署要解决的主要问题是,数据如何在两个机房间同步,因为 Apollo 底层使用 mysql 存储配置数据,所以这个问题就变为不同机房的 mysql 数据库如何进行数据的同步,以及某个节点不可用的情况下如何切换。
这里有必要对有赞的 RDS 系统做一个介绍,RDS 能够自动实现 mysql 的主从切换,应用通过 RDS proxy 来跟 server 交互,默认情况下,RDS proxy 会将写流量路由到 master,读流量按照配置进行路由。在 master 节点故障的时候,RDS 可以自动实现主从切换,并将写流量路由到新的 master 节点。
双机房部署图如下:
2.2 上云
有赞不仅仅作为一家 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 设计人员的前瞻性,这里仅仅是其中一个例子,另外一个例子是资源配置的托管,这个放在后续再做介绍。
2.3 中间件配置托管
Apollo 的核心抽象 Namespace,能够解决多个应用共享配置的需求,能够为这类多个应用共享的配置提供统一管理的入口。在有赞内部,各个中间件和框架的配置,都是由 Apollo 来集中管理的,比如 dubbo、分布式锁、调用链等等。集中托管能够带来很多的好处,比如减少业务方的配置成本以及因为配置错误引起的答疑量, 便于后续对配置的变更。
这里可以举一个例子,大家知道现在 service-mesh 的概念很火,service-mesh 能够很好的解决多语言调用的问题,而有赞内部除了 java 以外,node 也是一个主要的开发语言。所以公司开发了一套 service-mesh 组件,并且急需要将 java 语言的 rpc 框架替换成 service-mesh 的解决方案。这种例子中,rpc 框架迁移到 service-mesh 只需要修改 rpc 框架的某些配置项,因为 rpc 框架的配置是集中管理的,所以修改很容易。值得一提的是,Apollo 对于这种修改场景,为了保证稳定性,还提供了强大的灰度特性。
2.4 资源配置托管
资源的概念可以理解为应用运行时依赖的基础组件,比如数据库、消息中间件、缓存系统等等。一个完整的运维流程包括,资源的申请、资源的配置、资源的监控、资源的回收等等。
在使用 Apollo 托管资源配置之前,有赞的资源配置是托管在另外一个静态配置系统的,还有另外相当大的一部分是脱离管控的,散落在应用代码中。在公司的静态配置系统中,应用对资源配置的引用是通过复制的方式,而非引用的方式,对于资源的管理者,看不到使用该资源配置的应用,对资源配置的变更也需要推动业务方去修改应用的拷贝,对于散落在应用代码中的配置,要推动改造就更加不可能了。
除了配置管理方面的问题,针对数据库的配置,有对用户隐藏的需求,直接把用户名、密码暴露出去,容易带来不可控的风险。解决这个问题的方法是使用统一资源名,业务方只需要感知统一资源名,配置中心将统一资源名跟具体的资源配置映射。
项目的整体架构图如下,在这个项目中,Apollo 配置中心只是整个资源运维流程的一步,承担了资源配置的统一管理、配置脱敏、变更通知等重要功能。
2.5 接入 OPS 系统提升产品体验
有赞有一套自己的 ops 系统,所有应用的管理、发布,中间件的申请,数据库权限申请等都在这个平台。所以如果把 Apollo 控制台的功能迁移统一维护的 ops 系统,可以大大的增加维护和管理,提升用户体验,并且有助于后续的继续迭代。但是如果需要挨个对接之前 Apollo 的接口,需要很多的工作量,而且传递的参数也有可能因为不一致导致抛错。除此之外,对于有赞线上不同机房的部署,希望能在 ops 统一展示不同机房的名称,而 Apollo 默认就是 default 集群。
为了解决这个问题,我们在 Apollo 之前加了一层代理(Apollo-ops),ops 系统所有的请求都会发到 Apollo-ops,再由 Apollo-ops 统一转换成 Apollo 的 http 请求报文格式,获取请求结果。对于特殊的请求和新增操作 Apollo 的接口,可以 Apollo-ops 添加接口,这样可以减少对 Apollo 源码的侵入。ops 控制台界面如图所示:
三. Apollo 架构与设计
3.1 Apollo 架构图
上图简要描述了 Apollo 的总体设计,从下往上看:
Config Service 提供配置的读取、推送等功能,服务对象是 Apollo 客户端
Admin Service 提供配置的修改、发布等功能,服务对象是 Apollo Portal(管理界面)
通过 Apollo 的发布界面可以多环境、集群管理配置
Config Service 和 Admin Service 都是多实例、无状态部署,所以需要将自己注册到
Eureka 中并保持心跳,在 Eureka 之上架了一层 Meta Server 用于封装 Eureka 的服务发现接口。Apollo 提供了 MetaServiceProvider SPI,用户可以注入自己的 MetaServiceProvider 来自定义 Meta Server 定位逻辑
Client 通过域名访问 Meta Server 获取 Config Service 服务列表(IP+Port),而后直接通过 IP+Port 访问服务,同时在 Client 侧会做 load balance、错误重试
为了简化部署,我们实际上会把 Config Service、Eureka 和 Meta Server 三个逻辑角色部署在同一个 JVM 进程中。
3.2 Apollo 核心设计
3.2.1 Namespace 设计
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 的某些配置项。
3.2.2 Client 端实现
Client 通过轮询的方式,从 Config Service 读取配置。Client 的轮询包含两部分:
RemoteConfigRepository,定时轮询 Config Service 的配置读取 /configs/{appId}/{clusterName}/{namespace:.+}接口。
RemoteConfigLongPollService,长轮询 Config Service 的配置变更通知/notifications/v2 接口。当有新的通知时,触发 RemoteConfigRepository,立即轮询 Config Service 的配置读取/configs/{appId}/{clusterName}/{namespace:.+}接口。整体流程如图所示:
3.2.3 Apollo 开放平台
Apollo 提供了一套的 Http REST 接口,使第三方应用能够管理配置。虽然 Apollo 系统本身提供了 Portal 来管理配置,但是在一些特殊的场景下,需要程序自己去管理。第三方应用负责人需要提供一些第三方应用基本信息来生成一个 Token。
Token 获取成功后,可以给 Token 绑定可以操作的特定 Namespace,或者直接赋予整个应用下所有 Namespace 的权限。
3.3 Apollo 控制台
在有赞,Apollo 分为 4 个环境,分别是 daily、qa、pre、prod,在不同环境下可以分别创建不同的集群,在不同集群下可以创建 3 中类型的 Namespace(私有、公共、关联)。
Namespace 名称全局唯一,创建需要项目管理员的权限,创建页面如下:
成功创建 Namespace 后,可以点击新增配置来创建配置项,创建完后,提交配置项:
配置只要在发布后才会真的被应用使用到,所以在编辑完配置后,需要发布配置。
四. 未来展望
Apollo 已经是一套很成熟的开源配置管理中心软件,但是由于技术的更新,在一些技术细节上能有更好的优化。
webSocket 替代 http 长轮询:Apollo 在获取配置信息时,会发起一个长轮询,即客户端发送一个超时时间很长的 Request,服务器 hold 住这个连接(Apollo 默认是 30s),在有新数据达到时返回 Response。http1.1 的连接默认使用长连接,虽然长连接在一个 TCP 连接上可以传输多个 Request/Response 消息对,但是本质上还是会造成资源的浪费、实时性不强等问题。而 webSocket 只需要建立一次 Request/Response 消息对,之后都是 TCP 连接,避免了需要多次建立 Request/Response 消息对而产生的冗余头部信息。当 Apollo 配置被修改后,服务端可以通知客户端,客户端再来获取最新配置,整个流程可以在一个 webSocket 中进行。
配置中心统一:将公司的静态配置中心和动态配置中心融合起来,使用 apollo 替换 scm 的配置中心,这样做有两个好处,一是 apollo 相对于公司的静态配置中心,提供了更强大的配置集中管理功能,可以提升基础组件的配置管理能力。另一个原因是减少业务方的使用成本和开发的维护成本。
用 ETCD 替换 Eureka 注册中心:这个规划主要考虑到两点原因,一是将注册中心的功能从 configservice 中解耦出来,解耦出来之后 configservice 就可以切换到容器中,运维成本降低。另一个原因是因为 ETCD 是公司核心的注册中心组件,稳定性更高。
本文转载自公众号有赞 coder(ID:youzan_coder)。
原文链接:
评论