QCon 全球软件开发大会(北京站)门票 9 折倒计时 4 天,点击立减 ¥880 了解详情
写点什么

Gilt 如何将微服务部署到 AWS 环境,介绍 ION-Roller

2015 年 7 月 08 日

经过七年的发展,gilt.com 已经从一个使用 Ruby on Rails 开发的创业公司成长为使用 Scala 微服务架构的主流电子商务平台。Gilt 的限时抢购商业模式的基础是:在短时间内会涌入大量的客户访问,以竞买某些限量的奢侈品。通过使用微服务架构,它为我们的服务提供了可伸缩性、性能以及可靠性的结合,同时也为我们的开发团队带来了自治性、自主性以及灵活性。团队可以自由地选择编程语言、框架、数据库以及构建系统,为核心的网站功能、移动应用、个性化算法、实时数据源以及通知功能创建服务。

随着软件服务的大爆发,随之出现的问题是如何部署与运行我们所创建的代码。在 Gilt 发展的早期,它们的软件运行在“裸机”之上,部署在一个、随后升级为两个传统的数据中心之内。团队需要申请新的硬件设置,随后通过自定义脚本和 Capistrano 的混合使用进行部署。这种方式虽然能够运作,但也带来了很大的困难。对虚拟化技术的首次尝试表现为在每日的访问高峰时性能表现不佳,大量的服务最终在同一个物理环境内相互交织在一起,导致了性能方面的异常,并且有时会由于缺乏资源的分离性而导致停机。在访问高峰时,对于某个服务进行大规模的部署会占用“每次请求的多个线程”,由此造成了整个网站的瘫痪。

我们曾经尝试了多种技术,并且创建了各种工具,通过在生产环境中进行测试和持续部署等手段试图减轻这方面的问题。但经过研究发现,我们用于在数据中心的硬件上分配并自动设置服务的方式已经被证实是一种非常耗时、并且非常困难的方式。

因此我们开始考虑将微服务基础设施大规模地转移到云环境中,主要是指 Amazon Web Services(AWS)环境,并且提炼出不可变部署(Immutable Deployment)这一概念,它是由我们之前在函数式编程中所使用的不可变变量的经验所启发的。我们将一套原本基于自身的数据中心打造的工具“ION-Cannon”升级为一套即将开源的工具“ION-Roller”,促使我们开发 ION-Roller 这套工具的动力在于:

  • 允许团队通过声明式的方式指定如何将他们的服务部署到 AWS,例如可用的节点数的最小或最大值、每个实例的大小等等。
  • 提供一条将服务与应用作为不可变的 Docker 镜像部署到生产环境中的管道,可支持分阶段式发布,并且能够分阶段地将访问从老版本的服务转移到新版本的服务上。
  • 在大型发布的时候支持快速回滚,能够使一组已经处于“热”状态的实例仍然保持运行之前的版本。

在本文中,我们将谈论 ION-Roller 中的一些核心概念,以及相关的技术和实现途径,并且简要地叙述它的使用方式。

ION-Roller,基于云的微服务王国

我们首先将微服务世界想象成为构建在独立的、不可变的环境之上的 HTTP 终结点,通过 REST API 的方式进行访问。

HTTP 终结点

Gilt 使用 HTTP 作为系统间通信的传输封装,对于用户来说,HTTP 终结点表现为一个主机名与端口的组合(在它之上还有一个发现层)。我们的想法是,当组织中的软件与配置随着时间而演变时,将这个终结点作为一个组织级的概念进行使用。

虽然这个概念很简单,但可以实现许多实用的目标。通过对代理与 / 或网络配置的合理应用,我们就能够提供以下特性:

  • 逐步地发布新的软件版本
  • 安全地回滚至之前版本的软件或配置
  • 通过错误率以及延迟情况的监控,检测到异常情况的发生
  • 能够了解哪些软件提供了终结点
  • 能够了解软件或配置随时间进行变更的信息

在描述环境时配置胜于代码

在运维规模非常小的情况下,在部署过程中要求得到不受限制的灵活性看起来是一种合理的诉求。但随着规模的增加,这种方式很快就变得难以管理、监控及理解了。

声明式的配置在描述及管理部署方面是一种实用的工具,它能够以一种结构化的风格进行管理。从配置的角度来说,它能完成的任务比起代码来说更为有限,但也因此能够对请求进行分析与报告。

