Amazon EC2 容器服务(ECS)管窥

By Werner Vogels on 20 July 2015 06:00 AM

译者注 : Werner Vogels 是亚马逊的 CTO

在我上一篇关于 Amazon EC2 Container Service (Amazon ECS) 的帖子里,我讨论了在集群环境中搭建现代分布式应用的两大要素:可靠的集群状态管理和灵活的任务调度。因为有了 Amazon ECS,搭建和运维一个容器化的应用变地非常容易,也勾起了大家对于 ECS 实现原理的兴趣。这里我给大家讲解一下 ECS 的架构原理,以及这种架构带来的新特性。下图描述了 Amazon ECS 的内部结构:

Amazon ECS 的内部结构

我们是如何调度集群的

长话短说,Amazon ECS 的核心是集群管理模块,这个后台服务负责集群的调度和状态管理。集群管理模块为调度器(scheduler)提供了运行环境,这样用户可以开发自己的调度器,让集群管理和容器调度功能实现了解耦。这里提到的“集群”可以认为是底层的计算资源池,用来承载用户自己开发的应用。这个“计算资源池”包括了 AWS EC2 虚拟机的 CPU、内存和网络资源,通过容器对这些资源进行隔离。Amazon ECS 通过部署在每台 EC2 虚拟机中的 Amazon ECS Container Agent 来管理集群。ECS 可以遥控集群中每个 EC2 虚拟机中的 Agent,这样用户或者调度器就可以远程在 EC2 虚拟机里面启动、停止和监控容器的实例。这个 Agent 是用 GO 语言编写,在 Github 上遵循 Apache 协议开源。欢迎大家来提出意见并且贡献代码。

状态管理

集群调度需要集中维护全局状态,包括:集群中的 EC2 虚拟机、EC2 虚拟机上运行的任务、创建任务的容器和虚拟机可用的资源(例如端口、内存、CPU等)。如果不了解集群的当前状况,就不可能顺利地启动和停止任何容器任务。所有的现代分布式系统都会有这么一个核心组件,用来集中保存这些状态,一般是基于 Key/Value 数据库。

这个 Key/Value 数据库是唯一保存集群全局状态的组件,所有状态信息(状态和状态的转换事件)都会汇聚并且保存在这里。因为这个组件自身必须足够强壮而且可扩展,所以采用了分布式架构,避免硬件或者网络问题造成单点故障。当然分布式的 Key/Value 数据库也造成一些技术挑战:首先是数据的全局一致性和读写并发时的一致性问题,特别是集群的状态(例如容器停止和启动)随时都在变化。因此我们特别设计了一套维护一致性的机制,避免多个状态修改并发时造成冲突。例如当两个用户同时请求同一台 EC2 虚拟机上的全部剩余资源时,只有一个用户能够获得想要的资源,另外一位则被告知请求失败。

为了解决并发冲突问题,Amazon ECS 使用了 Amazon 分布式系统标准的解决方案:基于 Paxos 的事物日志系统会存储对数据库中状态记录的每一次修改。每次修改都会被作为一条事物日志加入日志系统,并且按顺序进行编号。因此数据库中某个记录当前的值,等于日志系统中相应的修改日志聚合之后的结果。当读取某条记录的时候,实际上返回的是日志系统内当前有关内容的快照。写入成功则意味这此次写入的修改日志是上次读取之后最新的一条。在集群信息频繁更新的环境下(例如 Amazon ECS 这种用集群搭建共享的资源池),这种乐观锁(optimistic concurrency)的机制让 Amazon ECS 能够有效地维护集群信息。有余避免了采用悲观锁,这种分布式架构让 Amazon ECS 实现了高可用、低延迟和高并发性能。

对外编程 API

基于上文介绍的 key/value 数据库,我们能可靠地维护集群的状态信息,还可以调度整个集群并且管理任意数量的容器。正如前面提到的,我们将容器的调度和集群的管理两个服务进行了解耦,这样客户就可以利用分布式状态信息管理功能来开发自己的调度逻辑。Amazon ECS 集群管理模块提供了一组 API,让用户能够访问数据库中所有的集群状态信息,并且返回经过结构化处理的数据。

例如“list”命令,用户能够获得集群的列表、每个集群中的 EC2 实例列表、运行中的任务列表和容器的配置信息(例如任务定义)。使用“describe”命令,用户可以获取指定 EC2 虚拟机的参数以及资源使用情况。最后,用户可以启动和停止任何机器上他的任务。最近我们在 Amazon ECS 上跑了一系列的测试,下图描述了用户可以期待的部分性能指标:

