[关闭]
@lsmn 2018-06-27T05:42:38.000000Z 字数 3580 阅读 1780

BuzzFeed如何从Perl单体应用迁移到Go和Python微服务

架构 微服务 Go Python


摘要

2016年,BuzzFeed启动了一个重构项目,把使用Perl编写的一个单体应用程序转换成一组微服务。他们之所以这样做,主要是因为Perl应用程序被证明很难扩展,使buzzfeed.com独自服务于每月大约70亿次页面访问。

正文

本文要点

  • BuzzFeed近日从一个单体Perl应用程序迁移到了大约500个使用Python和Go编写的微服务。
  • 起初,他们的路由逻辑是在CDN中实现的,但事实证明,这很难测试和维护。他们后来改成使用NGINX路由。
  • 网站路由服务被设计成在BuzzFeed自己的Rig平台上运行,这便于他们部署到他们运行的每一个Amazon弹性容器服务环境(ECS)(测试、过渡、生产)中的每个服务上。
  • BuzzFeed构建了一个抽象层,让他们可以使用YAML配置路由服务。

2016年,BuzzFeed启动了一个重构项目,把使用Perl编写的一个单体应用程序转换成一组微服务。他们之所以这样做,主要是因为Perl应用程序被证明很难扩展,使buzzfeed.com独自服务于每月大约70亿次页面访问,而且,组织还要向大约30个不同的社交平台发布内容,包括不同的语言版本。

除了扩展问题外,BuzzFeed发现,寻找懂Perl并且希望使用它开发的工程师越来越难。最后,他们希望自己能够在有了新产品想法时可以更快速地迭代。

从一个很高的层面上来说,新架构包含一个CDN,指向一个路由服务,该服务位于AWS上,使用了ECS容器化服务

high level overview

新的微服务是以Python作为主要语言开发的,对于性能比较敏感的组件则使用了Go。BuzzFeed的工程团队发现,这两种语言可以很好地互补,对于开发人员而言,根据需要从一种切换到另一种也相对简单。

在本文写作时,他们在AWS上的过渡环境和生产环境中已经有大约500个微服务。他们在分解服务时使用了听上去和SCS类似的东西;buzzfeed.com的首页是一个服务,新页面是单独的服务,作者页面也是,诸如此类。

团队面临的一项挑战是把请求路由到正确的后台应用程序。他们的CDN提供商Fastly具有在边缘通过编程定义行为逻辑的能力,使用的是一种基于C的编程语言VCL,工程团队最初就是直接使用VCL编写他们所有的路由逻辑。

不过,他们发现,随着配置越来越复杂,修改变得越来越困难,充分测试这些配置的能力愈发重要。BuzzFeed资深软件工程师Mark McDonnell告诉InfoQ:

我们在CDN中有大量的逻辑,只有少数几名工程师真正了解它是如何工作的。另外,CDN还被有意锁定,因为缓存层对我们而言特别重要,我们需要防止工程师意外修改,那可能会对我们的缓存策略产生不利影响,或者会导致进入的请求被路由到错误的源服务器。

此外,Fastly还有自己的Varnish HTTP加速器实现。BuzzFeed发现,这让工程师很难启用一个本地开发环境,并通过它运行他们的VCL逻辑,因此,VCL代码测试就成了问题。为此,工程团队决定实现一个新的路由服务来代替VCL路由,把大部分路由逻辑从CDN提取到一个单独的路由器微服务中。

网站路由服务被设计成在BuzzFeed自己的Rig平台上运行,这便于他们部署到他们运行的每一个Amazon弹性容器服务环境(ECS)(测试、过渡、生产)中的每个服务上。

对于反向代理,他们考虑了包括HAProxy在内的多个选项,但最终决定使用NGINX。McDonnell 说,“我们选择NGINX,因为我们平台基础设施团队中的大部分人都有使用经验,所以,我们得到了SRE的支持,我们其中的一个卖点是,它很容易提升速度”。如你所想,向系统添加NGINX确实会给单个请求带来额外的延迟,但按照McDonnell的说法,这可以“忽略不计”。

虽然把简单易用和熟悉度作为选择NGINX的其中两个原因,但BuzzFeed还是构建了一个配置该服务的抽象层。该服务是用Python编写的,它会从YAML动态生成所需的nginx.conf文件。它广泛使用了YAML特有的特性,如“锚和别名(Anchors and Aliases)”和“合并键(Merge Key)”,据BuzzFeed技术博客介绍,“这让我们可以跨环境块重用配置,而且很容覆盖特定的键,不需要重复大量的配置工作。我们主要把这种方法用于‘上游’和‘超时’区域,因为我们通常会希望覆盖那些特定于环境的值。”