在软件部署中进行风险管理

软件发布包括了各种权衡:灵活性与简便性、以及速度与安全性。

大多数生产环境的发展方向是转向新的部署流程,这种流程能够在特性开发或 bug 修复完成之后在更短的时间之内让用户看到这些变更。这也表示对生产环境进行变更的机率更高。由于每次发布都内含着风险,因此这一策略也有它的不利之处。为了抵消这种不利情况,团队需要采取一些能够缓解每次发布中的风险的发布流程,让每次发布都成为一个日常的、安全的操作。

风险体现在多个方面:异常的数量和严重度、受影响用户的数量及比例、以及从故障中恢复的时间长短。

发布流程对于异常的数量以及异常的严重度的影响并没有开发流程那么高,但频繁的发布通常能够使找到某个异常发生原因的速度更快(因为每个发布中的变量数量减少了)。

我们所能做的就是减少受影响的用户数量,以及减少修复的时间。在生产环境上进行测试、部分发布以及金丝雀部署(canary release)等方式正适用于这一场景。如果能够在没有任何生产环境中访问的情况下对新的发布进行测试,那么就能够将影响降至最低(甚至为零),但这种方式能够找到的异常数量也是最少的。由于许多异常情况只在常规的访问中才会出现,因此一台金丝雀机器(运行新软件的一个单一实例)是很有价值的工具,通过它可以了解新的发布是否适于使用。目前为止,对用户的影响依然是很小的,这取决于你如何对服务进行分片。保持渐进式的发布过程也是通过将受影响的比例最小化,将 bug 对用户的影响降至最低。

从故障中恢复的时间是不可变软件方式的一个主要优点,因为旧版本的软件依然“可用”。一旦发现异常情况,可以以最小的风险在最短的时间内回到之前的旧版本。我们稍后将进一步对不可变部署展开讨论。

运行中软件的期望状态以及实际状态

我们希望持续地监控某个终结点背后系统的期望运行状态以及实际运行状态的差别。如果这种差别确实存在,那么我们就应当(逐渐地)让实际状态向期望状态靠拢。我们持续地对运行环境进行大量的测试(包括运行的软件、负载均衡器设置、DNS 等等),如果其中任何一项不能够满足需求,就将它替换掉。

举例来说,假设我们知道访问应当由某个特定版本的软件进行处理,却发现没有任何一台运行这个版本的服务器是可用的(可以出于任何原因,包括某人无意中移除了这些服务器),那么就应当请求进行部署。

不可变部署

“不可变部署”的思想是通过一个已定义的、易于理解的配置部署软件,而且这个配置不会随着时间而改变。软件与配置都是发布中的自然组成,你不能通过修改数据中的某些地方随后重启的方式对软件进行变更,而是重新生成该软件的一个副本,让其中包含经过更新的细节内容。

这种思想能够产生更易于预测的环境,并且当某个新发布被证实“有问题”的情况下,能够回滚至软件的老版本而无需进行重启。(即使能够找到老版本的软件,在有些情况下要完全启动它也是一件费时费力的任务,因为它可能需要进行加载缓存等工作。)

在一个小规模的环境中,可能只存在少量的运行服务器,因此可能只有在发布完全结束之后,才能够发现新的发布中出现的异常情况。在传统的非不可变发布场景中,所有旧版本的软件都已经被替换了。

不可变部署方式在我们目前的服务中的实现还存在着一点不足之处。因为所有的环境都是按需搭建的,因此搭建时间取决于某些因素,例如启动新 EC2 实例的时间、以及下载适当的 Docker 镜像的时间。与之相比,一些其它部署工具要求存在一个机器池,不要求在软件安装之前搭建机器。因此我们选择为可回滚性牺牲了一些低延迟特性,因此在软件发布后的初次启动会有较高的延迟。

Docker 镜像

在微服务环境中,将工具与编程语言的选择从部署环境中抽象出来是一种非常实用的概念。它允许个别团队在开发软件时具有更多的自治性。虽然这方面的选择已经有许多,从特定于操作系统的包管理系统 —— 例如 RPM,到特定于云提供商的平台 —— 例如 Packer,但我们最终还是决定用 Docker 实现这一目标。Docker 在选择部署环境与策略方面提供了很高的灵活性,并且十分符合我们的需求。