性能指标

上图描绘了一次压力测试的结果,过程是从 Amazon ECS 集群中添加和移除实例。在72小时内,我们测试了 “DescribeTask” 接口的 50% 和 99% 延迟时间。即使实例数量变化剧烈,接口延迟时间没有明显的波动。 结果说明即使用户集群规模很大,而且不增加 ECS 管理节点性能的情况下,Amazon ECS 也能轻松地进行扩展集群。

用户基于这些 API 就可以在 Amazon ECS 上建设自己的解决方案。包括启动和停止容器的时间、地点和方式,都可以通过自行定义的调度器来实现。总之 Amazon ECS 的架构在设计之初就旨在帮助用户自己定义各种调度器(例如 bin packing、spread 等)来适应特定的业务场景。用户可以通过这个构架来读取集群的状态并且从资源池里面调度所需的资源。乐观锁的机制在避免冲突的前提下让大家都能够获得想要的资源。我们聪明的用户已经借助这个架构创造了不少有趣的方案,下面我给各位分享一下。

Hailo – 通过自定义的调度器实现弹性资源池

Hailo是一个移动app,用户可以呼叫出租车到指定的地点来接人。目前在全球拥有六万名加盟的司机,已经有百万用户使用过他们的服务。Hailo 建立于 2011年,从一开始就立足于 AWS。最开始的时候 Hailo 是传统的单体软件架构(monolithic),现在已经演进为分布在多个区域(AZ)的微服务架构。最初这些微服务都分布在手工划分的集群里,暴露出的问题是资源利用率不高,其次这种架构难以横向扩展。

因为 Halio 的工程师不想将宝贵的精力放在维护基础硬件之上,决定将这些微服务部署在一个能根据优先级和实际运行状态动态调度的资源池上。通过 Amazon ECS 的集群管理 API 能够完全了解集群全部的状态而且确保任务正确运行,所以 Hailo 决定使用 ECS 来承载他们的微服务。借助这些 API,Hailo 创建了自己的调度器,满足了自己的业务需求。

Remind – 私有 PaaS 实践

Remind 是一个跨平台的文字聊天应用,帮助老师来联系学生家长。目前有 2400 万家长和 150 万老师在使用 Remind。最初 Remind 的服务是建立在 Heroku 之上,包括消息发送、前端 API、网页客户端和聊天服务,基本都采用了传统的单体架构。

当用户数量爆发的时候,Remind 亟需建立起快速横向扩展的能力。2014年初,技术团队开始尝试容器技术和微服务架构,目标是在 AWS 上建立一个兼容 Heroku API 的 PaaS服务。技术团队尝试了开源的 PaaS 解决方案,例如 CoreOS 和 Kubernetes,重点是集群管理和容器调度功能。调研的结果是技术团队人力有限,难以保证自有平台稳定和高效的运行。

之后简单调研了 Amazon ECS,技术团队决定基于它来搭建 PaaS 平台。Amazon ECS 管理功能靠谱、运行有效,工程师可以聚焦在应用本身的研发上。不久之后的 6 月份,Remind 就将这个 PaaS 项目http://engineering.remind.com/introducing-empire/ 开源。Empire 平台显著地提高了系统性能,例如稳定性和延迟时间,再加上安全方面的提升。基础上线的良好反馈,Remind 会将 90% 自有服务迁移到这个平台上。

Amazon ECS – 完全可控的平台

上面只是部分用户案例,证明 Amazon ECS 为用户提供了高可用、易扩展和低延迟的容器管理服务。因为通过 API 可以获取完整的集群状态信息,用户可以自如地实现自己的容器服务。我们聚焦于为用户移除达到这个目标路上的障碍。有了 Amazon ECS,用户不再需要自己来实现和运维集群管理服务,完全可以聚焦于业务应用的开发。

自从去年 11 月份发布预览版以来,我们已经陆续推出了不少功能。您可以在 Jeff Barr 的博客上看到这一年推出的功能。我们的文档http://docs.aws.amazon.com/AmazonECS/latest/developerguide/ECS_GetStarted.html和面板自然是入门的最佳途径。当然还有很多功能已经写在我们的 roadmap 上等待实现,我们非常需要你们来论坛提供反馈!

原文链接:

http://www.allthingsdistributed.com/2015/07/under-the-hood-of-the-amazon-ec2-container-service.html

翻译:

谢乐冰,数人科技COO,毕业于柏林工业大学,多年电信运营商行业(HP)和云计算创业公司经验。