【深度】Docker是微服務(wù)的天生好基友?
作者介紹
李建業(yè),前阿里巴巴員工(花名:李福),2002年本科畢業(yè),之后一直從事軟件開發(fā),涉及辦公自動(dòng)化、電信網(wǎng)管/增值業(yè)務(wù)系統(tǒng)以及互聯(lián)網(wǎng);2009年12月加入淘寶的廣告應(yīng)用開發(fā)團(tuán)隊(duì);從2011年底開始,關(guān)注軟件研發(fā)本身,主要工作包括運(yùn)維自動(dòng)化系統(tǒng)和持續(xù)集成服務(wù)平臺(tái)。
前言
微服務(wù)這個(gè)話題也算有段時(shí)間了,而且并不神秘,很多公司都在走這條路。
有條推是這么解釋微服務(wù)的,很有趣,引用一下:
@arungupta Microservices = SOA -ESB -SOAP -Centralized governance/persistence -Vendors +REST/HTTP +CI/CD +DevOps +True Polyglot +Containers +PaaS
從微服務(wù)的角度看,Docker意味著什么?這兩種技術(shù)之間應(yīng)該是什么關(guān)系呢?
我認(rèn)為,Docker和微服務(wù)的關(guān)系應(yīng)該是——好基友 :-)。為什么這么說?且聽下文分解。以下為本文的目錄結(jié)構(gòu),請(qǐng)享用:
-
微服務(wù)和單體架構(gòu)
-
應(yīng)用開發(fā)
-
組織結(jié)構(gòu)
-
系統(tǒng)變更碎片化
-
運(yùn)維相關(guān)設(shè)施
好吧!我們正式開始。
1. 微服務(wù)和單體架構(gòu)
微服務(wù)的要點(diǎn)是“微”,與之對(duì)立的另一方是所謂的單體架構(gòu)(monolith)。在團(tuán)隊(duì)實(shí)踐中,這兩種思路在不同的方面體現(xiàn)出了優(yōu)劣差異:
-
應(yīng)用開發(fā)
-
微服務(wù)便于支持多元技術(shù)棧
-
單體架構(gòu)有利于IDE和其它開發(fā)工具的配合支持
-
-
組織結(jié)構(gòu)
-
微服務(wù)便于團(tuán)隊(duì)分裂,促進(jìn)局部功能的業(yè)務(wù)深入
-
單體架構(gòu)有利于開發(fā)者從全局角度理解和掌控功能
-
-
系統(tǒng)演化
-
微服務(wù)能夠有助于用“碎片化”的方式推動(dòng)系統(tǒng)演化,降低變更風(fēng)險(xiǎn)
-
單體架構(gòu)便于”整體交付“,可以減少向下兼容的需要
-
-
運(yùn)維相關(guān)設(shè)施
-
微服務(wù)增加了運(yùn)維單元數(shù)量,對(duì)自動(dòng)化運(yùn)維比較依賴
-
單體架構(gòu)可以手工運(yùn)維,或者配合簡(jiǎn)單的自動(dòng)化發(fā)布工具
-
其實(shí),微服務(wù)架構(gòu)還有符合單一職責(zé)原則和便于接口依賴等好處,不過這些和Docker沒什么關(guān)系,是單獨(dú)在設(shè)計(jì)環(huán)節(jié)有價(jià)值的優(yōu)點(diǎn),所以這里就不提了。
簡(jiǎn)單來說,微服務(wù)區(qū)別于單體架構(gòu)的地方就在于“分而治之”,即通過切分服務(wù)以明確模塊或者功能邊界。
然而,僅有“分”是不行的,軟件系統(tǒng)是一個(gè)整體,很多功能來自若干服務(wù)模塊的配合,因此必然要有“合”的手段,這對(duì)矛盾會(huì)體現(xiàn)在多個(gè)方面,我們分別說明。
2. 應(yīng)用開發(fā)
之前已經(jīng)討論過語言技術(shù)棧多元化的趨勢(shì),但是,對(duì)于單體應(yīng)用來說,多元化技術(shù)棧并不是值得推薦的實(shí)踐方法,因?yàn)檫@里涉及到混合語言編程和不兼容的軟件組織方式。
實(shí)際上,現(xiàn)實(shí)中一些團(tuán)隊(duì)之所以沒有辦法擁抱多元化的技術(shù)棧,往往首先是因?yàn)樗麄兊南到y(tǒng)是一個(gè)或者幾個(gè)“單體”應(yīng)用,開發(fā)者已經(jīng)習(xí)慣了原有的IDE和相關(guān)開發(fā)工具,引入其它技術(shù)帶來的好處還不如制造的麻煩多。
微服務(wù)則可以很好地避免這種情況,它通過切分系統(tǒng)的方式為不同功能模塊劃定了清晰的邊界,邊界之間的通信方式很容易可以做到獨(dú)立于某種技術(shù)棧,因此也就為納入其它技術(shù)帶來了空間。
現(xiàn)實(shí)中也可以看到這樣的例子,一些公司在初期會(huì)固定一個(gè)技術(shù)選型(比如facebook用php,google用python,阿里巴巴用java),而發(fā)展(或者收購(gòu))新的部門和組織以后,要么原有的應(yīng)用被分裂,要么新的業(yè)務(wù)催生出新的獨(dú)立應(yīng)用,這時(shí)往往逐漸開始擴(kuò)展技術(shù)選擇。
有拆就有合,不同技術(shù)棧的微服務(wù)之間,除了需要考慮通信機(jī)制,還要確保這些技術(shù)能夠(以較低成本)變成一個(gè)系統(tǒng)——不同服務(wù)可以使用不同的語言框架,但是在線上應(yīng)當(dāng)成為一個(gè)整體。
于是我們會(huì)在發(fā)布中遇到“合”的困難,而這正是docker能解決的問題,具體討論之前已經(jīng)展開過,這里強(qiáng)調(diào)一下結(jié)論——docker將所有應(yīng)用都標(biāo)準(zhǔn)化為可管理、可測(cè)試、易遷移的鏡像/容器,因此為不同技術(shù)棧提供了整合管理的途徑。
在這種情況下,開發(fā)人員可以自由選擇或者保持自己的常用工具,不必因?yàn)槲⒎?wù)的分裂產(chǎn)生過高的學(xué)習(xí)成本。
#p#
3. 組織結(jié)構(gòu)
說到團(tuán)隊(duì)和組織,不能繞開的一個(gè)話題就是“康威定律”(Conway’s law):
軟件系統(tǒng)的結(jié)構(gòu)受制于其生產(chǎn)者組織的溝通結(jié)構(gòu)。
從這個(gè)角度看,微服務(wù)的拆分會(huì)對(duì)團(tuán)隊(duì)擴(kuò)張帶來幫助,這不難理解,因?yàn)橄到y(tǒng)拆分為若干微服務(wù)會(huì)促進(jìn)這些微服務(wù)之間的邊界更清晰,我們知道,邊界清晰等于在邊界之間協(xié)作信息量少,如果按照微服務(wù)拆分團(tuán)隊(duì),團(tuán)隊(duì)之間的協(xié)作成本將是比較低的。
然而,“邊界之間協(xié)作信息少”是有代價(jià)的。這代價(jià)就是團(tuán)隊(duì)的每個(gè)人對(duì)系統(tǒng)失去了整體視角和掌控能力,在這一點(diǎn)上,單體架構(gòu)顯然要好很多——每個(gè)開發(fā)者的開發(fā)環(huán)境都有完整的系統(tǒng)構(gòu)建,所以很容易就可以獲得對(duì)系統(tǒng)的整體印象和理解。
這是微服務(wù)的短板,但是這個(gè)問題要分兩種情況考慮:
-
目前的技術(shù)發(fā)展使得在本機(jī)搭建一個(gè)完整的系統(tǒng)成本越來越高,即使不考慮微服務(wù)的影響,也會(huì)因?yàn)槠渌蛩囟聘哌@個(gè)代價(jià)。比如:一個(gè)普通的Web應(yīng)用也許會(huì)購(gòu)買
push.io
這樣的手機(jī)端推送服務(wù),因此期望本機(jī)能夠重現(xiàn)所有的系統(tǒng)功能有時(shí)并不現(xiàn)實(shí)。 -
微服務(wù)架構(gòu)在這方面的短板,其核心在于構(gòu)建成本,由于微服務(wù)來自不同團(tuán)隊(duì)和部門,因此如何搭建它就成為一個(gè)
謎
,同時(shí)由于不能低成本的獲得一個(gè)完整的系統(tǒng),系統(tǒng)整體的知識(shí)也就容易被開發(fā)者忽略,最終導(dǎo)致整體視角缺失。
問題不同,處理起來也是各異:
-
對(duì)于關(guān)系不大的其它服務(wù),可以保持我們常見的與外部服務(wù)進(jìn)行協(xié)作的方案——要么單獨(dú)申請(qǐng)(或分配)開發(fā)測(cè)試用的只讀賬號(hào),要么進(jìn)行mock,不影響系統(tǒng)的整體性即可。
-
對(duì)于大多數(shù)外部服務(wù),我們需要考慮建立自動(dòng)化系統(tǒng)構(gòu)建和測(cè)試的方法,這是微服務(wù)架構(gòu)帶來的研發(fā)挑戰(zhàn)。
顯然,方法一是繞道而行,適合少數(shù)場(chǎng)景,方法二是正面強(qiáng)攻,能夠應(yīng)對(duì)絕大多數(shù)情況。這個(gè)方法二就是Docker可以發(fā)力的地方。
筆者之前曾經(jīng)在公司內(nèi)部做過持續(xù)集成平臺(tái),服務(wù)的研發(fā)團(tuán)隊(duì)不少,其中有些雖然沒有提出微服務(wù)的概念,但是“將系統(tǒng)功能服務(wù)化,然后再整合”的做法其實(shí)已經(jīng)有很多運(yùn)用了,于是我們也在建立自動(dòng)化聯(lián)調(diào)和測(cè)試機(jī)制。
大致的作法是每個(gè)服務(wù)說明自己如何構(gòu)建(給出構(gòu)建腳本),并申明依賴的外部服務(wù)(運(yùn)行時(shí)依賴,不同于軟件包的依賴),然后由CI系統(tǒng)進(jìn)行全局構(gòu)建,這種做法非常好的節(jié)約了聯(lián)調(diào)時(shí)間,并使這一工作變得可重復(fù)。
遺憾的是,由于沒有Docker這樣的技術(shù),構(gòu)建這件事很難做到“整齊劃一”:
為了讓它們相互配合,需要編寫一些協(xié)作的腳本,這加重了研發(fā)團(tuán)隊(duì)的工作,因此真正能夠自動(dòng)聯(lián)調(diào)的應(yīng)用系統(tǒng)并不多。
微服務(wù)架構(gòu)中的一個(gè)系統(tǒng)往往是運(yùn)行時(shí)的多個(gè)系統(tǒng),而多系統(tǒng)聯(lián)調(diào)通常費(fèi)時(shí)費(fèi)力的,這不僅是由于編譯時(shí)間往往很長(zhǎng),更由于多系統(tǒng)的構(gòu)建過程往往互不相同,服務(wù)之間的依賴往往又不太簡(jiǎn)單,所以自動(dòng)化的成本很高。
但是,如果首先對(duì)各系統(tǒng)進(jìn)行Docker化,就很容易通過統(tǒng)一的docker build,建立一致性的構(gòu)建服務(wù),再結(jié)合compose等基礎(chǔ)設(shè)施處理服務(wù)依賴,這些工作最終就可以產(chǎn)生一個(gè)平臺(tái),(自動(dòng)化的)將被微服務(wù)打散的整個(gè)系統(tǒng)再構(gòu)建出來(由于使用了微服務(wù),構(gòu)建速度在理論上就可以是并行的,因此甚至?xí)葐误w架構(gòu)更敏捷)。
這個(gè)思路最有意思的地方在于:
建立這樣的基礎(chǔ)設(shè)施,是可以與具體公司的技術(shù)路線無關(guān)的,因此實(shí)際上可以構(gòu)建獨(dú)立的服務(wù)平臺(tái)為多個(gè)公司提供服務(wù)。
可能有人覺得這個(gè)平臺(tái)很像一個(gè)PaaS,確實(shí),這么發(fā)展下去有可能演化出一個(gè)獨(dú)立的PaaS平臺(tái),不過它可以做的更多,而且沒有傳統(tǒng)PaaS那樣對(duì)技術(shù)進(jìn)行限制,這是一個(gè)很有吸引力的方向,也是很多Docker創(chuàng)業(yè)公司可以做的事情。
4.系統(tǒng)變更碎片化
理論上,由于進(jìn)行了分解,微服務(wù)架構(gòu)的系統(tǒng)應(yīng)該更加有利于系統(tǒng)的“改良”,不必動(dòng)輒就傷筋動(dòng)骨甚至另起爐灶。
但是實(shí)際上并不一定會(huì)這樣。
微服務(wù)架構(gòu)是一種思想,它的合理運(yùn)用還是要依靠團(tuán)隊(duì)成員的,如果是從“單體”應(yīng)用演變過來的系統(tǒng),團(tuán)隊(duì)成員很容易感受到相反的體驗(yàn)——系統(tǒng)升級(jí)更復(fù)雜更難了。
比如下面這個(gè)變更(以java
應(yīng)用為例):
- public ListgetUser(String id)
+ public ListgetUser(Long id)
對(duì)于單體應(yīng)用來說,開發(fā)過程就是使用IDE的refactory
功能變更一下,上線也很簡(jiǎn)單,更換一下war文件即可,而對(duì)于微服務(wù)來說就復(fù)雜了。
微服務(wù)的開發(fā)過程可能是這樣的:
-
變更接口,發(fā)布新的接口jar包。
-
找到所有使用
getUser(String id)
這個(gè)接口的調(diào)用方應(yīng)用。 -
升級(jí)調(diào)用方的
pom.xml
文件,修改相應(yīng)代碼,提交、測(cè)試,但不上線。 -
同時(shí)修改服務(wù)提供方的代碼,對(duì)新接口進(jìn)行實(shí)現(xiàn)。
而發(fā)布過程是這樣的:
-
調(diào)用方服務(wù)停止。
-
服務(wù)方服務(wù)停止。
-
服務(wù)方服務(wù)升級(jí)軟件包。
-
服務(wù)方服務(wù)啟動(dòng)。
-
服務(wù)方驗(yàn)證服務(wù)是否正常。
-
調(diào)用方服務(wù)啟動(dòng)。
-
調(diào)用方驗(yàn)證服務(wù)是否正常。
這種操作十分脆弱,一旦發(fā)現(xiàn)服務(wù)上線失敗就會(huì)陷入兩難,有時(shí)會(huì)導(dǎo)致開發(fā)人員在線上解決問題,更進(jìn)一步引入了風(fēng)險(xiǎn)。
當(dāng)然,這個(gè)問題其實(shí)有標(biāo)準(zhǔn)解決方法——向下兼容,也就是每個(gè)服務(wù)的升級(jí)都至少兼容之前一個(gè)版本,這樣所有的依賴服務(wù)的升級(jí)就可以靈活進(jìn)行而不必將上線變成一件大事。
但是這樣做增加了向下兼容的壓力:
雖然是比較好的實(shí)踐,但并不能覆蓋所有情況,有時(shí)升級(jí)的內(nèi)容影響并不是很大,大家都會(huì)覺得“還不如一塊搞掉更簡(jiǎn)單些”。
而如果使用docker
,由于每個(gè)服務(wù)打包可以封裝為一個(gè)docker
鏡像,每個(gè)運(yùn)行時(shí)的服務(wù)都表現(xiàn)為一個(gè)獨(dú)立容器,我們之前建立的容器依賴就可以很容易的對(duì)應(yīng)到服務(wù)依賴上,基于這種統(tǒng)一性,系統(tǒng)升級(jí)就很容易配合一些自動(dòng)化工具實(shí)現(xiàn)“整體升級(jí)”(甚至還可以“整體降級(jí)”)。
#p#
5. 運(yùn)維相關(guān)設(shè)施
運(yùn)維環(huán)節(jié)的情形和研發(fā)環(huán)節(jié)有同有異。
應(yīng)用的依賴
相同之處是,運(yùn)維環(huán)節(jié)的工作和研發(fā)一樣,都會(huì)因?yàn)橐肓宋⒎?wù)而“支離破碎”,容易丟失全局視角,服務(wù)間的聯(lián)系可能會(huì)變成管理的灰色地帶,這些地方在討論組織結(jié)構(gòu)的時(shí)候已經(jīng)涉及到了。
不同之處在于,服務(wù)間關(guān)系的信息其實(shí)是來自研發(fā)環(huán)節(jié)的,如果這些信息能夠完整無誤的傳遞到運(yùn)維環(huán)節(jié),那么服務(wù)治理將會(huì)變得容易很多。所以,在對(duì)微服務(wù)進(jìn)行運(yùn)維管理時(shí),我們其實(shí)是可以“偷懶”的。
之前已經(jīng)提到,在Docker的幫助下,持續(xù)集成平臺(tái)可以建立統(tǒng)一的服務(wù),在不涉及具體技術(shù)細(xì)節(jié)的情況下為研發(fā)團(tuán)隊(duì)提供服務(wù),同時(shí)又不至于需要維護(hù)大量形式各異的腳本……其實(shí)故事并沒有結(jié)束,持續(xù)集成最終是要推進(jìn)到持續(xù)交付的,這時(shí)就會(huì)和運(yùn)維發(fā)生聯(lián)系。
在持續(xù)集成平臺(tái)上,我們要解決多服務(wù)協(xié)作的問題,辦法是讓每個(gè)服務(wù)聲明自己的依賴,然后在平臺(tái)上獲得全局圖像,當(dāng)這個(gè)平臺(tái)延伸或者對(duì)接至交付環(huán)節(jié)時(shí),之前的全局圖像信息將發(fā)揮作用,我們可以在它的指導(dǎo)下更加“智能”的進(jìn)行系統(tǒng)擴(kuò)容、自動(dòng)故障降級(jí)等一系列工作。
舉個(gè)例子,開發(fā)人員在編寫服務(wù)A時(shí)顯然會(huì)知道它所依賴的服務(wù)(假定叫B),所以可以在源碼中使用docker-compose.yml
申明,那么在服務(wù)A進(jìn)行持續(xù)集成時(shí),持續(xù)集成平臺(tái)將會(huì)找到服務(wù)B的鏡像,并創(chuàng)建相應(yīng)的容器和A連接,此時(shí)這個(gè)依賴信息是為了測(cè)試,但它可以被平臺(tái)獲得并記錄下來(這是真正有效的信息)。
當(dāng)線上進(jìn)行服務(wù)B擴(kuò)容時(shí),平臺(tái)根據(jù)之前的依賴信息,反向查詢到依賴B的其它服務(wù)(包括服務(wù)A),于是我們就可以根據(jù)預(yù)先的擴(kuò)容策略,自動(dòng)化的執(zhí)行對(duì)服務(wù)A等一些服務(wù)的
restart
/reload
操作(這里的reload
針對(duì)的是整個(gè)服務(wù),實(shí)際上基于Docker
的服務(wù)reload一般也就是依次restart
而已)
上述的例子也適用于有依賴的服務(wù)自動(dòng)升級(jí)降級(jí),做法類似。
打包構(gòu)建
對(duì)運(yùn)維而言,還有一個(gè)話題值得專門提出——打包構(gòu)建,作為基礎(chǔ)設(shè)施之一的構(gòu)建系統(tǒng),面對(duì)微服務(wù)的趨勢(shì),需要有什么樣的變化呢?
為了說明問題,我寫了一篇文章討論軟件打包,文章結(jié)尾提到了Docker對(duì)軟件打包的價(jià)值,這里結(jié)合微服務(wù)話題簡(jiǎn)單總結(jié)一下文中要點(diǎn):
軟件打包的目的是為了降低軟件交付物對(duì)外部環(huán)境的依賴,這個(gè)目的對(duì)于大規(guī)模分布式或者集群應(yīng)用特別有意義。因此這種以“自帶干糧”為特征的很多技術(shù)(比如靜態(tài)編譯的golang)會(huì)從google這樣的大規(guī)?;ヂ?lián)網(wǎng)公司中誕生,而為分布式系統(tǒng)服務(wù)的J2EE技術(shù)族中會(huì)出現(xiàn)專門為打包設(shè)計(jì)的war/ear規(guī)范。
顯然,按照微服務(wù)架構(gòu)的系統(tǒng),特點(diǎn)正是分布式越來越復(fù)雜,其中某些服務(wù)可能會(huì)擴(kuò)展成很大的集群,這和Google、facebook這樣的公司面臨的是同樣性質(zhì)的問題,所以解決方向也類似——讓構(gòu)建系統(tǒng)輸出的軟件交付物更加完備,即所謂的“自帶干糧”。
Docker
技術(shù)交付的正是這樣“自帶干糧”的鏡像文件,通過這個(gè)文件,打包系統(tǒng)交付的產(chǎn)品可以自由分發(fā)和管理,大大降低對(duì)環(huán)境的依賴。
總結(jié)
面對(duì)膨脹的未來,微服務(wù)走了一條拆解之路,但要想完整的實(shí)現(xiàn)你的業(yè)務(wù),還要能夠在某些情況下自由融合、彼此協(xié)作,Docker開啟的正是這樣一個(gè)方便之門。
無論是協(xié)同不同語言技術(shù)棧,降低運(yùn)維的成本,還是支持分布式系統(tǒng)的自動(dòng)化測(cè)試和持續(xù)交付,甚至是從單體架構(gòu)向微服務(wù)的逐步演化,Docker相關(guān)技術(shù)都可以為微服務(wù)提供有力幫助。
如何一起愉快地發(fā)展
高效運(yùn)維系列微信群是國(guó)內(nèi)高端運(yùn)維圈子、運(yùn)維行業(yè)垂直社交的典范?,F(xiàn)有會(huì)員1000余名,其中運(yùn)維總監(jiān)及以上級(jí)別會(huì)員300多名。
“高效運(yùn)維”公眾號(hào)值得您的關(guān)注,作為高效運(yùn)維系列微信群的唯一官方公眾號(hào),每周發(fā)表多篇干貨滿滿的原創(chuàng)好文:來自于系列群的討論精華、運(yùn)維講壇線上/線下活動(dòng)精彩分享及部分群友原創(chuàng)。“高效運(yùn)維”也是互聯(lián)網(wǎng)專欄《高效運(yùn)維最佳實(shí)踐》及運(yùn)維2.0官方公眾號(hào)。
提示:目前高效運(yùn)維兩個(gè)微信主群僅有少量珍貴席位,如您愿意,可添加蕭田國(guó)個(gè)人微信號(hào) xiaotianguo 為好友,進(jìn)行申請(qǐng);或申請(qǐng)加入我們技術(shù)交流群(技術(shù)討論為主,沒有主群那么多規(guī)矩,更熱鬧)。
重要提示:除非事先獲得授權(quán),請(qǐng)?jiān)诒竟娞?hào)發(fā)布2天后,才能轉(zhuǎn)載本文。尊重知識(shí),請(qǐng)必須全文轉(zhuǎn)載,并包括本行及如下二維碼。