微服務(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)該是——好基友 :-)
微服務(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)
- 微服務(wù)增加了運(yùn)維單元數(shù)量璃吧,對(duì)自動(dòng)化運(yùn)維比較依賴
- 單體架構(gòu)可以手工運(yùn)維,或者配合簡單的自動(dòng)化發(fā)布工具
其實(shí)废境,微服務(wù)架構(gòu)還有符合單一職責(zé)原則和便于接口依賴等好處肚逸,不過這些和Docker沒什么關(guān)系爷辙,是單獨(dú)在設(shè)計(jì)環(huán)節(jié)有價(jià)值的優(yōu)點(diǎn),所以這里就不提了朦促。
簡單來說膝晾,微服務(wù)區(qū)別于單體架構(gòu)的地方就在于“分而治之”,即通過切分服務(wù)以明確模塊或者功能邊界务冕。然而血当,僅有“分”是不行的,軟件系統(tǒng)是一個(gè)整體禀忆,很多功能來自若干服務(wù)模塊的配合臊旭,因此必然要有“合”的手段,這對(duì)矛盾會(huì)體現(xiàn)在多個(gè)方面箩退,我們分別說明离熏。
應(yīng)用開發(fā)
之前已經(jīng)討論過語言技術(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ā)展(或者收購)新的部門和組織以后路鹰,要么原有的應(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)化為可管理、可測試德挣、易遷移的鏡像/容器恭垦,因此為不同技術(shù)棧提供了整合管理的途徑。
在這種情況下格嗅,開發(fā)人員可以自由選擇或者保持自己的常用工具番挺,不必因?yàn)槲⒎?wù)的分裂產(chǎn)生過高的學(xué)習(xí)成本。
組織結(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ì)購買
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ā)測試用的只讀賬號(hào)匾竿,要么進(jìn)行mock瓦宜,不影響系統(tǒng)的整體性即可。
- 對(duì)于大多數(shù)外部服務(wù)搂橙,我們需要考慮建立自動(dòng)化系統(tǒng)構(gòu)建和測試的方法歉提,這是微服務(wù)架構(gòu)帶來的研發(fā)挑戰(zhàn)。
顯然区转,方法一是繞道而行苔巨,適合少數(shù)場景,方法二是正面強(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)和測試機(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í)間往往很長姐浮,更由于多系統(tǒng)的構(gòu)建過程往往互不相同谷遂,服務(wù)之間的依賴往往又不太簡單葬馋,所以自動(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è)公司可以做的事情。
系統(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 List<User> getUser(String id)</s>
+ public List<User> getUser(Long id)
對(duì)于單體應(yīng)用來說堆巧,開發(fā)過程就是使用IDE的refactory
功能變更一下妄荔,上線也很簡單,更換一下war文件即可谍肤,而對(duì)于微服務(wù)來說就復(fù)雜了啦租。
微服務(wù)的開發(fā)過程可能是這樣的:
- 變更接口,發(fā)布新的接口jar包荒揣。
- 找到所有使用
getUser(String id)
這個(gè)接口的調(diào)用方應(yīng)用篷角。 - 升級(jí)調(diào)用方的
pom.xml
文件,修改相應(yīng)代碼系任,提交恳蹲、測試虐块,但不上線。 - 同時(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ì)覺得“還不如一塊搞掉更簡單些”瓦阐。
而如果使用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í)”)戳杀。
運(yùn)維相關(guān)
應(yīng)用的依賴
運(yùn)維環(huán)節(jié)的情形和研發(fā)環(huán)節(jié)有同有異该面。
相同之處是,運(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è)依賴信息是為了測試守伸,但它可以被平臺(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ù)的趨勢,需要有什么樣的變化呢庭砍?
為了說明問題场晶,我寫了一篇文章討論軟件打包,文章結(jié)尾提到了Docker對(duì)軟件打包的價(jià)值怠缸,這里結(jié)合微服務(wù)話題簡單總結(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)化測試和持續(xù)交付,甚至是從單體架構(gòu)向微服務(wù)的逐步演化峻呛,Docker相關(guān)技術(shù)都可以為微服務(wù)提供有力幫助罗售。