Chris Richardson 微服務(wù)系列翻譯全7篇鏈接:
- 微服務(wù)介紹
- 構(gòu)建微服務(wù)之使用API網(wǎng)關(guān)
- 構(gòu)建微服務(wù)之微服務(wù)架構(gòu)的進(jìn)程通訊
- 微服務(wù)架構(gòu)中的服務(wù)發(fā)現(xiàn)
- 微服務(wù)之事件驅(qū)動(dòng)的數(shù)據(jù)管理
- 微服務(wù)部署(本文)
- 重構(gòu)單體應(yīng)用為微服務(wù)
原文鏈接:Choosing a Microservices Deployment Strategy
動(dòng)機(jī)
部署一個(gè)單體應(yīng)用意味著運(yùn)行著龐大應(yīng)用的多個(gè)副本,通常需要 N 臺(tái)服務(wù)器(物理機(jī)或虛擬機(jī)),在每臺(tái)服務(wù)器上運(yùn)行 M 個(gè)應(yīng)用實(shí)例。部署單體應(yīng)用一般并不特別直接,但還是比部署微服務(wù)應(yīng)用簡(jiǎn)單。
一個(gè)微服務(wù)應(yīng)用包括幾十甚至數(shù)百個(gè)服務(wù),使用不同的語(yǔ)言和框架寫成,每個(gè)服務(wù)都是一個(gè)擁有特定的部署、資源、擴(kuò)展性及監(jiān)控需求的小應(yīng)用。例如:根據(jù)服務(wù)需求運(yùn)行若干個(gè)服務(wù)實(shí)例,而且每個(gè)服務(wù)實(shí)例必須配套提供適當(dāng)?shù)?CPU、內(nèi)存 和 I/O 資源。更具挑戰(zhàn)性的是,部署服務(wù)還必須快速、可靠、高效。
單主機(jī)部署多服務(wù)實(shí)例
該模式下,需要多臺(tái)物理機(jī)或虛擬機(jī),在每個(gè)主機(jī)上部署多個(gè)服務(wù)實(shí)例。這是比較傳統(tǒng)的部署方法。每個(gè)服務(wù)實(shí)例運(yùn)行在一至多臺(tái)主機(jī)的端口上,主機(jī)通常像照看寵物一樣來(lái)管理這些服務(wù)。如下圖所示:

這一模式有幾個(gè)變型。其中之一就是每個(gè)服務(wù)對(duì)應(yīng)一個(gè)或一組進(jìn)程。例如:在 Apache Tomcat 服務(wù)器上部署 Java 服務(wù)實(shí)例作為 web 應(yīng)用,一個(gè) Node.js 服務(wù)實(shí)例可能包含一個(gè)父進(jìn)程或一至多個(gè)子進(jìn)程。
另一個(gè)變型是在一個(gè)進(jìn)程或進(jìn)程組中運(yùn)行多個(gè)服務(wù)實(shí)例。例如:在同一臺(tái) Apache Tomcat 服務(wù)器中部署多個(gè) Java web 應(yīng)用,或者在一個(gè) OSGI 容器中運(yùn)行多個(gè) OSGI 組件。
單主機(jī)多服務(wù)部署的優(yōu)點(diǎn):
1)資源利用率高,多個(gè)服務(wù)實(shí)例共享服務(wù)器及操作系統(tǒng)。如果一個(gè)進(jìn)程或進(jìn)程組運(yùn)行多個(gè)服務(wù)實(shí)例的話,效率就更高了,比如多個(gè)web應(yīng)用共享同一臺(tái) Apache Tomcat 服務(wù)器和 JVM。
2)部署服務(wù)實(shí)例快,只需將服務(wù)拷貝到主機(jī)并啟動(dòng)。如果服務(wù)是 Java 編寫的,復(fù)制 JAR包 或者 WAR 包;如果是 Node.js 或者 Ruby 等其它語(yǔ)言,拷貝源代碼即可。通過(guò)網(wǎng)絡(luò)復(fù)制這些字節(jié)數(shù)還是比較小的。
3)由于沒(méi)有太多開銷,啟動(dòng)服務(wù)通常很快。如果服務(wù)實(shí)例運(yùn)行在同一容器的進(jìn)程或進(jìn)程組,可以動(dòng)態(tài)部署到容器或使用重啟容器的方式啟動(dòng)服務(wù)。
不足在于:
1)服務(wù)實(shí)例之間沒(méi)有隔離。雖然可以準(zhǔn)確監(jiān)控每個(gè)服務(wù)實(shí)例的資源使用情況,但是并不能限制每個(gè)實(shí)例使用的資源,很有可能一個(gè)異常的服務(wù)實(shí)例會(huì)消耗掉主機(jī)的所有內(nèi)存和 CPU資源。
2)同一進(jìn)程運(yùn)行多個(gè)服務(wù)實(shí)例根本沒(méi)有隔離性,所有服務(wù)實(shí)例共享一個(gè) JVM 堆。一個(gè)異常的服務(wù)實(shí)例能夠輕易的破壞運(yùn)行在同一進(jìn)程中的其它服務(wù)實(shí)例。此外,也無(wú)法監(jiān)控每個(gè)服務(wù)資源使用的情況。
3)對(duì)運(yùn)維團(tuán)隊(duì)來(lái)講,需要了解部署服務(wù)的具體細(xì)節(jié)。服務(wù)可能用不同的語(yǔ)言和框架寫成,因而開發(fā)團(tuán)隊(duì)必須分享給運(yùn)維團(tuán)隊(duì)大量的細(xì)節(jié)。這種復(fù)雜性增加了部署中出錯(cuò)的風(fēng)險(xiǎn)。
每個(gè)主機(jī)一個(gè)服務(wù)實(shí)例
這一模式有兩種不同實(shí)現(xiàn):每臺(tái)虛擬機(jī)部署一個(gè)服務(wù)實(shí)例和每臺(tái)容器部署一個(gè)服務(wù)實(shí)例。
每臺(tái)虛擬機(jī)一個(gè)服務(wù)實(shí)例
該模式下,把每個(gè)服務(wù)打包為一個(gè)虛擬機(jī)鏡像,例如 Amazon EC2 AMI。每個(gè)服務(wù)實(shí)例(例如 EC2 實(shí)例)使用虛擬機(jī)鏡像啟動(dòng)。下圖展示了此模式的結(jié)構(gòu):