重新发明轮子?

当我们在寻找能够方便地管理基于不可变 web 服务器的基础设施时,所找到的选择是非常有限的。大多数现有的软件都是为了便于软件更新进行优化的,但这不是我们所感兴趣的更新方式。我们也希望能够管理 web 服务器流量的整个生命周期,但没有发现任何一种对这一任务进行优化的选择。

另一点需要考虑的是,到底是要求开发者学习并理解某种现有的部署工具集所带来的价值更高,还是说通过一种无冲突的、简化的工具,能够满足我们将某个特定版本的软件部署到生产环境中的方式能够带来更高的生产力。我们相信,开发者应当能够简单地发布软件,而无需理解复杂的底层机制(同时也保留进行自定义的可能性,这在一些高级场景中是必需的)。

不什么不选择 Puppet、Chef 等工具

许多工具采取的都是以机器为中心的视角,配置是在每一台机器的基础上完成的。我们则采取了系统状态这一更广泛的视角(比如我们需要某个软件的四个拷贝以提供服务),而并不太关心个别机器的情况。我们利用了 AWS 所提供的高层概念,例如将机器运行情况检查时总是失败的那些机器进行替换,以及当 HTTP 终结点的访问量激增的情况下动态地扩展它的能力等等。

为什么不选择 CodeDeploy

CodeDeploy 是一套由 Amazon 提供的软件发布管理系统,但它无法满足我们支持不可变基础设施的需求。虽然很容易搭建脚本,使用 CodeDeploy 发布软件,但你需要预先搭建你的环境。此外,CodeDeploy 本身没有内置的部署 Docker 镜像的功能。

为什么不选择简单的 Elastic Beanstalk

Elastic Beanstalk 提供了创建环境的各种功能,它允许你运行 Docker 镜像,并且创建大量的支持性系统,例如 EC2 实例、负载均衡器、自动扩展组(根据访问量改变服务器的数量)。它还允许对日志文件的访问,并且可以对这些系统进行一定程度的管理工作。

但是,它对于“不可变部署”概念的支持非常有限,对于某部分软件的多次发布将为同一个用户可见的终结点带来大量的访问量,然后逐渐地转移访问量。对于这一点,它唯一支持的能力就是“切换 CNAMEs”,这是一种非常粗糙的转移访问量的方式,因为所有的访问量都会瞬间转移到新的环境中。此外,出于 DNS 的本质,它在可靠性方面也存在问题,因为 DNS 查找结果在 DNS 进行变更后依然会被缓存一段时间,并且某些糟糕的客户端会忽略 DNS TTL 值,导致访问量依然在很长的一段时间内会被发送给旧的环境。

此外,Elastic Beanstalk 没有提供某种高层次的结构,以帮助你理解在某个终结点的背后有哪些东西运行在生产环境上。“有哪些运行正在运行,它们的配置情况又是怎样的”这一问题并不容易解答,需要某些高层次的系统辅助。

我们决定利用 Elastic Beanstalk 作为一种实用的方法以部署 Docker 软件,但需要有层次地进行适当的管理,并且对它的层次进行控制,以便为用户提供一个完整的工作流。

介绍 ION-Roller

ION-Roller 是一套服务(包含 API、web 应用和命令行界面),它利用了 Amazon 的 Elastic Beanstalk 及其底层的 CloudFormation 框架的功能,将 Docker 镜像部署到 EC2 实例中。

开始使用 ION-Roller,你需要进行一些简单的操作

启动部署软件过程的全部工序如下:

  • 准备一个 AWS 帐号,并在 ION-Roller 中为其授权,它能够为你启动实例并访问资源(如果你的组织在运行软件时使用了多个 AWS 帐户运行,ION-Roller 能够为所有这些帐号提供一个单一的部署视角,只需具有足够的权限即可)。
  • 由某个 Docker registry(可以选择 hub.docker.com,或使用私有 Docker registry 服务)提供的 Docker 镜像
  • 准备软件的部署规格说明,包括 HTTP 终结点、EC2 实例的数量与类型、Docker 镜像的运行时参数、环境变量、安全设置等等。它们将作为你的服务配置的一部分提供,并通过 REST API 提交至 ION-Roller。

