生成式AI领域的最新成果都在这里!抢 QCon 展区门票 了解详情
写点什么

搭建云原生配置中心的技术选型和落地实践

  • 2021-06-01
  • 本文字数:7830 字

    阅读完需:约 26 分钟

搭建云原生配置中心的技术选型和落地实践

引言

在从单体应用向微服务架构转型的过程中,服务配置管理从只需要应对一个单体服务,变为应对大量分布式服务,难度呈几何级增加。为了解决这个难题,各种应对分布式服务的配置中心应运而生,如何搭建一个高效合理的配置中心已经成为每个大型分布式系统必经的考验。而在服务“上云”的大趋势下,如何让配置中心在云平台顺利落地,更进一步,如何借助云计算的优势让配置中心如虎添翼,目前业内对这一块还处于探索阶段。本文将介绍 FreeWheel 核心业务系统在 AWS 云平台上搭建配置中心的实战,作为搭建云原生配置中心的参考,希望能给大家带来启发。


更多相关内容参见系列文章:云原生环境下的微服务实践全解

为什么需要配置中心?

分布式系统兴起之后,配置管理变得尤为复杂。1984 年,业界就有两位学者 Kramer Jeff 和 Magee Jeff 在 IEEE 发表了一篇文章《分布式系统的动态配置》,其中阐述到:


动态系统配置是在系统运行时对其进行修改和扩展的能力。在大型分布式系统中,这是一个必不可缺的功能,因为如果需要停止整个系统来对其部分硬件或软件进行修改,在生产环境是难以接受的,或者会产生较大经济损失。另外,动态配置也有助于系统在生产环境上组件的增量集成,以达成系统演进的目的。


这篇文章指出了动态配置对于分布式系统的重要性,即在系统运行时,如何经济安全地对系统进行调整。现在分布式系统已经发展到了从前难以想象的复杂程度,除了动态配置,配置管理还面临更多挑战,例如:

  • 如何统一管理数量众多的服务配置

  • 如何在异地众多机器节点上部署配置

  • 如何实现灰度

  • 如何确定配置是否生效

  • 如何对配置进行灾备


为了解决这些挑战,配置中心应运而生。配置中心就是分布式服务中统一管理服务配置的系统,它具备高效和动态控制服务的能力。配置中心一般具备服务注册、服务配置管理、部署服务配置等基本功能。下面是一个微服务架构下配置中心的示意图:

 


配置中心一般会包含服务端、客户端和界面这三个组件:每个微服务启动时可以通过客户端进行服务注册;用户可以通过界面创建、修改和部署配置;动态配置功能可以通过服务端实时推送、客户端定期拉取或者两者推拉结合来实现。

FreeWheel 云原生配置中心实战

痛点

  • 服务配置的数量大幅增加

Freewheel 核心业务系统中已拆分出数十个独立的微服务,每个微服务都需要部署多个环境(Staging、Production、开发、测试环境),多个集群,多个区域(Region)。开发人员需要维护多套服务配置。


  • 服务配置的部署方式不同

Freewheel 核心业务系统在云平台和数据中心的部署方式,以及在不同环境的部署方式各不相同。开发人员需要投入时间成本去学习和管理各种部署方式。


  • 缺乏动态配置功能

Freewheel 核心业务系统在运行时修改服务配置的流程较为繁琐,包括提交、评审、合并分支、运行测试、打包、部署 Staging 和 Production 等。这个效率不能满足团队需求,例如 Freewheel 作为面向企业级客户提供广告投放服务的系统,在广告投放的高峰期处理的数据量远高于平常,工程师团队需要动态配置服务的超时参数;又如在生产环境对问题进行定位和调试时,需要尽快调高日志级别,以便快速解决问题。


  • 保证 Freewheel 核心业务系统面向企业级用户的高可用性

软件系统总是会宕机的,当配置中心系统被许多微服务依赖做配置管理时,一定得考虑到它宕机时其他服务该怎么办。所以配置中心需要实现为弱依赖而非强依赖,即配置中心出现系统故障时,其他服务也能正常启动和运行。


  • 保证配置中心的安全性

