当应用程序规模比较小的时候,应用的配置可能只有短短的几行,配置的分发也只需要通过复制下本地文件下发即可。但是随之应用规模变大,配置变得越来越复杂,如何管理不同应用的不同配置版本变得越发困难,本文介绍了facebook是如何管理整个配置系统的。
摘要
facebook的网页端和app端每天都有成千上万次的配置变更,对于活跃用户也有上亿次的配置检查来决定下发产品新特性。通过配置变更,可以动态开启产品的新特性,对于不同的应用设备进行A/B测试,以及通过修改权重对不同数据中心进行负载均衡,同时还能下发不同的ML模型来提高feed流的的排名。
简介
软件的开发部署周期变得越来越短,对于互联网应用,频繁的软件更新不仅仅需要的是代码模型的更新,对于配置也有大量的变更需求。比如对于facebook主页,每天会进行两次的代码发版,对于配置则更频繁,目前主页每天有成千次的配置变更。
一大群开发人员频繁地进行配置变更很难避免配置错误的发生从而导致网页故障。然而避免配置错误只是配置管理众多挑战中其中的一个。配置管理还包括以下众多的挑战
配置管理收敛
facebook内部有大量的系统对配置管理有不同的需求,此前,每个系统都有自己的一套配置存储和分发机制,导致很难对配置进行统一的管理。为了避免配置扩散,facebook在统一的基础平台上构建了一套工具来支持不同的额用力场景。这些工具从统一的配置中心管理了成千上万的配置,并将他们分发到数十万的服务器和数亿的额移动设备。
配置更改及版本管理
大型系统中通常有许多灵活的配置开发可以动态修改,在facebook,配置文件的中位数大小为1kb,有的比较大的配置达到了Mb甚至Gb级别,人肉编辑修改这么大的配置犯错是难以避免的,即便一个小小的配置 错误也可能导致页面故障。因此facebook才用了配置及代码的管理方式来编译生成配置,并通过版本控制工具对配置生成程序和参数进行版本控制管理。
预防配置错误
- 配置生成器会自动进行参数合法性校验。
- 配置变更视为与代码变更一样需要进行code view。
- 影响到前端页面的配置变更需要经过自动集成测试。
- 配置变更会通过金丝雀测试发布到线上进行验证。
配置依赖
一个大型的系统往往由许多团队互相协作进行完成,因此不同的模块间必然存在依赖关系,每个系统 有自己的配置模块,然而往往也对其他系统的配置存在依赖。例如监控系统升级了新的配置来支持新的feature,所有系统的监控模块也要跟着更新配置来使新feature生效。facebook采用了类似c++里include的方式来声明配置的依赖关系。后面会详细介绍依赖的具体实现。
可靠可拓展的配置分发
从后端应用到移动设备,配置的大小从字节到Gb都有,对于地理位置分布 广泛的应用,配置下发 失败是在所难免的,如何保证配置的及时可靠,以及如何避免因为配置系统成为瓶颈从来带来可用性问题是一个巨大的挑战,本文后续将介绍如何应对这些挑战。主要包括
- 运行时配置管理
- 配置管理系统站,包括P2P分发
- 对大规模配置系统的使用经验以及分析
使用案例和解决方法
- 特性开关
facebook使用快速迭代发布的方法来快速获取用户反馈进行迭代。当一个产品还在 开发阶段的使用,他的代码就会被发布到线上,只不过是禁用了特性开关。facebook使用了 一个叫Gatekeeper的组件来慢慢开启新的feature。如果出现了问题Gatekeeper可以迅速关闭新特性,同时它还能决定该特性对哪些用户生效,例如内部员工或者1%的用户设备。
- 产品实验
通过A/B测试来进行决策驱动,例如,由于硬件差异,Facebook的Messenger上的VoIP的 回声消除参数需要针对不同的设备进行调整,通过配置变更可以进行不同参数的测试实验。
- 应用层流量控制
配置管理可以方便地管理不同的站点流量流向。自动化工具会自动修改流量进行区域间的流量迁移,以便在 生产环境进行负载测试,影子测试时,则可以通过镜像流量复制到测试服务进行测试。
- 负载均衡
facebook将用户数据存储在TAO系统里,当 新集群上线或者旧集群故障时,系统会自动根据集群新的拓扑结构进行流量的均衡和数据的迁移。
- 监控报警
通过动态修改配置,可以动态修改:
- 监控数据采集
- 修改监控面板
- 报警规则检测
- 报警规则订阅
- 根据报警信息自动进行修复,例如服务重启
- 更新ML模型
根据最新的数据生产最新的ML模型,通过下发更新模型来更新搜索排名和feeds流排名,ML模型的大小从kb到Gb都有
- 控制应用内部行为
这可能是最普遍的 一个使用场景了,生生产系统通常拥有许多的开关来管理内部的行为。例如存储服务会配置使用多少内部,定义如何批量刷盘,如何进行预读提升性能等。
工具链概览
- Configerator 所有组件的基石,包括版本管理 编辑 配置review和 自动金丝雀测试以及配置分发。其他的所有 工具都是构建于此之上以提供特殊的方法需求。
- Gatekeeper 产品特性功能开关,也可以通过A/B测试来获取最佳参数。
- Package Vessel P2P的 配置传输工具,主要用来传输GB量级的配置模型
- Sitevars 为 前端PHP提供的简单易用的适配API
- MobileConfig 管理安卓 IOS的配置,对后端的存储系统进行了解耦,
Configerator, Sitevars, and PackageVessel
配置修改
首先先假设: 1. 大多数开发人员更加倾向于通过写代码生成配置 2. 配置生成脚本比配置本身更容易维护。这两个假设在后面会有数据支撑验证。
带着这些假设,configerator逐步推行了配置及代码的策略。
如图二,通过依赖引用,配置编译器进行校验生成了最终的配置信息。通过将配置模块化以及导入复用,大大降低了配置的维护成本。配置编译器自动生成配置更新并推送到git仓库。
通过UI和sitavars提高易用性
configerator被设计成了所有案例场景的基石,因此必须具有足够的灵活性和拓展性。但是过于灵活也带来了一些问题。好比杀鸡焉用牛刀,对于一些复杂的配置信息,模块化依赖导入会提升可维护性,但是对于一些简单的配置,过于灵活就显得有些臃肿了。此外通过UI 生成无需写任何代码就生成配置骨架,然后在通过configerator进一步生成配置文件。sitavars主要为 前端的的php提供了简易的kv配置存储信息。
预防配置错误
配置错误会导致服务故障,facebook通过以下方法来 避免配置错误:
- config validator 保证变量的有效性
- 对配置生成程序和配置文件进行code review
- 人肉配置测试
- 自动化集成测试
- 自动化金丝雀测试
图三详细介绍了这个流程
首先,开发 人员修改完配置以后 ,会通过脚本使用新配置构建一套测试环境,当测试验证通过后会提交配置变更信息以及测试结果到Phabriacator。
如果配置的变更与前端有关联,则会触发 sandcastle工具使用 新 配置自动部署允许集成测试。sandcastle会将测试结果提交给phaboacator然后交给reviewers进行 review。
当review获得approve的时候开发人员会将变更推送到远程的 金丝雀服务。
金丝雀服务自动部署新配置新线上环境进行验证,弥补了人肉测试和集成测试覆盖不足的问题
-
生产环境的金丝雀测试分为多个阶段。例如
a. 先发布到20台服务器
b. 将配置变更发送到集群的所有节点。对于每一个阶段,会对指定的测试节点进行监控检测以及指标收集。
当新配置通过所有阶段的测试以后,金丝雀服务会将配置提交到远程的 Landing strip服务并将配置提交到主仓库。
可靠可拓展的配置分发
将配置分发给成千上万的服务,中间出现异常是 在所难免的。因此要求configerator 必须具备以下能力。
- 可用性(配置下发失败不能影响服务的运行)
- 数据一致性: 应用的不同节点必须保证配置下发最终一致,尽管没法保证 强一致。
facebook通过 git tailer持续从仓库中获取配置变更信息并推送到 zeus。 zeus是zookeeper的for可多版本并做了一些拓展和性能增强从而来满足facebook的规模。通过一致性协议保证服务的可用性。
zeus使用三层高扇出的树模型来推送配置变更。 leader → observer → proxy. 每个 leader的子树中有上百个observers。数据中心具有较大的额带宽,并且只有少量数据从数节点进行 扇出,因此高扇出模型在数据中心是可行的。数据量大的配置则通过P2P进行分发。
每个数据中心有 多个集群组成,每个集群又包含数千台服务器,并且有多台的服务被指定为observers,每个 observer保存leader的全量只读数据副本。当 用户提交写入以后,leader会将写同步给follower,然后在 异步同步给observers。如果 observer失败之后重连了,会发送本地保存的最新的transaction id然后获取未同步的写操作,zookeeper的一致性协议保证了commit消息是有序的。
每个服务会运行一个 proxy进程,并从集群中 随机选择 一个observer节点进行连接。如果observer故障 了,proxy会自动连接其他的observer节点,于observer节点不同的是,proxy不会保存所有的配置副本信息,而是只会保存应用服务所需的配置数据。
应用启动的时候,会通过链接的configerator client library去获取配置。启动阶段,会直接请求proxy去来获取配置信息。proxy从observer读取数据并将数据缓存到 本地磁盘,如果 proxy故障 了,那么应用程序会直接从本地的磁盘缓存直接读取数据。通过本地磁盘缓存,提高了配置服务的可用性。
configerator使用push的方式进行配置变更的通知。pull模型最大的优点是实现简单,因为服务端可以实现为无状态的。然而pull模型却不怎么高效: 1. pull的间隔不好定义控制,2. 每次pull需要返回全量数据对于请求量过大的场景不太适合。(此处不敢苟同,pull模型也能做增量返回的。
使用Package Vessel进行大配置分发
像ML模型这么大的配置信息,如果使用 zeus进行分发,会严重受限于带宽的吞吐导致可拓展性的问题。
Package Vessel使用 元数据于内容分离对的方式来 管理这些大配置信息。当配置 变更的时候,配置的内容会被存储到 存储系统,configerator只会保存更新 配置的metadata版本信息,以及存储配置内容的地址信息。当收到配置变更时,应用程序使用bt协议从存储服务拉取配置,使用相同配置的服务则可以直接用P2P进行分发共享。
当配置大小 超过1M的时候,推荐使用Package Vessel进行管理.
提升commit吞吐
当往git提交配置时,需要检查远端 是否有变更,如果有变更,则需要拉取变更到 本队然后进行合并提交,尽管变更和本地修改并无冲突。
Landing Strip简化了这一修改流程:
- 从不同的committers结束commit信息。
- 按照FIFO的方式进行处理
- 将提交推送到共享仓库而无需commit拉取到本地进行resolve,只有真的有数据冲突的时候才需要人为进行处理。
虽然该组件解决了提交冲突的问题,但是没法解决以下问题:
- 一个仓库每次只能接受 一次提交
- 仓库变大以后git操作会变慢
为了提升吞吐,facebook将一个统一的大仓库迁移成多个小仓库,通过命名空间进行区分,不同的路径前缀提交到不同的仓库,从而提升了整个的提交吞吐。
Gatekeeper
快速迭代发版 有助于快速获取用户反馈进行优化,同时也让问题定位变得简单, 因为每次版本的修改都较小从而较容易定位。
当应用还在开发的过程中,新feature的代码就会被提交到生产环境只不过是被设置为禁用状态,通过gatekeeper可以动态控制仪新feature的灰度比例,如果出现故障,则可以快速 关闭feature。
gatekeeper的控制逻辑通常在configerator中被 存储为 一个json文件,通过解析json配置信息执行对应的控制流。但是对于一些复杂的场景,则没法通过简单配置进行管理,例如maprerduce任务,因此gatek暴露了 laser的 kv接口用于与 外部系统进行集成。任何 系统可以实现laser接口于gatek进行进行从而控制访问逻辑。
移动端配置
由于复杂的额移动网络环境,移动端的配置管理和服务端有相当多不一样的地方。首先移动网络严格受限,其次移动端有各种各样的平台个各种不同的适配版本。
mobileConfig通过最大程度复用数据中心的工具链解决了这些挑战。
通过 translation layer进行了适配转化避免了耦合,同时通过跨平台的c++库进行适配调用转换。
由于推送通知是不可靠的,一次sdk还通过周期性的拉起来获取变更信息,为了减少带宽的开销,每次客户端会发送配置的hash值,只有 hash值变更的时候,服务端才会返回新的配置信息。
使用经验与数据分析
这一章节主要介绍了一些经验和使用数据的分析,具体的数据分析不做详细说明可以参考原文。简单 做下概括:
- 对于复杂的配置,开发人员更愿意使用代码进行配置生成
- 当配置发生变更时,大概需要10min进行金丝雀测试
- 配置错误主要由几种情况造成
- 版本兼容,旧代码无法解析新配置
- 新配置导致代码触发了异常路径bug
- 边界值错误
通过代码review可以有效降低这些配置错误,同时金丝雀测试也能有效降低配置错误的出现。
经验告诉我们,无论是代码还是配置,都需要详细的review。
结论
本文介绍了facebook的 配置管理技术栈,主要包含以下几个方面:
- 定于了问题域以及用户场景
- 描述了全局的解决方案
- 对线上的使用场景做了详细的数据分析
未来的主要工作方向是提高configerator的可拓展性,增强自动金丝雀测试,提升app的覆盖率,以及提高配置的抽象化。
这一套系统不但适用于大规模互联网系统,也适用于小型系统。以下为简要的总结,想要详细的了解最好仔细看原文。
- 敏捷配置管理才能支持敏捷开发
- 没有最好的只有最合适的,只要有合适的工具,大型公司也能 进行开放式的配置管理
- 在数据中心内部,push模型可能更高效 ?
- 推拉结合的方式可以提高可靠性
- 使用meta与数据内容分离的方式进行P2P的配置分发,同时保证了数据的 一致性。
- 单个 git仓库难以满足大规模的配置服务,可以通过命名空间的方式进行拆分管理。
Holistic Configuration Management at Facebook
TAO: Facebook’s Distributed Data Store for the Social Graph