起初,路由服务是使用NGINX的开源版本构建的,但后来转到了商业版本NGINX Plus。按照McDonnell的说法,可以获得付费支持是一个原因,但团队也需要只有付费版本中才有的某些特性,尤其是DNS监控;BuzzFeed的服务器区域相当复杂,那样,当AWS负载均衡改变IP地址时,NGINX Plus能够保证DNS条目也更新。BuzzFeed技术博客上有一篇博文是这样解释的:

在NGINX启动的时候,它会为我们在配置文件中定义的下游解析DNS。也就是说,它会把主机名转换成IP(或者一组IP,和使用AWS Elastic负载均衡器一样)。然后,NGINX会缓存这些IP,我们的问题在这里开始出现了。

你可能不熟悉负载均衡器,它们会检查多个服务器,看看它们是否健康,如果不健康,服务器会被关闭,并有一个新实例启动。我们发现的问题是,每个服务器实例的IP都在不断变化。如前所述,NGINX在启动时会缓存IP,而因为ELB[Elastic负载均衡器]的原因,新进来的请求会被代理到不再有效的缓存IP。

一种解决方案是热重载NGINX配置。那是个手动过程,我们不知道什么时候该那样做,因为我们无法知道服务器实例何时会停止服务。

NGINX文档中有几个设法变通解决这个问题的选项;不过,除了一个选项外,其他所有选项都意味着我们需要重新设计我们的整个架构,因为它们不支持NGINX的upstream指令(作为配置抽象的一部分,我们对它的依赖度很高)。

允许我们利用接口设计的那个解决方案只有NGINX Plus提供。采用商业版本的成本是合理的:我们不需要从头重写我们的整个服务,后续我们也可以利用额外的特性(其中有一部分我们已经考虑过)。一旦切换,该解决方案只需要增加一行resolver指令,为缓存的DNS解析结果定义一个TTL。

VCL逻辑用于帮助BuzzFeed在他们推出新服务时根据用户的地理位置分类用户,那样,他们就可以从一个类似新西兰这样的小国家开始。McDonnell解释说:

对于任何geoip.country_code是NZ的用户,他们会设置一个X-BF-SiteRouter头。然后,在VCL代码中,如果X-BF-SiteRouter头已经设置(取决于请求哪个路由),我们会把后端切换到Site Router。

一旦我们满意,我们就会把服务区域扩展到AU、GB、OC、EU、SA(南美),最终,我们会把所有东西都指向Site Router(实际上,北美是最后一个区域)。

我们还使用了Vary头保证来自不同区域、还没准备好访问Site Router的用户不会意外从中获得缓存的响应。

迁移到新架构已经在团队生产率方面为我们带来了益处,McDonnell告诉我们:

工程师可以根据自己的意愿自由启动服务,我们每天部署很多次。周遭有各种流行的术语,如持续集成、持续交付和持续部署,它们只有稍微的不同。最终,我们有了一个非常棒的交付管道,但是,我们特有的平台实现无法自动把变更推送到生产环境,目前我们还得手动选择,那对于我们的应用场景而言是合理的(例如,并不是我们构建的所有东西都必须自动存在于生产环境中)。

BuzzFeed有一个富A/B系统,用于测试单个的“特性”,规模较大的基于A/B测试的上线在他们的方法中反而变得比较简单(综合运用VCL逻辑和Site Router特有的“override”配置来处理请求路由)。

新基础设施监控(以及日志聚合和分析)是使用DataDog实现的。他们还在评估Honeycomb.io

关于作者

Charles Humble于2014年3月成为InfoQ.com编辑团队负责人,指导我们的内容创作,包括新闻、文章、著作、视频演讲和采访。在承担InfoQ的这个全职角色之前,Charles负责我们的Java报道,并且是薪酬研究公司PRPi Consulting的CTO,该公司2012年7月被PwC收购。他已经从事企业级软件工作将近20年,做过开发,当过架构师和开发经理。在业余时间,他从事音乐创作,是伦敦氛围科技舞曲团Twofish的三名成员之一。

查看英文原文:How BuzzFeed Migrated from a Perl Monolith to Go and Python Microservices

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注