這也是 Netflix 部署視頻流媒體服務(wù)的最初方案。Netflix 使用 Aminator 把每個(gè)服務(wù)實(shí)例打包成 EC2 AMI,每個(gè)運(yùn)行的服務(wù)實(shí)例就是一個(gè) EC2 實(shí)例。
有多種工具可用來(lái)構(gòu)建虛擬機(jī)鏡像??梢耘渲贸掷m(xù)集成(CI)服務(wù)器(例如 Jenkins)來(lái)調(diào)用 Aminator,把服務(wù)打包為 EC2 AMI。Packer.io 是另一個(gè)自動(dòng)化創(chuàng)建虛擬機(jī)鏡像的工具,不同于 Aminator,它支持包括 EC2、DigitalOcean、VirtualBox 和 VMware 在內(nèi)的多種不同虛擬化技術(shù)。
Boxfuse 公司使用更加優(yōu)秀的方式來(lái)構(gòu)建虛擬機(jī)鏡像,克服了下面會(huì)講到的虛擬機(jī)鏡像的不足。Boxfuse 把 Java 應(yīng)用打包為一個(gè)迷你的虛擬機(jī)鏡像。這些鏡像能夠快速構(gòu)建、啟動(dòng),由于只暴露了有限的可能被攻擊的端口,所以也更安全。
CloudNative 使用 Bakery 這款 SaaS 工具來(lái)創(chuàng)建 EC2 AMI。用戶的微服務(wù)通過(guò)測(cè)試后,能夠配置 CI 服務(wù)器調(diào)用 Bakery,把服務(wù)打包為 AMI。使用 Bakery 這樣的 SaaS 工具意味著你不需要浪費(fèi)寶貴的時(shí)間來(lái)設(shè)置創(chuàng)建 AMI 的基礎(chǔ)設(shè)施。
每臺(tái)虛擬機(jī)一個(gè)服務(wù)實(shí)例的優(yōu)點(diǎn):
- 每個(gè)服務(wù)實(shí)例運(yùn)行互相隔離,有固定的 CPU 和內(nèi)存,不會(huì)占用別的服務(wù)的資源。
- 能夠充分利用成熟的云服務(wù)平臺(tái)。AWS 這樣的云平臺(tái)提供了負(fù)載均衡和自動(dòng)擴(kuò)展這樣實(shí)用的功能。
- 封裝了服務(wù)實(shí)現(xiàn)的技術(shù)細(xì)節(jié)。一旦服務(wù)被打包成虛擬鏡像,就變成了黑盒,虛擬機(jī)鏡像的管理 API 就成了部署該服務(wù)的 API。部署變得更簡(jiǎn)單可靠。
不足:
- 資源利用率低。每個(gè)服務(wù)實(shí)例完全占有包括操作系統(tǒng)在內(nèi)的整個(gè)虛擬機(jī)。此外,在公有 IaaS 中,固定大小的虛擬機(jī)資源沒(méi)有被充分利用。
- 公有 IaaS 通常依據(jù)虛擬機(jī)數(shù)量收費(fèi),不考慮其忙碌還是空閑。AWS 這類的 IaaS 提供了自動(dòng)擴(kuò)展,但是很難針快速響應(yīng);因而很容易過(guò)度調(diào)配虛擬機(jī),增加部署花費(fèi)。
- 部署新的服務(wù)通常很緩慢。虛擬機(jī)鏡像由于其大小的問(wèn)題,構(gòu)建過(guò)程會(huì)比較慢,而且操作系統(tǒng)啟動(dòng)也要花費(fèi)一定時(shí)間。然而,因?yàn)檫€有 Boxfuse 這樣輕量級(jí)的虛擬機(jī)存在,這一問(wèn)題也并非普遍。
- 用戶或組織中的其他人要負(fù)責(zé)大量無(wú)差別的沉重的工作。除非使用 Boxfuse 這樣的工具來(lái)解決構(gòu)建和管理虛擬機(jī)鏡像這些復(fù)雜的事情,否則這種必要且耗時(shí)的工作會(huì)占用你處理核心業(yè)務(wù)的時(shí)間。
每臺(tái)容器一個(gè)服務(wù)實(shí)例
使用每臺(tái)容器部署一個(gè)服務(wù)實(shí)例時(shí),每個(gè)服務(wù)實(shí)例運(yùn)行在自有容器中。容器是操作系統(tǒng)層面的虛擬化機(jī)制,一個(gè)容器由運(yùn)行在沙盒中的一個(gè)或多個(gè)進(jìn)程組成。從進(jìn)程的角度看,它們有著自己的端口命名空間和根文件系統(tǒng)。用戶能夠限制容器的內(nèi)存和 CPU 資源,有些容器還能限制 I/O 速率。容器技術(shù)的代表包括 Docker 和 Solaris Zone。下圖展示了這種模式的架構(gòu):