配置中心的管理对象是比较敏感的服务配置项,对安全性有较高要求,需要合理配置用户和集群的访问权限。

配置中心选型

为了解决上述痛点,我们开始为 Freewheel 核心业务系统设计并搭建配置中心。在选型阶段,我们参考了当时较为成熟的几个配置中心产品,如 Apollo、Nacos、Consul 等。配置中心的第一个版本中,我们选择了 Apollo 作为服务端和界面,因为 Apollo 在用户界面友好度、核心功能支持度、社区文档完善度方面都较为突出。Apollo 产品架构主要包含 Config Service(提供配置推送和拉取接口)、Admin Service(提供配置管理接口)、 Portal(用户界面)和客户端,如下图所示:



Freewheel 核心业务系统当时正往 AWS 云服务上迁徙,我们为配置中心开发了客户端,并在 AWS 开发环境部署了 Apollo 的相关服务。但随后我们产生了一些顾虑。


首先是学习维护成本:Freewheel 核心业务系统的微服务架构使用 GO 技术栈,与 Apollo 使用的 Java 不一致,工程师团队需要投入额外的学习成本;使用 Apollo 还需要在 AWS 上维护四套非云原生的服务:Config Service、Admin Service、Portal 和 DB,由于缺乏产品级的 Apollo 技术支持,会产生较大的维护成本。其次是产品国际化的问题:Freewheel 对于开源产品的使用有严格的审计流程,需要提交代码库、英文版的架构设计和使用说明文档。Apollo 作为一款国内自研产品,没有发布详细的英文文档。


因此我们开始考察其他产品如 AWS AppConfig。调研发现,AppConfig 的功能没有 Apollo 那么全面:


功能

AWS AppConfig

Apollo

配置创建部署

支持

支持

配置参数正确度校验

支持

支持

多环境支持

支持

支持

用户权限控制

需要定制

支持

版本管理

支持

支持

灰度发布

支持

支持

用户界面

需要定制

支持

服务端推送功能

不支持

支持

客户端拉取功能

需要定制

支持

配置监控

支持

支持


配置中心一个重要的服务端推送功能不被 AppConfig 支持,这会影响配置中心的 SLA,即配置生效的时延。然而作为一家 B2B 公司,Freewheel 对配置中心 SLA 的要求不太高。而配置中心其他几个重要功能:客户端拉取、用户权限控制、用户界面,我们能基于现有的微服务架构用较小的开发成本实现。

但使用 AppConfig 的好处是:作为 AWS 云原生服务,AppConfig 跟我们的 AWS 服务集群有很好的契合度,能方便获得 AWS 技术团队的支持,降低学习维护成本和使用成本。我们估算了配置中心的费用,主要包括 AppConfig API 调用和 S3 费用。假设一个微服务部署两个区域,启动 3 个 POD,配置文件大小为 10K,每天更新两次配置,每分钟轮询一次 AppConfig,那么这个微服务使用配置中心的费用大约是 0.6 美元/月。


综合考虑 Freewheel 的业务需求和使用成本后,我们采用了基于 AWS AppConfig 的配置中心架构。

配置中心架构



配置中心的核心模块包括 AWS AppConfig 服务端、微服务客户端、用户界面。主要使用场景包括:

  • 各个微服务通过用户界面管理配置:包括创建配置应用程序,向 AWS S3 读写配置文件, 通过 AppConfig 部署最新的配置,在数据库中记录用户的操作历史。

  • 各个微服务通过客户端对 AppConfig 服务端进行定期轮询,一旦发现配置更新,就从 AppConfig 服务端拉取配置并使之在微服务中生效。

配置中心落地实现

AWS AppConfig 服务端

AWS AppConfig 是 AWS 开发用来创建、管理和快速部署应用配置的服务。 客户端和用户界面的实现与 AppConfig 提供服务的实体密切相关。AppConfig 通过以下实体来管理应用配置:

  • 应用程序(Application):应用程序就是需要 AppConfig 提供配置管理的应用,如在 EC2 实例上运行的微服务,AWS Lambda 的无服务器应用程序等等。

  • 环境(Environment):对于每个应用程序,可以定义一个或多个环境,例如 Staging 或 Production。

  • 配置(Configuration)和配置文件(Configuration Profile):每个环境下只有一个配置,配置文件就是记录配置内容的文件对象。每次更新配置,实际更新的是配置文件的内容(版本)。

  • 配置策略(Deployment Strategy):配置策略定义了配置的部署方式,如部署节点是线性扩张还是指数扩张、部署时长、监控和回滚策略等。


