原文:How We Build Code at Netflix
作者:Ed Bukoski, Brian Moyles and Mike McGarr
译者:杰微刊兼职翻译刘晓鹏
Netflix在部署到云环境之前是如何构建代码的?过去我们已经讨论过这个问题的部分内容,现在是到分享更多细节的时候了。在这篇文章中,我们将描述这个为7500,0000 全球Netflix成员提供电影和TV播放的应用是如何实现从源代码到服务部署的。
上面的图表是对Spinnaker(我们的全球持续交互平台)之前发布博文的扩展。每一行代码在进入Spinnaker之前,都需要经过很多的步骤:
1、代码构建并使用 Nebula进行本地测试。
2、将修改的代码提交到git中心仓库。
3、 Jenkins任务执行 Nebula,包括构建,测试以及应用程序的打包。
4、将新的版本放到AMI(Amazon Machine Image,亚马逊虚拟机镜像)上“烘烤”。
5、 通过Spinnaker流水线部署和升级修改后的代码。
本文剩余部分将演示每个阶段所使用的工具和处理过程,以及为什么我们要采用这样的方式。通过分享一些我们现在正处理的挑战,我们将变得更加密切。在Netflix 构建和部署代码的过程中使用了很多工具,也遇到了很多挑战。你们可以期待这是描述这些工具和挑战细节的第一篇文章。
文化,云与微服务
在深入 Netflix如何构建代码之前,有必要强调几个驱动和塑造我们解决方案的关键要素:我们的文化,云及微服务。
Netflix的文化是自由与责任,这种文化使得工程师可以采用任何他们认为最适合任务的工具来创建解决方案。根据我们的经验,如果一个工具被广泛使用,那它一定是令人信服的,具有巨大价值的,并能减少绝大部分 Netflix工程师认知负担的。团队可以自由的修改实现方案,但同时也有责任来维护这些方案。Netflix核心团队提供的工具本被认为是这条“柏油路”的一部分。我们今天所关注的仅仅是这条“柏油路”上支持的引擎工具(Engineering Tool)。
此外,2008 年,Netflix开始将流服务移植到AWS上,并将基于Java 应用的庞大数据中心转换为基于 Java微服务的云。我们微服务架构允许 Netflix团队以松耦合的方式存在,并以他们认为合适的速度来构建和推进更新。
构建
自然,部署一个应用或一个服务的第一步就是构建。我们创建了 Nebula(一个 Gradle构建系统上的可选插件集)来辅助繁重的构建任务。Gradle为构建,测试及打包 Java应用(这包含了我们大部分的代码)提供一流的支持。之所以选择 Gradle是因为它易于编写可测试的插件,同时降低了项目构建文件的大小。Nubula继承了 Gradle 健壮的自动化构建功能,包括一系列开源的依赖管理插件,版本管理、打包等等。
上面的“build.gradle”文件展示了在Netflix中是如何定义构建一个简单Java应用的。该项目的构建文件声明了几个 Java依赖,同时使用了 4 个Gradle插件,其中3个要么是Nebula的一部分,要么是应用在Nebula上的内部插件。“nebula”插件是一个只在内部使用的Gradle插件,为整合我们的基础架构提供规则和必要的配置。“nebula.dependency-lock”插件可以使项目生成一个基于版本的.lock的文件,用于解决依赖和重复构建问题。“netflix.ospackage-tomcat”插件及ospackage块将在下文中接触到。
通过Nebula,我们提供可重用和一致的构建功能,其目标是减少每个应用程序构建文件中的样板文件。在以后的技术博客中将更加深入的讨论 Nebula及其特性(我们已经将其开源)。现在,我们可以通过Nebula 网站来获取源码。
整合
当一行代码构建完成并通过了本地 Nebula的测试,就准备对其进行持续集成和部署了。第一步是将更新过的源码推送到git仓库。团队可以自由寻找git工作流。一旦提交了更新,就会触发一个Jenkins的任务。我们使用 Jenkins进行持续集成已经有好几年了。一开始,数据中心使用的是一个大的 Jenkins master ,现在,在AWS上已经发展成为 25 个Jenkinsmaster了。在整个 Netflix上,Jenkins主要用于各种自动化测试的任务,以上提到的只是简单的持续集成。
一个Jenkins任务是可配置的,涉及构建、测试和应用程序代码的打包。如果被构建的仓库是一个库文件,Nebula将发布.jar 到我们的归档仓库。如果仓库是一个应用,Nebulaospackage插件就会执行。使用 Nebula ospackage(操作系统包的简称)插件,一个应用构建出的归档文件就会绑定到 Debian或RPM包上,它们的内容通过一个简单的基于DSL的Gradle来定义。然后,Nebula将会发布 Debian 文件到一个包仓库,这个仓库对处理流程的下一个阶段——烘烤是有用的。
烘烤
我们的部署策略是以不可变服务器为中心的模式。为了减少配置漂移,确保源代码可重复部署,强力反对在线修改实例。Netflix的每次部署首先都会创建一个新的AMI。为了从源头生成 AMI,我们创建了一个“Bakery”。
当一个Jenkins任务执行成功后,通常会触发一个 Spinnaker流水线。Spinnaker流水线可被一个Jenkins 任务或一次 Git提交触发。Spinnaker将读取由Nebula生成的操作系统的包,然后调用Bakery API来进行烘烤。
部署
一旦烘烤完成,Spinnaker使得最终的AMI可以部署数十、数百甚至数千个实例。由于 Spinnaker对实例暴露的运行时环境是允许应用在运行时自由配置的,这样就使得的AMI可以跨多个环境。一次成功的烘烤将触发 Spinnaker流水线的下一个阶段——部署到测试环境。从这起,团队通常会使用一连串的自动化集成测试来执行部署。从这一点开始,特定应用程序的部署流水线将变得非常定制化。团队将使用 Spinnaker来管理多区域部署,Canary 版本、红/黑部署等等。可以这么说,Spinnaker流水线为团队如何控制部署代码提供了极大的灵活性。
未来之路
总之,这些工具使得诸多工作变得高效和自动化。例如,我们只需要 16 分钟,就可以完成对云弹性计算和维护服务(JanitorMonkey)从代码检出到多区域部署的过程。
也就是说,我们一直在努力提高开发者经验,不断的挑战我们自己,以便做到更好,更快,同时更简单。
我们正积极应对的一个挑战是如何在Netflix中管理二进制依赖。Nebula 提供的工具侧重于简化 Java的依赖管理。例如,Nebuladependency-lock插件可以生成一个基于版本的.lock文件,从而使得应用程序解决它们完整的二进制依赖。Nubularesolutionrules 插件允许我们发布整个组织的依赖规则,从而影响整个Nebula的构建。这些工具使得二进制依赖管理更加简单,但是还是没有将“痛苦”减少到可以接受的范围。
另一个挑战我们正在处理的是烘烤时间。不久之前,16 分钟完成从代码提交到部署只是一个梦想。但是现在,由于系统其它部分都变得更快了,这反而成为快速创新的一个障碍。从上面部署SimianArmy的例子可以看出,烘烤的过程花了 7 分钟,烘烤占据了整个部署时间的 44%。我们发现烘烤时间中最耗时间的操作是安装包(包括依赖解决)和 AWS快照自身的处理。
随着Netflix的成长和发展,存在一个不断增长的需求,就是我们的构建和部署工具需要为非 JVM语言提供很好的支持,如JavaScript/Node.js, Python, Ruby和 Go。对非 JVM应用,我们当前推荐的的方式是使用 Nebulaospackage插件来生成 Debian包,以便对其进行烘烤,对工程师来说,如果跳过构建和测试,平台是其首选的工具。这虽然解决了团队当前的需求,但是还是需要扩展其成为语言无关的工具。
容器为最后两个挑战提供了一个有趣的可能的解决方案,我们正在探索如何通过容器来帮助我们改善构建、烘烤和部署的过程。如果我们能够提供一个基于容器的本地环境,就能更类似的模拟云环境,从而可能减少开发和测试过程中的所需的烘烤次数,提高开发者的生产力,加速整个开发过程。如果容器可以部署在本地,这样在服务器上就不需要进行修改,从而减少认知的负担,工程师就只需关注问题的解决和创新,而不是纠结bug的产生是不是因为环境的差异。
你可以期待在以后的文章中提供我们如果解决这些问题的更新。如果听到这些挑战让你感到兴奋,请加入我们的工程工具团队。你可以查看我们现在开放的职位,并马上申请!