使用這種模式時(shí),用戶將服務(wù)打包為容器鏡像。一個(gè)容器鏡像就是運(yùn)行服務(wù)所需的應(yīng)用和庫(kù)組成的文件系統(tǒng)鏡像。一些容器鏡像還包括完整的 Linux 根文件系統(tǒng)。以部署 Java 服務(wù)為例,構(gòu)建的容器鏡像包括 Java 運(yùn)行時(shí)或者Apache Tomcat 服務(wù)器以及編譯好的 Java 應(yīng)用。
一旦將服務(wù)打包為容器鏡像,就可以啟動(dòng)一到多個(gè)容器了。通常一臺(tái)物理機(jī)或虛擬主機(jī)上會(huì)運(yùn)行多個(gè)容器,可以使用 Kubernetes 或 Marathon 這樣的集群管理工具來(lái)管理容器。集群管理工具把主機(jī)看做資源池,根據(jù)每個(gè)容器需要的資源和每個(gè)主機(jī)上可用的資源來(lái)調(diào)度容器。
每臺(tái)容器一個(gè)服務(wù)實(shí)例的優(yōu)點(diǎn)類似虛擬機(jī)具有的優(yōu)勢(shì):
- 服務(wù)實(shí)例之間完全隔離,也能容易的監(jiān)控每一臺(tái)容器的資源消耗。
- 與虛擬機(jī)類似,容器能夠封裝實(shí)現(xiàn)服務(wù)的技術(shù)細(xì)節(jié)。容器管理 API 也可用作管理服務(wù)的 API。
- 不同于虛擬機(jī),容器技術(shù)更為輕量,容器鏡像構(gòu)建速度也更快。比如在筆記本電腦上,只用短短五秒就能把 Spring Boot 應(yīng)用打包為 Docker 鏡像。由于沒(méi)有冗長(zhǎng)的操作系統(tǒng)啟動(dòng)過(guò)程,容器啟動(dòng)也非常迅速。容器啟動(dòng),服務(wù)就會(huì)運(yùn)行。
不足:
- 雖然容器技術(shù)正迅速走向成熟,然而相對(duì)虛擬機(jī)架構(gòu)來(lái)說(shuō)還略顯青澀。由于容器之間共享同一主機(jī)的操作系統(tǒng)內(nèi)核,因而也沒(méi)有虛擬機(jī)那么安全。
- 管理容器鏡像也是一項(xiàng)繁重的工作。除非使用 Google Container Engine 或 Amazon EC2 這些容器解決方案,否則需要同時(shí)管理容器基礎(chǔ)設(shè)施和虛擬機(jī)基礎(chǔ)設(shè)施。
- 容器通常部署在按每臺(tái)虛擬機(jī)定價(jià)的基礎(chǔ)設(shè)施上,為了處理負(fù)載高峰,可能會(huì)過(guò)度配置虛擬機(jī),從而增加額外的成本。
有趣的是,容器和虛擬機(jī)之間的區(qū)別變的模糊起來(lái)。如前文所述,Boxfuse 能夠快速構(gòu)建和啟動(dòng)虛擬機(jī),Clear Container 項(xiàng)目則致力于創(chuàng)建輕量級(jí)的虛擬機(jī)鏡像,unikernel 技術(shù)也引起了大家的注意。Docker 近期(注:2016 年 1 月 21 日)收購(gòu)了 Unikernel Systems。
Serverless部署
AWS Lambda 就是 serverless 部署技術(shù)的范例。它支持 Java、Node.js 和 Python 服務(wù)。為了部署一個(gè)微服務(wù),你需要把服務(wù)打包為 ZIP 文件并上傳到 AWS Lambda,還要提供元數(shù)據(jù),指定處理請(qǐng)求的函數(shù)名稱。AWS Lambda 自動(dòng)為微服務(wù)運(yùn)行足夠的實(shí)例來(lái)處理請(qǐng)求??梢院?jiǎn)單根據(jù)每個(gè)請(qǐng)求花費(fèi)的時(shí)間和消耗的內(nèi)存來(lái)計(jì)費(fèi)。開發(fā)人員無(wú)需擔(dān)心服務(wù)器、虛擬機(jī)或容器的各個(gè)方面。
Lambda 函數(shù)是一個(gè)無(wú)狀態(tài)的服務(wù),通過(guò)調(diào)用 AWS 服務(wù)處理請(qǐng)求。例如,一個(gè) Lambda 函數(shù)在一張圖片被上傳到 S3 時(shí)候調(diào)用,他能在 DynamoDB 表中插入一條記錄,并向 Kinesis stream 發(fā)送一條消息來(lái)觸發(fā)圖片的處理。一個(gè) Lambda 函數(shù)也可以調(diào)用第三方 web 服務(wù)。
有以下四種方法來(lái)調(diào)用 Lambda 函數(shù):
- 直接調(diào)用,直接使用 web 服務(wù)請(qǐng)求
- 自動(dòng)調(diào)用,自動(dòng)響應(yīng)由 S3、DynamoDB、Knesis、或 Simple Email Service 等 AWS 服務(wù)生成的事件
- 自動(dòng)調(diào)用,自動(dòng)通過(guò) AWS API 網(wǎng)關(guān)處理來(lái)自應(yīng)用客戶端的 HTTP 請(qǐng)求
- 定期調(diào)用,通過(guò)類似 Cron 的定時(shí)任務(wù)實(shí)現(xiàn)
可以看出,AWS Lambda 是部署微服務(wù)的一個(gè)便捷的方式?;谡?qǐng)求的定價(jià)方式意味著用戶只需要為服務(wù)實(shí)際運(yùn)行的業(yè)務(wù)付費(fèi)。此外,用戶無(wú)需考慮 IT 基礎(chǔ)設(shè)施的問(wèn)題,從而能夠?qū)W⒂趹?yīng)用的開發(fā)。
然而,AWS Lambda 也有一些局限性。它并不適合被用來(lái)部署長(zhǎng)期運(yùn)行的服務(wù),比如消費(fèi)來(lái)自第三方消息的服務(wù)。請(qǐng)求需要在 300 秒內(nèi)完成,由于 AWS Lambda 理論上能夠針對(duì)每個(gè)請(qǐng)求運(yùn)行單獨(dú)的實(shí)例,因此服務(wù)必須保持無(wú)狀態(tài)。此外,它還必須用一種支持的編程語(yǔ)言來(lái)編寫。服務(wù)也需要快速啟動(dòng),否則將會(huì)超時(shí)或停止。
總結(jié)
部署一個(gè)微服務(wù)應(yīng)用充滿挑戰(zhàn)。應(yīng)用由幾十個(gè)甚至上百個(gè)用不同的語(yǔ)言和框架實(shí)現(xiàn)的服務(wù)所組成,每個(gè)服務(wù)都是一個(gè)擁有獨(dú)立部署、資源、擴(kuò)展和監(jiān)控需求的微應(yīng)用。微服務(wù)部署的模式有多種,包括單虛擬機(jī)單服務(wù)實(shí)例和單容器單服務(wù)實(shí)例。另一個(gè)有趣的微服務(wù)部署方法則是 AWS Lambda,一個(gè) serverless 的方式。