如果想了解使用 AWS 帐户安装 ION-Roller 的细节与完整的文档,敬请期待即将开源的这个项目 https://github.com/gilt/ionroller

使用 ION-Roller 部署软件

你可以通过内置的命令行工具简单地启动部署过程:

ionroller release <SERVICE_NAME>

这个工具能够在发布过程中提供即时的反馈信息:

复制代码
[INFO] NewRollout(ReleaseVersion(0.0.17))
[INFO] Deployment started.
[INFO] Added environment: e-k3bybwxy2f
[INFO] createEnvironment is starting.
[INFO] Using elasticbeanstalk-us-east-1-830967614603 as Amazon S3 storage bucket for environment data.
[INFO] Waiting for environment to become healthy.
[INFO] Created security group named: sg-682b430c
[INFO] Created load balancer named: awseb-e-k-AWSEBLoa-A4GOD7JFELTF

你也可以选择以编程的方式触发一次部署过程,ION-Roller 提供了一套 REST API,可以对你的配置和发布过程进行完全控制。

在系统的背后,ION-Roller 会触发一个 Elastic Beanstalk 部署流程,充分地利用了它的功能,包括创建一个负载均衡器、安全以及自动扩展组、设置 CloudWatch 监控,并从某个 Docker registry 中获取特定的 Docker 镜像。

访问重定向

一旦 ION-Roller 检测到部署成功之后,它就能够安全地逐渐将访问从老版本转移到新版本的服务。

访问的重定向是通过对 EC2 实例的修改实现的,它能够通过负载均衡器对 HTTP 请求进行响应。随着发布的流程推进,新部署的实例会逐渐加入负载均衡器的配置中,而原先部署的实例会逐步被删除。这一流程的启动时机是可配置的。

渐进式的访问重定向使你能够对最近的发布进行监控、快速地检测到失败、并在必要时进行回滚。

软件回滚

由于在发布过程中,老的环境依然可访问,老版本的软件也依然在运行中,因此我们可以安全地将软件回滚到之前的某个老版本。无用的旧实例在一段时间(可配置)之后才会被移除,由于这段延迟的存在,因此我们在发布全部完成之后的一段时间内仍然可以选择回滚。

我们的目标是持续地监控终结点的运行情况,并且在检测到异常的情况下自动地回滚到老的版本。我们将使用 Amazon 的 CloudWatch 警告功能发出一个即将进行回滚操作的信号。

进行手动回滚非常简单,运行以下脚本即可:

ionroller release <SERVICE_NAME> <PREVIOUS_VERSION>

如果旧的实例仍然可用,那么只需几秒钟的时间即可将访问全部转回老版本上。当然,前提是你没有进行任何使老版本的软件不可用的操作(例如更新数据存储系统的 schema 等等)。如果你希望依靠这种能力以回滚软件,那么牢记这一点是非常重要的,无论你使用的是哪一种部署系统。

金丝雀发布以及在生产环境中测试

ION-Roller 通过对访问转移流程的配置,支持金丝雀发布的概念。当有新的版本部署到一些初始的实例之后,该流程就会停止,可以对生产环境中的访问进行一些发布测试。在一段可配置的时间之后,发布流程将会继续推进。

在生产环境进行测试

对于某些用例,你希望能够对新的软件进行测试(或演示),而又不希望产生实际的访问,那么我们需要 ION-Roller 搭建一个分离的 HTTP 终结点,在生产环境的终结点更新之前,可以通过它处理请求。

留意系统状况 - ION-Trail

ION-Roller 能够查看某个终结点背后的环境与软件随着时间推移产生的变化,在对环境进行监控或进行变更的过程中,它将记录与环境生命周期或部署活动相关的事件。可以通过这一功能实现系统的审计、监控以及报表功能。ION-Trail 是一个支持性的服务,为所有被记录的部署活动提供了一个事件源。

结论 ——去中央化的 DevOps