下图是 Freewheel 核心业务系统集群的实际应用场景,由于 Development、Staging 、Production 三个环境的集群互相隔离,我们为不同环境的集群创建了独立的 AppConfig 服务端和用户界面。微服务在用户界面创建与之关联的应用程序,这个应用程序仅包含一个环境。我们选择了 S3 来存储配置文件,可以通过用户界面读写配置文件。目前配置中心在部署时使用的配置策略是每 30 秒部署 50%的节点。


配置中心客户端

客户端是微服务进行配置轮询和配置更新的重要组件。我们在配置中心客户端做了灾备处理,从而实现了微服务集群对配置中心的弱依赖。即便配置中心的服务端或者用户界面出现故障,微服务集群的运行也并不受影响,只是不能使用配置管理的功能。配置中心客户端的工作流程如下:



微服务启动后,我们会将备份配置文件加载到内存中,然后启动一个 Go Routine 关联配置中心,按照一定时间间隔来轮询配置。如果发现配置更新,就把更新内容合并到内存配置和其他定制的配置中,否则等待下一次轮询。客户端使用 Go 语言开发,下面是关联和查询配置中心的示例代码:


func InitConfigCenter(serviceName string, callbacks ...CustomCallback) {
    go func() {
       for {
           func() {
               defer func() {
               // 处理Panic
               }()
               appConfigClient := getAppConfigClient()
               // 查询配置更新,如有更新则使之生效
               GetAndMergeAppConfig(appConfigClient, serviceName, callbacks...)
               }()
               time.Sleep(time.Duration(pollingDuration) * time.Second)
           }
    }()
}
复制代码


  • 参数 serviceName:在服务端具有唯一性的标志符,一般等同微服务名。管理人员在用户界面需要输入同样的 serviceName 去创建 AppConfig 应用程序,然后客户端和服务端通过该 serviceName 匹配应用程序。

  • 参数 CustomCallback:微服务可定制的修改配置的接口。获取配置更新后,客户端会默认修改内存配置使配置生效。但有些配置不是从内存配置中读取的,例如存储在全局变量里的配置,此时可以通过这个接口定制更新配置的方法。


考虑到弱依赖的设计原则,客户端内存配置的更新采用了合并策略(Merge)而非替代策略。初始内存配置从备份读取,随后从服务端不停拉取最新配置进行合并。服务端配置可以对内存配置进行全量覆盖、部分覆盖、或者新增配置。



客户端的内存配置管理是基于 Viper("github.com/spf13/viper")实现的,合并配置时使用了 Viper.MergeConfig 方法:

func mergeAppConfig(newConfig *appconfig.GetConfigurationOutput, callbacks ...CustomCallback) {
        memoryConfig := config.Get()
        if err := memoryConfig.MergeConfig(bytes.NewReader(newConfig.Content)); err != nil {
        // 处理配置合并错误
        }
        // 更新内存配置
        config.Set(memoryConfig)
        // 更新定制配置
        for i, callback := range callbacks {
               callback.CustomCallbackFunc(newConfig)
        }
}
复制代码


客户端的一个重要逻辑是配置版本的保存和比对。客户端在本地存储了之前轮询获得的服务端最新配置版本,每次调用 AppConfig API 查询时都会输入这个配置版本。AppConfig API 会比较请求里的配置版本和服务端最新的配置版本,两者不一致时会返回最新的配置版本和配置内容,否则返回原来的配置版本。版本不一致时,调用 API 的费用会比平时高很多。客户端收到服务端答复后,再次比较本地和答复里的配置版本,如果不一致就会保存新的版本,并且进行配置合并。下面是调用 AppConfig GetConfiguration API 的代码:


import (
        "github.com/aws/aws-sdk-go/aws"
        "github.com/aws/aws-sdk-go/aws/session"
        "github.com/aws/aws-sdk-go/service/appconfig"
)
var latestConfigVersion = "-1" // 第一次查询时的初始版本
 
func getAppConfig(region, env, clientId, configuration, applicationName string) (*appconfig.GetConfigurationOutput, error) {
   awsConfig := &aws.Config{ Region: aws.String(region) }
   mySession := session.Must(session.NewSession(awsConfig))
   appConfigClient := appconfig.New(mySession, aws.NewConfig())
   return appConfigClient.GetConfiguration(&appconfig.GetConfigurationInput{
               Application: &applicationName,
               Environment: &env,
               Configuration: &configuration,
               ClientConfigurationVersion: &latestConfigVersion, // 使用本地记录的最新服务端配置版本
               ClientId: &clientId,
        })
}
复制代码


GetConfiguration 输入包括 AppConfig 的应用程序名、环境名、配置名、配置文件版本和客户端指定的唯一 ClientId,输出 GetConfigurationOutput 包括配置文件版本和配置内容(可选项)。

配置中心用户界面

配置中心用户界面是用来统一管理各个微服务配置的窗口。我们在 Freewheel 内部业务数据查询平台 Falcon 上搭建了配置中心的用户界面,仅允许 LDAP 账户开通配置中心的访问或管理权限。


AppConfig 服务在 AWS 控制平台也有自己的管理界面,但是不能满足我们的需求。首先 AWS 控制平台的权限管理比较严格,工程师团队在生产环境只有部分读权限,没有写权限,如果我们需要在 AWS 控制平台上管理和部署配置,每次都需要单独申请权限。另外 AppConfig 原生的管理界面比较简单,不能看到具体的配置项内容,需要去相应的 S3 页面下载配置文件,也不具备配置对比和查看用户历史操作的功能。


配置中心用户界面架构:



配置中心用户界面包含了前端和后端模块,前端模块由 React 实现,包括以下页面:

  • 主页:展示所有微服务应用程序列表。

  • 应用页面:展示单个微服务应用程序的详细信息,由主页进入。

  • 创建页面:为一个新的微服务创建应用程序,由主页进入。

  • 配置上传页面:上传新的配置文件,由应用页面进入。

  • 配置部署页面:选择一个配置文件版本进行配置部署,由应用页面进入。

  • 历史记录页面:展示应用程序所有部署历史和用户,由应用页面进入。


后端模块由 Node.js 实现,分为配置管理和用户管理两个子模块。在配置管理模块调用 JS SDK 的 AppConfig Client 和 S3 Client 实现上述前端页面功能;在用户管理模块实现了权限管理和历史记录功能,用户的创建、上传、部署行为会被记录到数据库中。创建一个可用的 AppConfig 应用程序实际上包含了四个步骤:创建应用程序,创建环境,上传初始配置文件,在应用程序中绑定配置文件。在应用程序中关联配置文件后,会记录配置文件的地址和版本。每次为这个应用上传并部署新的配置文件后,关联配置文件的版本就会变动。在历史记录页面可以看到历次部署的状态、开始时间、配置版本、部署时长和操作用户,还可以对配置内容进行灵活对比。下面给大家展示一下配置中心的用户界面。


Falcon 平台主页:



配置中心主页:



配置中心历史记录页面:

 


落地常见问题

在搭建配置中心的实战过程中,我们踩了不少的坑,也总结了一些经验。在这里分享给大家,希望能对大家有所帮助。


  • 如何合理使用 AppConfig 服务使其收费最低?

GetConfiguration API 是 AWS AppConfig 服务中最重要的 API,通过轮询这个 API 可以获得配置版本变化信息和最新的配置项内容。该 API 请求所带的参数 ClientConfigurationVersion 与服务端最新的配置版本一致时收费较低,否则收费较高。为避免额外收费,客户端一定要在本地存储之前查询的服务端最新的配置版本,在调用 API 时使用。


客户端还需要注意一个逻辑,就是客户端真实生效的配置版本不一定等同于服务端最新的配置版本,因为客户端从发现配置版本变化到启动配置更新这一过程是可能出错的。即使客户端在配置更新过程出错,也要保存出错版本供下次调用使用。


  • 如何获取有效的配置文件版本?

