7

只是为了好玩,我正在使用微服务架构设计一些 Web 应用程序。我正在尝试确定进行配置管理的最佳方法,并且我担心我的配置方法可能存在一些巨大的缺陷和/或存在更好的东西。

为了解决这个问题,假设我有一个用 c++ 编写的身份验证服务、一个用 rust 编写的身份服务、一个用 haskell 编写的分析服务、一些用 scala 编写的中间层和一个用 javascript 编写的前端。还会有相应的身份数据库、身份验证数据库、分析数据库(可能是用于会话的 redis 缓存)等……我正在使用 docker swarm 部署所有这些应用程序。

每当部署这些应用程序之一时,它必须发现所有其他应用程序。由于我使用 docker swarm,只要所有节点共享必要的覆盖网络,发现就不是问题。

但是,每个应用程序仍然需要上游服务 host_addr、可能是端口、某些 DB 或密封服务的凭据等......

我知道 dockersecrets可以让应用程序从容器中读取配置,但是我需要为每个服务用每种语言编写一些配置解析器。这看起来很乱。

我宁愿拥有一个configuration service,它保存有关如何配置所有其他服务的知识。因此,每个应用程序都会以一些 RPC 调用开始,该调用旨在在运行时获取应用程序的配置。就像是

int main() {
    AppConfig cfg = configClient.getConfiguration("APP_NAME");
    // do application things... and pass around cfg
    return 0;
}

AppConfig 将在 IDL 中定义,因此该类将立即可用并且与语言无关。

这似乎是一个很好的解决方案,但也许我真的错过了这里的重点。即使在规模上,数以万计的节点也可以通过一些配置服务轻松地提供服务,因此我认为不会出现任何扩展问题。同样,这只是一个爱好项目,但我喜欢考虑“假设”场景:)

微服务架构中如何处理配置方案?这看起来是一种合理的方法吗?Facebook、Google、LinkedIn、AWS 等主要参与者是做什么的?

4

2 回答 2

2

我将使用以下现有解决方案之一,而不是构建自定义配置管理解决方案:

春天云配置

Spring Cloud Config是一个用 Java 编写的配置服务器,提供 HTTP API 来检索应用程序的配置参数。显然,它附带了一个 Java 客户端和一个不错的 Spring 集成,但由于服务器只是一个 HTTP API,您可以将它与您喜欢的任何语言一起使用。配置服务器还具有配置值的对称/非对称加密功能。

配置源:外部化配置存储在 GIT 存储库中,Spring Cloud Config 服务器必须可以访问该存储库。然后可以通过 HTTP API 访问该存储库中的属性,因此您甚至可以考虑实现配置属性的更新过程。

服务器位置:理想情况下,您可以通过域(例如config.myapp.io)访问配置服务器,这样您就可以根据需要实现负载平衡和故障转移方案。此外,您需要为所有服务提供的只是那个确切的位置(以及一些身份验证/解密信息)。

入门:您可以查看Spring 文档集中配置的入门指南,或阅读Spring Cloud Config 快速介绍

Netflix 阿凯厄斯

Netflix Archaius是 Netflix OSS 堆栈的一部分,“是一个 Java 库,提供 API 来访问和利用可在运行时动态更改的属性”。虽然仅限于 Java(它与您所要求的上下文不完全匹配),但该库能够使用数据库作为配置属性的源。

信心

confd使用存储在外部源(etcd、consul、dynamodb、redis、vault 等)中的数据使本地配置文件保持最新。配置更改后,confd 重新启动应用程序,以便它可以获取更新的配置文件。

在您的问题的上下文中,这可能值得尝试,因为 confd 不对应用程序进行任何假设并且不需要特殊的客户端代码。大多数语言和框架都支持基于文件的配置,因此 confd 应该很容易添加到当前使用 env 变量并且没有预期分散配置管理的现有微服务之上。

于 2017-09-29T08:26:33.293 回答
0

我没有给你一个好的解决方案,但我可以指出一些问题供你考虑。