希望读者在阅读本文后能够对于我们将微服务架构部署到 AWS 的思想有所了解。ION-Roller 允许我们将整个 DevOps 组织实现去中央化,并且通过声明式的部署让工程师更易于掌握。ION-Roller 允许我们进行分阶段的发布与热回滚,并且支持诸如“金丝雀发布”与“在生产环境中测试”等特性。如果想了解更多的信息,请关注 Gilt Tech 博客( http://tech.gilt.com ),我们很快就会在博客上宣布 ION-Roller 开源项目的公开发布。

关于作者

Natalia Bartol曾在IBM**** 担任Eclipse 支持工程师 **,从事改善开发者工具的工作,随后在 **Zend Technologies 担任 Eclipse 开发人员及团队主管。如今她在 Gilt 担任软件工程师,专注于微服务的部署以及提高开发者的生产力。Natalia 拥有波兹南科技大学软件工程专业的理科硕士证书。

Gary Coady**** 是来自Gilt Groupe 的一位高级软件工程师,他的工作是自动化部署、编写构建工具以及传授 Scala 技术。他之前曾在 Google 担任网站可靠性工程师,将大量时间用于剖析、管理以及处理大规模环境中的运维异常。Gary 拥有都柏林大学圣三一学院计算机科学专业的学士学位(Mod)。

Adrian Trenaman在位于爱尔兰都柏林的 gilt.com 担任工程师高级副总裁。他拥有爱尔兰国立梅努斯大学计算机科学专业的博士学位,和爱尔兰管理学院的商业开发专科文凭,以及都柏林大学圣三一学院计算机科学专业的学士学位(Mod. Hons)。

查看英文原文: Deploying Microservices to AWS at Gilt: Introducing ION-Roller

立即免费注册 AWS 账号,获得 12 个月免费套餐:点击注册

有云计算问题?立刻联系 AWS 云计算专家:立即联系

2015 年 7 月 08 日 09:271758
用户头像

发布了 428 篇内容, 共 151.1 次阅读, 收获喜欢 23 次。

关注

欲了解 AWS 的更多信息,请访问【AWS 技术专区】

评论

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

Week5 作业1

shuyaxx

真零基础Python开发web

MySQL从删库到跑路

Python django Web bottle

请简述 JVM 垃圾回收原理

orchid9

Snowpack - 更快的前端构建工具

曲迪

效率工具 前端 前端工程化 前端进阶

应届秋招生,熬夜吃透华为架构师这份‘典藏级’计算机网络+计算机操作系统,成功上岸腾讯

云流

网络协议 编程之路 计算机知识

大数据和Hadoop平台介绍

MySQL从删库到跑路

大数据 hadoop

week5 作业二

shuyaxx

5G+工业互联网的中国登山队,如何攀跃“产业化”山峦?

脑极体

秒杀系统

橘子皮嚼着不脆

架构师训练营 week5 课后作业

花果山

极客大学架构师训练营

第五周作业

晴空万里

极客大学架构师训练营

第九周学习总结

orchid9

技术选型总结一

Mars

技术选型

架构师训练营 1 期第 9 周:性能优化(三)- 总结

piercebn

极客大学架构师训练营

架构师训练营第二期 Week 5 总结

bigxiang

极客大学架构师训练营

第9周作业1

Yangjing

极客大学架构师训练营

第9周作业2

Yangjing

极客大学架构师训练营

架构师 01 期,第九周课后作业

子文

架构师训练营第 9 周作业

netspecial

极客大学架构师训练营

架构师训练营

【喜讯】Apache DolphinScheduler 荣获 “2020 年度十大开源新锐项目”

海豚调度

Apache DolphinScheduler Apache DolphinScheduler 新一代大数据任务调度 十大开源新锐项目

Java 中常见的细粒度锁实现

rookiedev

Java 多线程 细粒度锁

架构师训练营第二期 Week 5 作业

bigxiang

极客大学架构师训练营

Netty源码解析 -- 对象池Recycler实现原理

binecy

Netty 对象存储 高性能

训练营第五周作业

大脸猫

极客大学架构师训练营

架构师训练营第 9 周课后练习

叶纪想

极客大学架构师训练营

InfoQ 写作平台的魔力

Yolanda

能源区块链研究|区块链与核电安全

CECBC区块链专委会

区块链 核电

Week 9 作业01

Croesus

助推城市智慧化!正舵者携手中科院演绎区块链魅力

CECBC区块链专委会

区块链 人工智能

第九周作业

极客大学架构师训练营

边缘计算隔离技术的挑战与实践

边缘计算隔离技术的挑战与实践

Gilt如何将微服务部署到AWS环境,介绍ION-Roller-InfoQ