AppConfig 的配置文件版本等同于 S3 文件版本。但 S3 上传配置文件和 AppConfig 部署配置不是一个事务操作,所以最新的 S3 文件版本不等同于 AppConfig 的有效配置文件版本。所以要获取 AppConfig 最新生效的配置文件版本,不能调用 S3 API,而是调用 AppConfig ListDeploymentsCommand API,读取返回列表中最新的配置版本。


  • 如何在本地开发环境调试 AppConfig?

在本地开发环境调试 AppConfig 时不能使用生产环境的 IAM 角色,可以使用一个 AWS 账号的临时凭证来发送 AppConfig API 请求:


import (
        "github.com/aws/aws-sdk-go/aws"
        "github.com/aws/aws-sdk-go/aws/credentials"
        "github.com/aws/aws-sdk-go/aws/session"
)
 
func getAWSSession(region, env, accessKey, secretKey, awsSessionToken string) *session.Session {
        awsConfig := &aws.Config{ Region: region }
        if env == DEV_ENV {
               creds := credentials.NewStaticCredentials(accessKey, secretKey, awsSessionToken)
               awsConfig.Credentials = creds
        }
        return session.Must(session.NewSession(awsConfig))
}
复制代码


其中 accessKey、secretKey、awsSessionToken 来源于 AWS CLI 为这个 AWS 账号提供的临时凭证信息,可在 AWS 控制平台上用账号登陆后实时查询。不添加这个临时凭证信息就会自动使用 EC2 默认或者配置的 IAM 角色凭证。


  • 如何合理配置 AppConfig 服务的读写权限?

以 Freewheel 配置中心的客户端和用户界面为例,客户端需要发送大量 AppConfig 读请求,用户界面需要发送少量 AppConfig 读写请求。所以我们为客户端 EC2 的默认 IAM 配置了 AppConfig 读权限,为用户界面 EC2 申请了特殊 IAM 角色并为它配置了 AppConfig 读写权限。要使特殊 IAM 角色生效,需要修改 K8S 部署文件:

// deployment.yaml
spec:
  template:
    sepc:
      serviceAccountName: {{ $.Your.Arn.Account.Name }}
 
// serviceaccount.yaml
annotations:
  eks.amazonaws.com/role-arn: {{ $.Your.Arn.Role.Name }}
复制代码


特殊 IAM 角色配置成功后,可以在 POD 里查询环境变量确认:AWS_ROLE_ARN,AWS_WEB_IDENTITY_TOKEN_FILE。使用特殊 IAM 角色,需要通过 AWS STS 获取临时凭证后再发送 AWS 服务请求。注意如使用 JS SDK V3 发送请求,则需使用 v3.10 或以上版本(否则不支持获取凭证的功能),如下所示:

// AWS JS SDK V3获取凭证
const { AppConfigClient } = require("@aws-sdk/client-appconfig");
const { getDefaultRoleAssumerWithWebIdentity } = require("@aws-sdk/client-sts");
const { fromTokenFile } = require("@aws-sdk/credential-provider-web-identity");
const appconfig = new AppConfigClient({ credentials: fromTokenFile({
        roleAssumerWithWebIdentity: getDefaultRoleAssumerWithWebIdentity() })
});
复制代码


  • 使用特殊 IAM 角色遇到 ExpiredTokenException 怎么解决?

EC2 默认 IAM 的权限长期有效,特殊 IAM 角色的凭证是有期限的。如果在服务运行时遇到了 ExpiredTokenException,需要审视一下 AWS API Client 的生命周期。如配置中心用户界面,为每次请求重新生成一个 AppConfigClient 来避免凭证过期。

配置中心未来展望

配置驱动资源正在成为云计算的一个重要技术趋势,即认为不光是应用进程,与云计算相关的所有资源都可以通过配置去驱动。这将令配置中心的云端之路充满变化和挑战。未来我们也会继续思考配置中心的其他应用模式,比如如何在云服务平台上与其他服务整合,如何去独立支撑某些业务场景等等。


