微服务架构

以前的博文中,我讲解了Linux容器技术的相关实现,好比如何使用Docker来创建流线型的开发和测试体验。由于能够实现跨不一样类型基础设施的兼容(好比,在AWS上,容器也能够如实体服务器上同样轻松的运行),容器让代码的部署异常便捷。在实际工做中,测试和开发环境的细微不一样极可能会致使应用程序的部署失败;所以在这种状况下,对于开发和测试工做,容器技术可让开发者豁免不少预想以外的工做和相互推脱。git

在本篇文章中,咱们将讨论是什么特性让容器技术如此适应开发和测试工做,一样适用于在AWS平台上构建一个基于微服务的架构。对于Web应用程序来讲,微服务架构可让应用程序的代码库更加敏捷,而且容易管理。下面,咱们将介绍这个架构为什么能够大幅提高开发者生产效率的缘由,并了解它可以快速迭代和扩充一个代码库的原理。对于快速发展中的创业公司来讲,微服务架构可让开发团队在研发过程当中更加的敏捷和灵活。数据库

Web开发简史

首先,咱们先简洁地回顾下20年内基于Web开发的历史,它可让咱们知悉微服务架构为何能够在Web开发领域如此的盛行,同时也顺便了解这个架构能够解决的问题。编程

在Web应用程序开发的早期,应用程序一般使用Common Gateway Interface(CGI)创建,这个接口为网络服务器提供了处理浏览器发来的HTTP请求时执行脚本(一般状况下用Perl编写)的能力。CGI的扩展性很是很好,由于它须要为每一个输入请求都创建一个Perl进程。为了解决这个问题,那个时代的网络服务器一般都会添加模块化的支持。Apache,现下最为流行的网络服务器之一,增长了“mod_perl”让Perl代码能够在内部运行,这样一来,CGI脚本就能够在更少的时间内执行。ubuntu

即便对比传统的CGI相似mod_perl这些技术有了很大的提高,但仍然存在问题。也就是说,负责视图层(好比,在HTML页面上执行一个动态模块)的代码一般会被混入应用程序逻辑代码中。这就意味着,完成一个简单的任务,好比在HTML列表中增长一列,或者在form中增长一个元素,一般须要修改一个低等级的应用程序代码。所以,Web程序开发技术下一个阶段中衍生了“server pages”,它容许在HTML嵌入执行代码。这样一来,应用程序逻辑代码与视图代码被很好的分离。在Java开发领域,一个被称为“Model 2”的设计模式得以快速演变,在这里,应用程序代码放到Java servlets中,数据则经过Java Beans进行,视图层逻辑则使用了Java server pages,详见下图:设计模式

图1:Model 2设计模型浏览器

随后,在Java领域,“Model 2”模式在很短的时间就演化成了“Model-View-Controller(MVC)”框架,好比Apache Struts。而在其余领域,Ruby on Rails则很是盛行。在MVC模式中,控制器类会定义方法,经过“route”类映射成URL模式被调用。 控制器方法会利用“model”类封核心应用程序实体的业务逻辑和数据。最后,每一个控制方法都会渲染一个“view”用于显示,并修改相应模式类的方法。在这种模式下,业务、应用程序、视图逻辑被很好的分离,如图2:bash

图2:MVC设计模型服务器

REST协议的盛行

就在MVC被普遍接受并成为网络开发途径的同时,进程间通讯(IPC)也开始利用上了基于文本的序列化格式,好比XML和JSON。而在相似SOAP这些协议实现跨HTTP IPC的不久后,网络开发已再也不限制于给浏览器创建交付内容的应用程序,为其余程序执行操做和交付数据的网络服务也逐渐走上历史的舞台。这种基于服务的架构拥有很是强大的功能,由于它消除了代码库共享的依赖性,从而开发者能够更进一步的解耦应用程序组件。而SOAP协议和相关的WS-*标准也变得愈来愈复杂,并更加依赖于应用程序服务器的实现,至此开发者开始投身更为轻量级的REST协议。同时,随着移动设备的剧增,Web UX development逐渐走向AJAX和JavaScript框架,应用程序开发者开始普遍使用REST在客户端设备和网络服务器之间作数据传输。网络

后来人们发现,MVC框架一样很是适合开发REST端点。REST面向资源的特性被很好的映射成了控制器和模型理念,如图3所示:架构

