1. 背景
在过去几年里,社交、移动和云计算深刻改变了整个互联网的格局。作为设计软件领域的全球领导厂商,Autodesk 也与 2009 年正式开始从传统桌面设计软件提供商向在线服务、协作和移动端设计转型。在这次转型中,公司充分利用现代云计算的巨大优势给客户带来了大大超过传统桌面软件的处理能力、用户体验和性价比。其中 AWS 是目前公司服务的主要运行平台,每年在此投入千万美金级别。
1.1. 传统软件交付的挑战
在过去的 30 多年里,Autodesk 拥有了非常多的桌面设计软件(如 AutoCAD,Maya,3dsMax 等)。由于设计软件经常需要处理超大的数据集合(如整个上海中心的设计模型)和极其复杂的运算逻辑(如阿凡达电影画面的绘制),其软件尺寸一般都比较大(GB 级别)。以前,客户基本都是通过互联网下载、快递或者分销商获得软件安装包,整个过程比较耗时。另外,软件的升级、安装和维护也是一个非常大的工作量(一些大的设计公司要购买上千份软件拷贝)。尽管公司软件已经支持基于应用程序虚拟化的集中管理模式,但它还是有如前期基础设施建设成本大,服务能力缺乏弹性,仍需要专职运维人员等明显的缺点。所以,提升软件交付的用户体验成为我们必须要面对的一个问题。
如大多数人所猜测,SaaS 成为我们的第一个尝试方向。在 2006 年,Autodesk 实验室开始尝试以 SaaS 提供设计软件服务的可能性,并且取得了不错的成绩。但是,目前 SaaS 应用仍然面临着浏览器能力限制、大数据传输慢等诸多问题,无法给专业设计师提供传统桌面软件一样的体验和设计能力。
1.2. 云计算带来的新可能
随着云计算的兴起,以 AWS 为代表的基础设施云提供商让我们有可能以一种全新的方法去解决这个问题。我们可以利用基础设施云的强大后台来帮助用户运行虚拟化的软件实例。用户无需下载、安装和维护这些软件,只需要链接到互联网上就可以在线使用我们的软件。而且按需付费以降低使用成本。基于此,Autodesk 实验室在 2009 年开始这个尝试(注:该服务已经于 2013 年成为公司云平台产品的一部分),并选择了 AWS 做作为我们的后台云服务提供商。选择 AWS 有下面的几个原因:
- 需要基础设施云(IaaS)。现在的平台云(PaaS)大部分都是为 Web 服务准备的,并不适合我们运行虚拟化实例的要求。
- 需要强大的弹性运算能力。Autodesk 设计软件对于计算的需求都很大,而计算能力的成本不低。所以,弹性计算能力能让我们很好的控制成本。AWS EC2 在这方面非常符合我们的需求。
- 需要丰富的服务。除了运算能力,我们还需要给用户提供海量设计数据的存储,快速的数据访问,安全的访问控制等。AWS 云服务在这方便服务非常完备,而且相互集成很容易。
- 需要稳定的服务。AWS EC2 能够提供超过 99.5% 的弹性计算可用率,能为我们建立可靠服务提供坚实的基础设施。
- 需要全球化部署。Autodesk 是一个在全球提供软件、服务的公司,所以我们希望基础设施提供商也能全球布局。而 AWS 已经在全球建立多个数据中心。
2. 自动化部署
在完成服务的基本实现并上线服务后,整个后台的维护和部署成本在不断加大。尤其考虑到我们需要高频(每个月更新一次)、多地(多个 AWS 数据中心)部署服务并同时需要维持高的服务可用性,构建一个自动化的部署系统成为必须要做的事情。
2.1. 设定目标
作为一个运行在 AWS 上的服务,我们在设计之初就开始思考 AWS 给自动化部署带来的新可能和挑战。在我们看来,针对 AWS 上服务的自动化部署需要特别关注到下面的一些特点:
- 基础设施的服务化。在 AWS 中,你可以利用类似 Cloud Formation 服务在很短时间(几分钟)获取你所要的所有基础设施(包括运算、存储、网络、IO 等),而这些基础设施已经按照你的要求自动配置、连接好。所以,我们可以让自动化部署把基础设施的管理也包括进来,而这在传统的数据中心模式下很难实现。
- 支持弹性资源。因为需要支持弹性,整个服务要在运行时动态加入新的基础设施,如计算单元等。自动化部署系统需要能让新加入的基础设施立马投入服务。
- 保护数据更为重要。在传统的数据中心,我们可以让部署完全在内部网络完成,在确认好所有的安全配置后再上线。但是 AWS 是一个公有云服务,你的所有部署其实是在公有网络上完成的。所以,我们必须在自动化部署中充分考虑到数据安全的问题。
结合上面的特点、DevOps 的普遍实践和项目的实际情况,我们给整个自动化部署系统定下了下面的目标:
- 一键式部署:必须尽可能的自动化所有部署过程,包括基础设施的创建和部署。
- 多环境支撑:必须能够适应于 Production、Staging 和 Development 环境。
- 无服务中断:必须能够无缝的进行服务升级、切换。
- 随时可回滚:必须可以很容易的回滚到前面的版本以处理意外问题。
- 安全性检测:必须在确认部署环境的安全设置已经满足条件后才开始做部署工作。
2.2. 整体架构
在确定目标后,我们进行了技术选型,希望能够找到既很好支持 AWS 相关自动化操作,又能集成 DevOps 优秀实践的方案(注:那时 AWS 还没有推出 OpsWorks 服务)。但最后并没有找到合适的方案,于是决定自己来实现整个自动化部署系统。经过几轮的改进和实现,现在的自动化部署系统整体结构如下:
图 1:自动化部署系统整体架构
类似于传统的自动化部署,我们也同样有开发分支和发布分支,并与持续构建系统对接。当开发人员提交代码后,相应的构建就会在代码所在的分支自动触发。在完成代码编译和集成的自动化测试(目前主要是单元测试)后,将产生相应部署组件并存放在构建系统中(目前,每个构建会产生所有的系统组件并拥有同样的版本号)。至此,整个构建过程完成。
为了支持多环境部署和安全隔离,我们使用两个独立的 AWS 账号来运行不同的环境。其中开发环境在一个AWS账户内,只部署开发分支的构建。而生产系统则在另外一个 AWS 账户中,其下运行 Prod/Staging 两组环境。发布分支永远只会向 Staging 环境部署并在完成最后验证测试后与当前 Prod 环境进行热切换,从而达到无服务中断的目的。
2.3. 一键部署流程
在完成自动化持续构建后,我们就可以部署其中的任意版本。当部署某一构建版本时(无论开发分支还是发布分支),整个流程如下:
图 2:一键式部署流程
1、触发“一键部署”命令:在构建系统中选择好要部署的分支和版本,直接一键点击触发命令。
2、上传部署文件到 S3:如图 1 所示,部署组件是在运行时动态下载安装。AWS S3 提供在线存储并能够方便的下载到 EC2 实例中,所以把部署组件上传到 S3 中最合适不过。为了支持多版本并存和部署回滚,所有上传到 S3 的部署组件都按版本号分文件夹存储。
3、获取当前部署环境的配置文件:部署环境配置文件存在相应 AWS 账户下的 S3 中。它定义了当前 AWS 账户下面的部署配置,包括需要部署的数据中心列表,每个数据中心下面的基础设施描述(Cloud Formation Stack)。由于 Prod/Staging 环境都在同一个 AWS 账户下,每个数据中心都会有两组 Cloud Formation Stack 配置(分别用于 Prod 和 Staging 环境)。部署系统会选择当前 Staging 环境的 Cloud Formation Stack 作为下一步的部署目标。
4、初始化部署目标基础设施:根据当前选中的 Cloud Formation Stack 初始化整个基础设施,包括启动相应的 EC2 实例,绑定 Elastic IPs 等。
5、运行部署环境:在整个基础设施运行起来后,EC2 实例第一步就是自动做运行时部署(利用操作系统的启动脚本实现)。具体运行时部署细节如下:
图 3:运行时部署
6、EC2 实例在完成整个自动化部署中要及时进行有效性检测,如检测下载组件包是否完整正确,组件安装是否成功,期望的服务是否可以正确访问等。在完成所有部署和有效性检测后方加入的正式服务系统并处理用户请求,否则及时报告运维团队进行处理。
2.4. 为什么选择运行时部署
看到上面部署流程,相信很多人会问一个问题:AWS 不是可以通过虚拟机镜像(AMI)来启动 EC2 实例,为什么不把上面的部署组件直接烧入 AMI?这样在 EC2 实例启动后就直接使用而无需做运行时部署。其实,我们的最初解决方案就是把部署组件直接烧入 AMI,但很快就发现了这种方案的局限:
部署组件的变化是非常频繁的,尤其是在发布前,一天都能够产生上十次的变化。这就意味着自动部署系统可能需要频繁产生 AMI。而 AMI 的创建过程并不快(10 分钟~1 小时),并且过多的 AMI 也会造成管理问题和额外的存储成本。
作为一个平台项目,各个产品会在我们的平台上运行。我们希望提供给各个产品部门的基本 AMI 是平台版本无关的。这样产品部门在基本 AMI 基础上部署它们产品并重新生成的产品 AMI 也是平台版本无关的。于是产品 AMI 可以不做任何修改就能够快速采用最新的平台版本。
具体如下图所示:
图 4:自动化部署中的 AMIs
为了保证图 4 中的基本 AMI 和产品 AMI 是平台版本无关,我们就需要保证烧入 AMI 的所有部分都是能够独立于平台版本的。但是,启动脚本需要做平台组件的运行时部署,显然这个运行时部署脚本也会随着平台更新不断变化,所以我们无法直接把整个运行时部署脚本放到启动脚本中。针对这个问题,我们的解决方案就是把整个运行时部署脚本分成两部分。一部分做平台组件的安装、检测工作,并随安装组件存到 S3 中,而另外一部分只做基本不会改变的事情(下载平台组件包并调用自动化部署脚本)并设置为操作系统的启动脚本。这也是图 3 中启动脚本和自动化部署分开两部分的原因。
正是因为采取了上面的 AMI 生成、管理策略,我们的产品部门基本上不用太频繁的更新它们产品的 AMIs(除非产品自身有更新),真正做到平台版本和产品版本的去耦合,极大的降低了运维成本。
2.5. 运行时版本选择与回滚
由于产品 AMI 是平台版本独立的,以它为镜像运行起来的 EC2 实例无法知道应该去下载哪一个版本的平台组件。幸运的是 AWS 的 EC2 服务提供了“User Data”机制。简单来说,就是在启动 EC2 实例的时候可以传入一段用户自定义数据给 AWS。当这个实例运行起来后,实例内部程序可以通过调用 AWS EC2 的元数据服务取得先前传入的用户自定义数据。于是,我们可以把当前需要运行的平台版本号以“User Data”的方式传给 EC2 实例,然后让启动脚本读取相应版本号并下载相应版本组件来部署。
同样,基于“User Data”机制和平台版本无关的 AMI,我们非常容易得实现版本的回滚。只需要修改传入给“User Data”中的版本号并保证要回滚到的平台组件版本仍然存在于 S3 中即可。即使是从零开始回滚到以前版本也可以在几分钟内完成(主要时间花费在启动 EC2 实例上)。在实际运营中,因为已经有了 Prod/Staging 的热切换,我们一般会在切换上新的版本后保持原来的版本运行一段时间(这段时间一般是问题的高发期)以便做到秒级的回滚。
2.6. 关于安全
如前面所说,我们需要格外关心在 AWS 上自动化部署的安全问题。在我们的实践中,时刻会遵循最小授权原则并利用 AWS 中的 IAM 服务实施,具体体现在下面的两个原则上:
- 不要让管理员之外的任何人直接使用 AWS 根账户(包括它的 Access Key 和 Access Security)。取而代之的是创建专门的 IAM User 并给于其必须的权限
- 不要在公司防火墙之外使用任何账号的 Access Key 和 Access Security。取而代之的是使用 IAM Role 来获取 EC2 实例运行时需要的资源访问权限。
例如,我们的构建系统需要访问多项 AWS 服务资源(如 S3、EC2、Cloud Formation 等),而构建系统又在防火墙内部,所以我们创建专用的 IAM User 来做自动化部署并给予构建系统需要的资源访问权限。另外,EC2 实例需要访问 S3 并下载部署组件,所以 EC2 应该以专用的 IAM Role 来运行并在 IAM Role 中给予相应的 S3 桶只读属性。
2.7. 为什么不是 AWS OpsWorks
熟悉 AWS 服务的人可能都知道 Amazon 已经在 2013 年发布了它的 DevOps 服务: OpsWorks。我们的自动化部署系统为什么没有选择这个服务呢?最直接的原因就是我们开始构建自动化部署系统时候,AWS 还没有发布 OpsWorks。在 AWS 发布 OpsWorks 后,我们对此做了仔细调研,并决定继续使用目前的系统。要了解其背后原因,就要完整理解 AWS 提供的一系列应用程序管理服务(Application Management Services)之间的关系。这里,让我们首先看看 AWS 文档中这张示意图:
图 5:AWS 中的应用程序管理服务
就如上图所示,AWS 为各种应用场景提供了不同的解决方案。由于针对的应用场景不同,这些服务的关注点也就不同。例如,AWS Elastic Beanstalk 主要是为 Web 应用程序提供快速部署服务(有点类似国内 SAE、BAE 等服务),它帮助开发和运维人员隐藏了大量的底层细节,非常方便上手部署应用。但是,该服务在管理底层基础设施架构上就基本没有多少灵活性,无法适应项目的个性化需求。就我们的服务而言,它开始于最右边的完全自己定制方案。在 AWS 推出 Cloud Formation 后,为了提高系统的效率和自动化程度,我们迁移到以 Cloud Formation 为基础的新方案。但是我们没有继续向 AWS OpsWorks 迁移。究其缘由,让我们先多方面比较一下 AWS OpsWorks 和 AWS Cloud Formation:
特性 | AWS Cloud Formation | AWS OpsWorks |
基础设施架构 | 以 Stack 的方式管理整个基础设施,你可以以任何你想要的架构组织你的基础设施。 | 遵循常见的架构实践,以 Stack、Layer 和 App 为核心观念来管理整个服务架构,并利用 Chef 来把 App 自动化部署到各个 Layer 上。尽管它也有很大的灵活性,但是你需要把自己的基础设施架构映射到上面的这些概念中以实施该服务。 |
AWS资源管理 | 支持几乎所有的 AWS 资源。你可以在 Cloud Formation 的 JSON 模板中方便地加入各种 AWS 资源。Cloud Formation 会自动帮你管理各种资源之间的依赖关系。你也可以自定义一些资源之间的逻辑依赖关系。 | 支持主要的 AWS 服务资源(如 EC2、EBS、ElasticIP,ELB 等),并利用这些资源构建常见的 Layers。当然你也可自己加入一些非内建的资源(如 RDS 服务)到 OpsWorks 中来,但是它无法达到 Cloud Formation 对于资源管理的广度。另外,目前的 OpsWorks 仅仅支持 Amazon Linux 和 Ubuntu LTS 操作系统,你可以使用这些系统的默认 AMI 或者在这些默认 AMI 基础上创建的新 AMI。 |
定制化 | 支持利用参数改变由资源模板定义的 Stack 默认行为。 | 支持利用自定义 JSON 改变 Stack 的默认行为。 |
自动部署 | 支持各种自动化部署工具(如 Chef、Puppet 等),但是你需要自己实现所有的自动化部署脚本。 | 支持基于 Chef 的自动化部署,并且提供了大量的内建自动化脚本。这些内建的自动化脚本会帮助开发和运维人员完成很多的常见工作(如自动部署 Tomcat 服务器等)并已经集成到整个服务中去。另外,它同样支持自定义的 Chef 脚本来实现项目相关的部署。 |
弹性支撑 | 支持完全自由控制的弹性策略。用户可以使用 Auto-Scaling 组加自定义的性能指标来确定 Scale-up/down 的条件。 | 提供基于负载(Load-Based)和基于时间(Time-Based)的弹性策略。其中基于负载的弹性策略主要考虑 CPU、内存等几个主要因素。 |
系统监控 | 支持基于 Cloud Watch 的监控机制。用户可以监控大量的内建指标并添加自定义的监控指标到 Cloud Watch 中去。 | 提供以 Stack 为单元的整合监控界面,同样以 Cloud Watch 为基础提供监控服务。另外,它还提供了基于 Ganglia 的可选监控方案。 |
安全管理 | 支持 IAM 的完整功能。用户可以精细控制各个资源的访问权限。 | 支持 IAM 的完整功能。并能方便的把相应的安全策略批量实施。 |
表 1:Cloud Formation 与 OpsWorks 的比较
参考上面的比较和我们目前的自动化部署系统,OpsWorks 对于我们仍然有一些限制:
无法支持 Windows 操作系统。我们的很多服务仍然是运行在 Windows 系统上。
无法提供自定义的弹性策略。我们的系统实现了自己的调度算法,目前把该调度算法映射到现在 OpsWorks 中还比较困难。
随着 OpsWorks 的发展,这些限制未来都可能会被解决。但是,作为一个已经运行的系统,我们仍然需要仔细评估 OpsWorks 带来的收益和成本以确定是否迁移。如果需要在 AWS 上面部署一个新的应用,推荐的实践就是如图 5 从左向右依次选择,并且在确认左面的方案有明确限制的情况下再考虑下一个选择。如果图 5 中左边服务已经能够解决问题,就不建议自己开发相应的系统。毕竟整个部署系统的开发和维护还是需要一个不小的成本,而 AWS 提供的这些应用程序管理服务都免费的。另外,在绝大多数情况下,Cloud Formation 已经足够灵活以满足你在 AWS 上部署服务。但是,在某些情况下(例如,同时支持在多个云服务提供商上部署服务),你可能还得选择完全自己开发或采用第三方供应商的方案。
在过去的一年里,我们利用上面的自动化部署系统让整个部署过程的耗时从最初的几天快速下降到几分钟之内,大大降低了的开发、运维人员的负担。如此同时,自动化部署还把部署出错的可能性降到了一个极低的水平,提高了系统的整体可用性。
3. 总结
在上面的文章中,我仔细介绍了整个自动化部署系统,并讨论了怎样充分利用 AWS 服务的特点来提高自动化部署的效率。该系统运行高效、稳定,而且极大程度的降低了平台自身和运行于平台上产品的运维成本。接下来,我们仍然会持续改进整个系统,其中关注的重点有:
解决各个平台组件之间的兼容问题。我们希望让自动化部署系统能够根据设置的规则自动检测各个组件内的兼容问题并选择合适版本自动化部署,这样,各个组件可以按照自动的节奏(和版本号)独立发布(而不是像现在所有的组件必须以一个版本一块部署)。
开发整个自动化部署系统的控制台界面(Dashboard)。该界面可以让我们自己和产品部门看到系统的目前部署状态及部署历史,并且可以让一键部署在该控制台触发,从而让平台内部的构建系统彻底隐藏在背后。
尝试和 AWS OpsWorks 的结合。OpsWorks 的一个优势就是集成了大量 DevOps 实践精髓并提供了丰富的内建部署脚本,可以降低自动化部署系统自身的开发和维护成本。尽管如前所述,目前还没有办法向该系统迁移,但我们仍然会密切关注 OpsWorks 自身的发展,并会在平衡收益与成本的前提下积极尝试和 AWS OpsWorks 的结合。
关于作者
徐桂林现为Autodesk中国研发中心的高级开发工程师。2007 年加入 Autodesk 中国研发中心,先后在 Autodesk Labs、CTO Office 工作,现在 Autodesk 云平台事业部(Cloud Platform)工作。在 2007 年开始参与多个 SaaS 相关的开发,主要做前端开发。从 2009 年年底转向做后端,开始使用 AWS 服务并领导中国团队的开发工作。毕业于浙江大学计算机系 CAD&CG 国家重点实验室,关注云计算、前端开发等相关技术。