作者简介:


孙自然 Lead Software Engineer,FreeWheel


毕业于北京大学计算机系,目前就职于 Comcast FreeWheel 核心业务团队。研究方向为微服务架构、云计算、产品设计等领域,热衷于新技术的探索与分享。


系列文章推荐阅读:云原生环境下的微服务实践全解

公众号推荐:

2024 年 1 月,InfoQ 研究中心重磅发布《大语言模型综合能力测评报告 2024》,揭示了 10 个大模型在语义理解、文学创作、知识问答等领域的卓越表现。ChatGPT-4、文心一言等领先模型在编程、逻辑推理等方面展现出惊人的进步,预示着大模型将在 2024 年迎来更广泛的应用和创新。关注公众号「AI 前线」,回复「大模型报告」免费获取电子版研究报告。

AI 前线公众号
2021-06-01 17:104718

评论

发布
暂无评论
发现更多内容

借助ETL快速查询金蝶云星空表单信息

RestCloud

ETL 金蝶云

Sparkle 5 for Mac(网页开发工具)

展初云

网页设计 Mac软件 Sparkle

VMware Fusion Pro 13 for Mac(VM虚拟机)v13.5.0激活版

iMac小白

一键部署,高效私有化大模型

百度开发者中心

大模型 #人工智能 LLM

VMware InstallBuilder Enterprise for Mac(跨平台安装程序的开发工具)v23.11激活版

iMac小白

Sketch 57 for mac(矢量绘图软件) v57.1中文激活版

mac

Sketch 苹果mac Windows软件 矢量绘图软件

聊一聊大模型 | 京东云技术团队

京东科技开发者

人工智能 大模型 ChatGPT

报名!星河社区五周年「极客工坊」8城巡回启动,共话大模型!

飞桨PaddlePaddle

人工智能 开发者 星河社区

Pixea 5 for Mac(看图软件)

展初云

Mac 看图软件 Pixea Plus for Mac

After Effects 2023 for Mac(AE视频特效制作软件) v23.6完美激活版

mac

AE2023 苹果mac Windows软件 视频特效软件 After Effects 2023

集成测试的实践与思考

老张

软件测试 集成测试 质量保障

Parallels Desktop 19 for Mac(PD19虚拟机)一键激活版

iMac小白

MySQL Shell如何接管手动搭建(含仲裁节点)MGR集群

GreatSQL

greatsql

情感语音识别:技术发展与未来趋势

来自四九城儿

Mp3tag for Mac(音频标签编辑器)

展初云

Mac软件 Mp3tag 音乐标签

Linux RN6752 驱动编写

EquatorCoco

Linux 运维

LLM大模型AI能力助力企业数字化智能化转型

百度开发者中心

#人工智能 LLM

ThreadPoolExecutor线程池内部处理浅析 | 京东物流技术团队

京东科技开发者

Java 线程池 ThreadPoolExecutor

AI 的“道德感”如何训练而来(2)

石君

AIGC 人工智能道德

INFINI Easysearch 与华为鲲鹏完成产品兼容互认证

极限实验室

华为鲲鹏 easysearch 极限科技 华为鲲鹏认证

GraphicConverter 12 for mac v12.0.8中文激活版

iMac小白

SpringMvc集成开源流量监控、限流、熔断降级、负载保护组件Sentinel | 京东云技术团队

京东科技开发者

Java sentinel springmvc

Kubernetes集群原地无损大版本升级的一些探索

chen

Kubernetes 升级 Kubernetes 集群 kubernetes 运维

Flutter App混淆加固、保护与优化原理

如何使用Selenuim浏览器自动化框架实现自动登录社交媒体账号和自动发布文章

爱写字的阿城

自动化 selenium

低成本大模型解决方案

百度开发者中心

gpu #人工智能 LLM

Discuss the difference between MT7915 and QCA9880-QCA9882

wifi6-yiyi

MT7915

情感语音识别的挑战与未来趋势

来自四九城儿

Retrobatch 2 for mac(图像批量设计工具)

展初云

搭建云原生配置中心的技术选型和落地实践_架构_孙自然_InfoQ精选文章