首先,您的应用程序可能需要一些引导配置,使它们能够定位并连接到配置服务。例如,您提到使用 IDL 为支持远程过程调用的中间件系统定义配置服务 API。我假设您的意思是 CORBA IDL。这意味着您的引导配置不仅是要连接的端点(可能指定为字符串化 IOR 或路径/in/naming/service),而且还是您正在使用的 CORBA 产品的配置文件。您不能从配置服务下载该 CORBA 产品的配置文件,因为那将是先有鸡还是先有蛋的情况。因此,您最终不得不为每个应用程序实例手动维护 CORBA 产品配置文件的单独副本。

其次,您的伪代码示例表明您将使用单个 RPC 调用来一次检索应用程序的所有配置。这种粗粒度级别是好的。相反,如果应用程序使用单独的 RPC 调用来检索每个名称=值对,那么您可能会遇到重大的可伸缩性问题。为了说明,假设一个应用程序在其配置中有 100 个名称=值对,因此它需要进行 100 次 RPC 调用来检索其配置数据。我可以预见以下可扩展性问题:

  • 例如,如果应用程序和配置服务器在同一个局域网上,则每个 RPC 可能需要 1 毫秒的往返时间,因此您的应用程序的启动时间是 100 次 RPC 调用中的每一次的 1 毫秒 = 100 毫秒 = 0.1 秒. 这似乎可以接受。但是,如果您现在在另一个大陆上部署另一个应用程序实例,例如往返延迟为 50 毫秒,那么该新应用程序实例的启动时间将是 100 次 RPC 调用,每次调用延迟 50 毫秒 = 5 秒。哎哟!

  • 需要进行100 次 RPC 调用来检索配置数据假设应用程序将检索每个名称=值对一次并将该信息缓存在例如对象的实例变量中,然后稍后通过访问名称=值对那个本地缓存。然而,迟早会有人x = cfg.lookup("variable-name")for-loop 内部调用,这意味着应用程序每次都会在循环周围进行 RPC。显然,这会减慢应用程序实例的速度,但如果最终有数十或数百个应用程序实例这样做,那么您的配置服务将被每秒数百或数千个请求淹没,它将成为集中式性能瓶颈。

  • 您可能会开始编写长期运行的应用程序,在启动时执行 100 次 RPC 以检索配置数据,然后在终止前运行数小时或数天。让我们假设这些应用程序是 CORBA 服务器,其他应用程序可以通过 RPC 与之通信。迟早您可能会决定编写一些命令行实用程序来执行以下操作:“ping”应用程序实例以查看它是否正在运行;“查询”应用程序实例以获取一些状态详细信息;要求应用程序实例正常终止;等等。这些命令行实用程序中的每一个都是短暂的。当它们启动时,它们使用 RPC 来获取它们的配置数据,然后通过使单个 RPC 到服务器进程来 ping/查询/杀死它来完成“真正的”工作,然后它们终止。现在有人会编写一个 UNIX shell 脚本,它每秒为数十或数百个应用程序实例中的每一个调用这些 ping 和查询命令。这个看似无害的 shell 脚本将负责每秒创建数十或数百个短期进程,而这些短期进程将向集中配置服务器发起大量 RPC 调用以检索名称=值对,一次一对。那种 shell 脚本会给你的集中式配置服务器带来巨大的负载。

并不是要阻止您设计集中式配置服务器。以上几点只是对您需要考虑的可扩展性问题的警告。您的应用程序计划通过一个粗粒度 RPC 调用来检索其所有配置数据肯定会帮助您避免我上面提到的各种可伸缩性问题。

为了提供一些思考,您可能需要考虑一种不同的方法。您可以将每个应用程序的配置文件存储在 Web 服务器上。应用程序的 shell 启动脚本“包装器”可以执行以下操作:

  • 使用wgetcurl从 Web 服务器下载“模板”配置文件并将文件存储在本地文件系统中。“模板”配置文件是一个普通的配置文件,但有一些值的占位符。占位符可能看起来像${host_name}.

  • 也可以使用wgetcurl下载包含搜索和替换对的文件,例如${host_name}=host42.pizza.com.

  • 对所有下载的模板配置文件执行这些搜索和替换术语的全局搜索和替换,以生成可以使用的配置文件。您可以使用 UNIX shell 工具sed或脚本语言来执行此全局搜索和替换。或者,您可以使用像Apache Velocity这样的模板引擎。

  • 执行实际应用程序,使用命令行参数指定路径/​​to/downloaded/config/files。

于 2017-09-23T10:48:21.553 回答