图3:MVC的REST端点

Monolithic架构

所以,曾今由模型、视图层、控制器组成,主要用于给应用程序交付HTML内容的MVC应用程序发生了本质上的变化——它们不只能够支撑传统的HTML,也能够经过REST端点来支撑JSON。应用程序被部署为一个单一的文件(好比Java)或者同一个目录下的文件合集(好比Rails)。然而不容忽视的是,全部应用程序代码都运行在相同的进程中。所以在缩放过程当中,开发者须要将应用程序代码的多个副本部署到多个所需的服务器上。下图解析了Monolithic架构:

图片4:Monolithic架构

在Monolithic架构中存在着不少的问题。首先,随着应用程序的功能和服务愈来愈多,代码将变得愈来愈复杂。对于新的开发者来讲,这一点很是头疼。新型集成开发环境在加载、编译整个应用程序代码时也可能存在问题,同时这个过程的耗时也可能很是长。此外,由于全部程序代码都运行在服务器上的相同进程中,致使应用程序某个组成的扩展也很是难。若是某个服务是内存密集型的,而另外一个是CPU密集型的,那么服务器必须有足够的内存和CPU来知足每一个服务的需求。所以,鉴于每一个服务器都使用很是高的CPU和内存,基础设施的总体花费可能会很是高,特别是在纵向扩展策略下。最后很是微妙的是,应用程序的组成一般也会映射到研发团队的结构上。UX工程师负责UI组件的创建,中间层开发者一般负责创建服务器端点,而数据库工程师和DBA们则负责数据访问组件和数据库。若是某个UX工程师指望给增长一些显示,他每每须要来自中间层和数据库工程师的配合。就像水同样,人们一般指望以最少的阻力执行,每一个工程师也都指望为其负责的应用程序嵌入尽量多的逻辑。鉴于这些问题,随着时间的推移,代码将愈来愈难以管理。

微服务架构

微服务架构的发明就是用来解决这些问题。定义在Monolithic架构应用程序中的服务将拆分红独立的服务,它们在不一样的主机上进行独立的部署。

图片5:微服务架构

每一个微服务都对应了一个独立的业务功能,也只定义了该功必须的一些操做。这听起来比较相似面向服务架构(SOA),事实上,微服务架构和面向服务的架构确实有不少共同的特性。两个架构都使用服务的模式组织代码,两种架构在不一样的服务间都创建了很是明确的边界。然而,面向服务的架构起源于Monolithic应用程序交互的需求,一般彼此都会提供一个API(基于SOAP)。在面向服务架构中,集成重度依赖于中间件,特别在企业服务总线(EBS)中。微服务架构一般会利用一个消息总线,可是不管任何状况在消息层都不会存在逻辑——它纯粹的被用于服务之间的交互。这与ESB有着很是显著的差异,ESB包含了大量逻辑——用于消息路由、模式验证、消息翻译和业务规则。所以,对比传统的面向服务架构,微服务架构每每更为简单,不会包含用于定义服务间接口的同级别控制和规范化数据建模。经过使用微服务,开发将很是快速,服务的衍变也只需匹配业务的需求。

微服务架构的另外一个核心优点就是服务能够基于资源的需求进行独立扩展。取代运行包含大量CPU和内存的大服务器,微服务能够被部署在更小的主机上,这些主机只须要知足其部署服务的需求。同时,开发者能够根据业务的需求选择开发语言,好比:一个图像处理服务可使用相似C++这样的高性能语言实现,一个执行数学或者静态操做的服务可使用Python实现,对资源进行增删查改的基础操做则每每经过Ruby进行。在微服务中,开发者并不须要考虑Monolithic架构中使用的“一刀切”模型——好比只使用MVC框架和单一的编程语言。

然而,不容忽视的是,微服务一样存在一些劣势。由于服务一般部署在多个主机上,很难持续跟踪指定服务究竟运行在某台主机上。同时,由于微服务架构使用的主机容量每每小于Monolithic架构,随着微服务架构不停的横向扩展,主机数量将以一个很是恐怖的速度增加。在AWS环境中,微服务架构中独立服务须要的资源每每会小于最小的EC2实例类型。从而形成了超量配置并浪费开销。此外,若是服务使用不一样的编程语言将开发,这就意味着每一个服务的部署都须要彻底不一样的库和框架,从而服务的部署很是复杂。


