一般来说,在一个集群中运行现代化分布式应用的两个关键组件是:可靠的状态管理和灵活的调度。Amazon ECS简化了构建和运行容器化应用的流程,但是如何实现才是Amazon ECS真正有意思的地方。今天,我想要探讨Amazon ECS架构并阐述这个架构能够做些什么。如下是Amazon ECS的基本组件图:
我们如何协调(Coordination)集群
让我们来谈谈Amazon ECS到底做了什么。Amazon ECS的核心是集群管理器,它是一个后台服务,能够处理集群协调和状态管理的任务。在集群管理器之上是不同的调度器。集群管理和容器调度是互相解耦的,所以Amazon支持客户使用和创建他们自己的调度器。集群其实就是应用可以使用的计算资源池。而这里的资源池就是根据容器划分的Amazon EC2实例的CPU、内存和网络资源。Amazon ECS通过运行在集群中每个EC2实例上的容器代理来协调集群。代理允许Amazon ECS与集群中的EC2实例进行通信,并根据用户或调度器的请求来启动、终止和监控容器。代理使用Go语言编写,资源占用少,目前在GitHub上基于Apache协议开源。欢迎大家贡献和反馈。
我们如何管理状态
为了协调集群,我们的集群上需要有一个SSOT[单一数据源]:集群中的EC2实例,运行在EC2实例上的任务,组成任务的容器,可用/占用资源(例如,网络端口、内存、CPU等)。在获得精确的集群状态信息之前,我们是不可能成功开启和终止容器的。为了解决这个问题,需要在某个地方存储状态,因此现代集群管理器的心脏是键值数据库。
这个键值数据库对任何集群输入的和存储于此的信息表现为SSOT。为了保证可靠性和可扩展性,这个键值数据库需要采用分布式来确保持久性和可用性,并规避网络划分和硬件故障带来的影响。也正因为键值数据库是分布式的,确保数据一致性以及正确的进行并发修改会变得更加困难,这种情况在状态持续变化的环境(比如,容器的停止和启动)中尤甚。对此,为了保证多状态修改不会出现冲突,某些形式的并发控制就需要落实到位了。打个比方,假设有2个开发者从某个EC2实例请求剩余的内存以供他们的容器使用,这个时候,只有其中一个容器能够真正得到这些资源,而另一个则会被告知请求未完成。
为了实现并发控制,我们采用了Amazon分布式系统的核心原语之一来实现Amazon ECS,这是一个基于Paxos的事务日志的数据存储系统,它保存了每一项数据变更的记录。在日志中,任何数据的写入均以事务的形式提交,并对应一个特定顺序的ID。数据当前的值就是日志中记录的那些事务的总和。对于任何数据的读取,得到的都只是日志当前时间点的一个快照。如果写操作是继上次读取操作完成以来最新提交的事务,则判定写操作成功。这种原语允许Amazon ECS以乐观锁的形式存储集群的状态信息,对于共享数据经常变动的场景(比如当需要表达诸如ECS之类的计算资源共享池的状态时)而言,这是一种理想的方式。这个架构使得Amazon ECS具有高可用性、低延迟和高吞吐量的特点,因为数据存储并未使用悲观锁(译者注:作者自己表述得很含糊,大家参见多版本并发控制MVCC)。
通过API访问
既然我们有了一个键值数据库,那么我们便能够成功协调集群,并确保所需数量的容器正在运行,因为我们有一种可靠的方法来存取集群的状态。之前提到过,我们解耦了集群管理和容器调度两个模块,因为我们希望客户能够充分利用Amazon ECS状态管理的能力。我们已经通过一系列API开放了Amazon ECS集群管理器,它允许客户以结构化的方式访问存储在键值数据库中的集群状态信息。
通过list命令,客户可以读取托管的集群,特定集群中运行的EC2实例,运行中的任务以及组成任务的容器配置(如任务定义)。通过describe命令,客户可以获取EC2实例的具体信息以及每个实例上的可用资源。最近,客户亦可以启动和停止任何集群中的任务了。近期,我们针对Amazon ECS进行了一系列的负载测试,我们希望分享一些性能要点,客户在Amazon ECS上创建应用的时候应该会关注它们的。