本文中,我們將探討容器的一些特性,在減輕開發(fā)/測試工作量的同時,也使得它們非常適合在AWS中構(gòu)建一個微服務(wù)構(gòu)架。對于Web應(yīng)用程序來說,微服務(wù)架構(gòu)可以讓應(yīng)用程序的代碼庫更加敏捷,并且容易管理。下面,我們將介紹這個架構(gòu)為何可以大幅提升開發(fā)者生產(chǎn)效率的原因,并了解它能夠快速迭代和擴(kuò)充一個代碼庫的原理。對于快速發(fā)展中的創(chuàng)業(yè)公司來說,微服務(wù)架構(gòu)可以讓開發(fā)團(tuán)隊在研發(fā)過程中更加的敏捷和靈活。
Web開發(fā)簡史
首先,我們快速回顧一下過去20年的Web開發(fā)歷史,這有助于解釋為什么微服務(wù)架構(gòu)變得如此流行,以及這一架構(gòu)解決的問題。
在Web應(yīng)用開發(fā)的最初階段,應(yīng)用都是使用通用網(wǎng)關(guān)接口(CGI)創(chuàng)建的。這個接口為Web服務(wù)器提供了一種在處理瀏覽器HTTP請求時執(zhí)行腳本(通常是使用Perl編寫)的方法。由于對腳本資源的每個請求都需要啟動一個Perl進(jìn)程處理,CGI架構(gòu)無法很好地擴(kuò)展。為了解決這個問題,當(dāng)時流行的Web服務(wù)器添加了模塊支持。Apache,如今最流行的Web服務(wù)器之一,添加了“mod_perl”模塊,在服務(wù)器內(nèi)部運行Perl代碼,這使得CGI腳本能更快執(zhí)行。
盡管像mod_perl這樣的技術(shù)讓傳統(tǒng)CGI獲得了巨大提升,問題依然存在。負(fù)責(zé)構(gòu)建視圖的代碼(比如解析頁面HTML的動態(tài)部分)與應(yīng)用的業(yè)務(wù)邏輯混雜在一起。這意味著要完成一個簡單的任務(wù),比如在HTML表格中添加一列或在表單中添加一個新元素,都需要修改底層應(yīng)用代碼。因此,Web編程技術(shù)的下一輪革新成果“server pages”出現(xiàn)了,它是允許在HTML里嵌入可執(zhí)行代碼的模板框架??梢愿逦貙?yīng)用邏輯與視圖邏輯分開。在Java編程世界,一種名為“Model 2”的設(shè)計模式迅速出現(xiàn),如圖1所示,它將應(yīng)用代碼放入Java servlet、數(shù)據(jù)放入叫Java Bean的類中、視圖邏輯放入JSP:
“Model 2”很快演變成了如今廣泛使用的Model-View-Controller(MVC)設(shè)計模式。很多早期的MVC框架都是以Java為基礎(chǔ)的(如Apache Struts),不過其它框架如Ruby On Rails也在快速普及。在MVC設(shè)計模式中,“controller”類定義了映射到“route”類URL的方法。controller方法使用封裝了核心應(yīng)用實體業(yè)務(wù)邏輯和數(shù)據(jù)的“model”類。最后,每個controller方法渲染一個用于顯示和編輯相應(yīng)model類數(shù)據(jù)的“view”。如圖2所示,這個模式將業(yè)務(wù)、應(yīng)用和視圖邏輯清晰地分離開了:
REST協(xié)議崛起
在MVC作為Web開發(fā)的事實選擇被快速采納的同時,進(jìn)程間通訊(IPC)演進(jìn)為使用以文本為基礎(chǔ)的系列化格式,如XML和JSON。像SOAP這樣的協(xié)議允許通過HTTP進(jìn)行IPC,很快Web開發(fā)人員不再只是創(chuàng)建為瀏覽器提供內(nèi)容的Web應(yīng)用,還有為其它程序執(zhí)行任務(wù)并交付數(shù)據(jù)的Web服務(wù)。這種基于服務(wù)的架構(gòu)擁有非常強大的功能,因為它消除了對共享代碼庫的依賴,允許應(yīng)用開發(fā)人員更進(jìn)一步解耦應(yīng)用組件。SOAP協(xié)議和相關(guān)的WS-*標(biāo)準(zhǔn)很快變得越來越復(fù)雜,并且嚴(yán)重依賴于應(yīng)用服務(wù)器的具體實現(xiàn),因此開發(fā)人員遷移到更輕量的REST協(xié)議上。隨著移動設(shè)備的廣泛使用,Web UX開發(fā)轉(zhuǎn)移到了AJAX和JavaScript框架上,應(yīng)用開發(fā)人員開始擴(kuò)展REST的功能用于在客戶端設(shè)備與Web服務(wù)器間傳輸數(shù)據(jù)。
事實上,MVC框架也非常適合開發(fā)REST端。如圖3所示,REST面向資源的特性被很好的映射成了控制器和模型理念,如圖3所示:
一體化架構(gòu)(Monolithic Architecure)
MVC應(yīng)用由模型、視圖和控制器組成,主要為應(yīng)用提供可變的HTML內(nèi)容,包括傳統(tǒng)的HTML和REST端的JSON。很多這類應(yīng)用使用了一體化架構(gòu)。應(yīng)用以一個單一文件(如Java)或放置在同一目錄下的一組文件(如Rails)的形式進(jìn)行部署。所有應(yīng)用代碼運行在同一進(jìn)程中。擴(kuò)展時要求將完全相同的應(yīng)用代碼部署到多臺服務(wù)器上。圖4是一體化架構(gòu)的表示:
一體化架構(gòu)存在一些問題。首先,在添加功能和服務(wù)到應(yīng)用中時,代碼庫會變得非常復(fù)雜。對于新的開發(fā)人員是個巨大挑戰(zhàn)。如今的IDE甚至在加載整個應(yīng)用代碼時會發(fā)生問題,而且編譯與構(gòu)建時間非常久。因為所有的應(yīng)用代碼都運行在服務(wù)器的同一個進(jìn)程中,要擴(kuò)展應(yīng)用的其中一部分(就算可能)非常困難。假如有一個服務(wù)是內(nèi)存密集型的,另一個是CPU密集型的,服務(wù)器就必須提供足夠的內(nèi)存和CPU來處理每個服務(wù)的基本負(fù)載。如果每臺服務(wù)器都需要大量CPU和內(nèi)存,花費不小,如果使用負(fù)載均衡進(jìn)行應(yīng)用的橫向擴(kuò)展,則費用將更高。最后,更微妙的是,隨著時間推移,工程團(tuán)隊架構(gòu)常常開始呈現(xiàn)為應(yīng)用架構(gòu)。UX工程師被派去構(gòu)建UI組件,中間層開發(fā)人員構(gòu)建服務(wù)端,數(shù)據(jù)庫工程師和DBA處理數(shù)據(jù)訪問組件和數(shù)據(jù)庫。如果一個UX工程師想要添加一個數(shù)據(jù)到界面上,就必須與中間層和數(shù)據(jù)庫工程師協(xié)商。像水一樣,人類傾向于選擇阻力最小的路徑,這意味著每個工程組都會在他們控制的應(yīng)用部分嵌入盡可能多的邏輯。時間一久,注定會產(chǎn)生難以維護(hù)的代碼。
[page]微服務(wù)架構(gòu)
微服務(wù)架構(gòu)設(shè)計用于解決這個問題。在一體化架構(gòu)中定義的服務(wù)被分解成一個個單獨的服務(wù),并在不同主機上分開部署。
每個微服務(wù)針對一個特定的業(yè)務(wù)功能,并且只定義該業(yè)務(wù)功能所需的操作。這聽起來像是以服務(wù)為導(dǎo)向的架構(gòu)(SOA),實際上,微服務(wù)架構(gòu)與SOA有一些共通的特性。兩個架構(gòu)都將代碼組織成服務(wù),二者都定義了清晰邊界來區(qū)隔服務(wù)。然而,SOA源于將提供API(通常以SOAP為基礎(chǔ))的一體化應(yīng)用與其他應(yīng)用整合在一起的需求。在SOA中,整合嚴(yán)重依賴于中間件,特別是企業(yè)服務(wù)總線(ESB)。微服務(wù)架構(gòu)經(jīng)常使用消息總線,不過在消息層完全沒有邏輯,它簡單的被用來在服務(wù)間傳送信息。這與ESB存在巨大差別,后者包含了大量消息路由、schema驗證、消息轉(zhuǎn)換和業(yè)務(wù)規(guī)則的邏輯。由此可見,微服務(wù)架構(gòu)相比傳統(tǒng)SOA累贅更少,且在定義服務(wù)間接口時不要求相同級別的治理和規(guī)范的數(shù)據(jù)模型。使用微服務(wù),開發(fā)速度快并且服務(wù)可根據(jù)業(yè)務(wù)需要演進(jìn)。
微服務(wù)架構(gòu)的另一個關(guān)鍵優(yōu)勢在于,服務(wù)可以根據(jù)其資源需求進(jìn)行單獨擴(kuò)展。與運行配備了大量CPU和內(nèi)存的大型服務(wù)器不同,微服務(wù)可以部署在僅包含該服務(wù)需要的資源的較小主機上。此外,每個服務(wù)可以使用最適合其操作執(zhí)行的語言來實現(xiàn)。圖像處理服務(wù)可以使用高性能語言如C++實現(xiàn)。執(zhí)行數(shù)學(xué)或統(tǒng)計操作的服務(wù)可以使用Python實現(xiàn)。執(zhí)行基本的資源CRUD操作的服務(wù)可以用Ruby實現(xiàn)。微服務(wù)架構(gòu)不要求一體化架構(gòu)的“一刀切”模型,后者通常使用單一的MVC架構(gòu)和單一的編程語言。
不過,微服務(wù)也有一些缺點。因為服務(wù)分布在多臺主機上,要了解哪臺主機運行了某些服務(wù)可能會很困難。另外,盡管每臺主機可能不像運行一體化架構(gòu)的主機那么強大,在微服務(wù)架構(gòu)橫向擴(kuò)展時,主機的數(shù)量將比一體化架構(gòu)增加得更快。在AWS環(huán)境中,即便最小的EC2實例類型,微服務(wù)可能都不需要其全部資源,結(jié)果就是超量供給及成本上升。如果服務(wù)使用不同編程語言實現(xiàn),意味著每個服務(wù)的部署都需要一個完全不同的庫與框架集合,使得部署到服務(wù)器非常復(fù)雜。
容器是解決之道
Linux容器可以減輕微服務(wù)架構(gòu)面臨的這些挑戰(zhàn)。Linux容器使用了內(nèi)核接口如別名(cname)和命名空間(namespace),允許多個容器共享同一個內(nèi)核,同時運行時又與其他容器完全隔離。Docker執(zhí)行環(huán)境使用了一個叫l(wèi)ibcontainer的模塊,用于規(guī)范這些接口。Docker還提供了一個與GitHub類似的名叫DockerHub的容器鏡像倉庫,便于共享和分發(fā)容器。
同一臺主機上運行的多個容器間的隔離性使得部署用不同語言和框架開發(fā)的微服務(wù)代碼時非常簡單。使用Docker,我們可以創(chuàng)建一個Dockerfile描述所有該服務(wù)使用的語言、框架和依賴庫。比如,以下Dockerfile可以用來定義一個微服務(wù)的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"使用該鏡像創(chuàng)建的容器可以簡單地放置到一臺運行了另一個容器的主機上,即便這個容器創(chuàng)建自一個使用了Java和DropWizard框架的Docker鏡像。容器執(zhí)行環(huán)境將運行在該主機上的容器彼此隔離開,因此容器使用的語言、庫和框架依賴不會與其它容器相沖突。
容器的可移植性讓微服務(wù)的部署變得微不足道。要對運行在指定主機上的服務(wù)進(jìn)行升級,只需要簡單地停止運行中的容器,然后啟動以最新版服務(wù)代碼的Docker鏡像創(chuàng)建的新的容器。該主機上的所有其它容器都不會受此變化影響。
容器還有助于提高主機資源利用率。如果指定的服務(wù)無法使用Amazon EC2實例的所有資源,可以啟動其它服務(wù)容器來利用這些空閑資源。當(dāng)然,如果是手工在容器中部署服務(wù)、管理哪些服務(wù)運行在哪些主機上以及監(jiān)控所有運行容器的主機的利用率,事情將迅速變得難以掌控。
最近發(fā)布的Amazon EC2容器服務(wù)(Amazon ECS)就是用于解決這個問題的。使用Amazon ECS,定義一個名叫“集群(cluster)”的計算資源池。一個集群由一個或多個Amazon EC2實例組成。Amazon ECS管理了運行在集群里的所有容器應(yīng)用的狀態(tài),提供自動監(jiān)測和日志,管理集群的利用率,完成工作的高效調(diào)度。Amazon ECS提供了一個叫“任務(wù)定義(task definition)”的組件,用于定義一組包含應(yīng)用的容器。任務(wù)定義里的每個容器指明其所需資源,Amazon ECS將根據(jù)集群里的可用資源調(diào)度任務(wù)執(zhí)行。
微服務(wù)可以簡單的定義成一個任務(wù),并可由兩個容器組成——一個運行服務(wù)端代碼,另一個是數(shù)據(jù)庫。Amazon ECS管理著容器間的依賴,以及集群里的資源均衡。Amazon ECS還提供了對彈性負(fù)載均衡(Elastic Load Balancing)、Amazon EBS、彈性網(wǎng)絡(luò)接口(Elastic Network Interface)及自動擴(kuò)展(Auto Scaling)等重要的AWS服務(wù)的無縫訪問。使用Amazon ECS,容器應(yīng)用也可以使用所有這些部署應(yīng)用到EC2的關(guān)鍵功能。
像Amazon ECS這樣的容器管理方案還簡化了“服務(wù)發(fā)現(xiàn)(service discovery)”的實現(xiàn)。因為微服務(wù)經(jīng)常要跨多主機部署,并常常需要根據(jù)負(fù)載進(jìn)行伸縮,要讓一個服務(wù)知道如何定位另一個服務(wù),服務(wù)發(fā)現(xiàn)就是必需的。最簡單的方式,就是使用負(fù)載均衡器。不過很多情況下,還是有必要使用一個真正的分布式配置服務(wù),比如Apache Zookeeper。Amazon ECS API可以整合像Zookeeper這樣的第三方工具。也能使用Amazon ECS管理Zookeeper集群??梢允褂萌蝿?wù)定義將組成Zookeeper集群的容器分在一組,并通過Amazon ECS服務(wù)調(diào)度到集群的Amazon EC2主機上執(zhí)行。
從很多方面而言,使用容器實現(xiàn)微服務(wù)架構(gòu)是一項不同于過去20年Web開發(fā)觀察到的進(jìn)化。這種進(jìn)化受到了如何更好的利用計算資源和維護(hù)日益復(fù)雜的web應(yīng)用的需求驅(qū)動。如我們所見,通過Linux容器使用微服務(wù)架構(gòu)解決了這兩項需求。我們簡要說明了如何將微服務(wù)定義成Amazon ECS的一項任務(wù),但是在分布式系統(tǒng)中使用容器遠(yuǎn)遠(yuǎn)不僅限于微服務(wù)。漸漸地,容器已經(jīng)成了所有分布式系統(tǒng)的“一等公民”,未來的文章中我們將探討類似Amazon ECS這樣的工具如何在管理以容器為基礎(chǔ)的計算資源中發(fā)揮重要作用。
原文鏈接:http://dockerone.com/article/176