容器的用武之地

Linux容器技术的使用能够很大程度上缓解微服务架构所带来的问题。Linux容器技术使用了相似cnames和namespaces这样的内核接口,它容许不一样容器共享相同的内核,同时容器之间还进行了彻底的隔离。Docker执行环境使用了一个被称为libcontainer的模块,它标准化了这些接口。Docker一样为容器镜像提供了一个类GitHub的资源库DockerHub,让容器的共享和发布很是简单,也正是这种相同主机上的容器隔离简易了不一样语言开发的微服务代码部署。使用Docker,咱们能够建立一个DockerFile来描述全部用到的语言、框架和服务间库的依赖性。举个例子,下面代码中的DockerFile能够用来定义一个微服务的Docker镜像,它使用了Ruby和Sinatra框架:

FROM ubuntu:14.04
MAINTAINER John Doe <jdoe@example.com>
RUN apt-get update && apt-get install -y curl wget default-jre git
RUN adduser --home /home/sinatra --disabled-password --gecos '' 
sinatra
RUN adduser sinatra sudo
RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
USER sinatra
RUN curl -sSL https://get.rvm.io | bash -s stable
RUN /bin/bash -l -c "source /home/sinatra/.rvm/scripts/rvm"
RUN /bin/bash -l -c "rvm install 2.1.2"
RUN /bin/bash -l -c "gem install sinatra"
RUN /bin/bash -l -c "gem install thin"

使用这个镜像创建的容器能够便捷地被部署到一个主机上,这个主机同时还运行了另外一个使用Java和DropWizard 定义的Docker镜像所创建的容器。容器执行缓解隔离了主机上运行的不一样容器,所以不存在使用不一样语言、库和框架容器所形成的冲突问题。

同时值得高兴的是,近期发布的Amazon EC2 Container Service(Amazon ECS)能够帮你搞定全部这些工做。使用Amazon ECS,你能够定义一个被称为“cluster”的计算资源池,一个cluster由一个或以上的EC2实例组成。Amazon ECS负责管理集群中全部基于容器的应用程序,提供 telemetry和logging,并管理集群的容量优化,进行高效的任务调度。Amazon ECS提供了一个“任务内容(task definition)”的理念,它能够定义组成一个应用程序的一组容器。task definition中的每一个容器都指定了该容器所需的资源,而Amazon ECS将基于集群中的可用资源来调度这个任务的执行。

微服务能够很是便捷地被定义为一个任务,它能够由两个容器组成——一个负责运行服务终端代码,另外一个负责运行数据库。Amazon ECS能够管理这些容器之间的依赖性,同时也能够跨集群进行资源平衡。同时,Amazon ECS还能够无缝的访问多个AWS重点服务,好比Elastic Load Balancing、Amazon EBS、Elastic Network Interface和Auto Scaling。经过Amazon ECS,使用 Amazon EC2部署应用程序的全部基本特征都对基于容器的应用程序可用。

此外,相似Amazon ECS 这样的容器解决方案还能够简化“service discovery(服务搜寻)”这样的实现。由于微服务每每会跨多个主机部署,并根据负载进行缩放,service discovery更有利于服务之间的定位。在最简单的状况下,可使用负载均衡器来进行,可是在更为复杂的环境中,一个真正的分布式配置服务很是有必要,好比Apache Zookeeper。使用Amazon ECS API,与相似Zookeeper这样的第三方工具整合将很是容易。配置了Zookeeper的容器能够被添加到一个task definition中,并能够经过Amazon ECS在集群中的Amazon EC2调度执行。

从许多方面来看,使用容器技术实施微服务架构转变都与过去20年Web开发的衍变很是相似。许多这些衍变都是为了更好的利用计算资源,以及更方便的维护愈来愈复杂的Web应用程序。如咱们所见,使用Linux容器技术来实现微服务架构彻底匹配了这两个需求。在本文中,咱们简单地接触了使用Amazon ECS来定义一个微服务架构,可是容器在分布式系统中的使用已经远超过了微服务。在分布式系统中,愈来愈多的容器成为了first class citizens,而在将来的报告中,我将讨论为何 Amazon ECS对管理给予容器的计算是相当重要的。