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