專欄前面的文章,我主要給你講解了微服務架構的基礎組成以及在具體落地實踐過程中的會遇到的問題和解決方案,這些是掌握微服務架構最基礎的知識。從今天開始,我們將進一步深入微服務架構進階的內容,也就是微服務與容器、DevOps之間的關系。它們三個雖然分屬於不同領域,但卻有着千絲萬縷的關系,可以說沒有容器的普及,就沒有微服務架構的蓬勃發展,也就沒有DevOps今天的盛行其道。
之后我還會具體分析它們三者之間是如何緊密聯系的,今天我們先來看微服務為什么要容器化。
微服務帶來的問題
單體應用拆分成多個微服務后,能夠實現快速開發迭代,但隨之帶來的問題是測試和運維部署的成本的提升。相信拆分微服務的利弊你早已耳熟能詳,我講個具體的例子。微博業務早期就是一個大的單體Web應用,在測試和運維的時候,只需要把Web應用打成一個大的WAR包,部署到Tomcat中去就行了。后來拆分成多個微服務之后,有的業務需求需要同時修改多個微服務的代碼,這時候就有多個微服務都需要打包、測試和上線發布,一個業務需求就需要同時測試多個微服務接口的功能,上線發布多個系統,給測試和運維的工作量增加了很多。這個時候就需要有辦法能夠減輕測試和運維的負擔,我在上一講給出的解決方案是DevOps。
DevOps可以簡單理解為開發和運維的結合,服務的開發者不再只負責服務的代碼開發,還要負責服務的測試、上線發布甚至故障處理等全生命周期過程,這樣的話就把測試和運維從微服務拆分后所帶來的復雜工作中解放出來。DevOps要求開發、測試和發布的流程必須自動化,這就需要保證開發人員將自己本地部署測試通過的代碼和運行環境,能夠復制到測試環境中去,測試通過后再復制到線上環境進行發布。雖然這個過程看上去好像復制代碼一樣簡單,但在現實時,本地環境、測試環境以及線上環境往往是隔離的,軟件配置環境的差異也很大,這也導致了開發、測試和發布流程的割裂。
而且還有一個問題是,拆分后的微服務相比原來大的單體應用更加靈活,經常要根據實際的訪問量情況做在線擴縮容,而且通常會采用在公有雲上創建的ECS來擴縮容。這又給微服務的運維帶來另外一個挑戰,因為公有雲上創建的ECS通常只包含了基本的操作系統環境,微服務運行依賴的軟件配置等需要運維再單獨進行初始化工作,因為不同的微服務的軟件配置依賴不同,比如Java服務依賴了JDK,就需要在ECS上安裝JDK,而且可能不同的微服務依賴的JDK版本也不相同,一般情況下新的業務可能依賴的版本比較新比如JDK 8,而有些舊的業務可能依賴的版本還是JDK 6,為此服務部署的初始化工作十分繁瑣。
而容器技術的誕生恰恰解決了上面這兩個問題,為什么容器技術可以解決本地、測試、線上環境的隔離,解決部署服務初始化繁瑣的問題呢?下面我就以業界公認的容器標准Docker為例,來看看Docker是如何解決這兩個問題的。
什么是Docker
Docker是容器技術的一種,事實上已經成為業界公認的容器標准,要理解Docker的工作原理首先得知道什么是容器。
容器翻譯自英文的Container一詞,而Container又可以翻譯成集裝箱。我們都知道,集裝箱的作用就是,在港口把貨物用集裝箱封裝起來,然后經過貨輪從海上運輸到另一個港口,再在港口卸載后通過大貨車運送到目的地。這樣的話,貨物在世界的任何地方流轉時,都是在集裝箱里封裝好的,不需要根據是在貨輪上還是大貨車上而對貨物進行重新裝配。同樣,在軟件的世界里,容器也起到了相同的作用,只不過它封裝的是軟件的運行環境。容器的本質就是Linux操作系統里的進程,但與操作系統中運行的一般進程不同的是,容器通過Namespace和Cgroups這兩種機制,可以擁有自己的root文件系統、自己的網絡配置、自己的進程空間,甚至是自己的用戶ID空間,這樣的話容器里的進程就像是運行在宿主機上的另外一個單獨的操作系統內,從而實現與宿主機操作系統里運行的其他進程隔離。
Docker也是基於Linux內核的Cgroups、Namespace機制來實現進程的封裝和隔離的,那么Docker為何能把容器技術推向一個新的高度呢?這就要從Docker在容器技術上的一項創新Docker鏡像說起。雖然容器解決了應用程序運行時隔離的問題,但是要想實現應用能夠從一台機器遷移到另外一台機器上還能正常運行,就必須保證另外一台機器上的操作系統是一致的,而且應用程序依賴的各種環境也必須是一致的。Docker鏡像恰恰就解決了這個痛點,具體來講,就是Docker鏡像不光可以打包應用程序本身,而且還可以打包應用程序的所有依賴,甚至可以包含整個操作系統。這樣的話,你在你自己本機上運行通過的應用程序,就可以使用Docker鏡像把應用程序文件、所有依賴的軟件以及操作系統本身都打包成一個鏡像,可以在任何一個安裝了Docker軟件的地方運行。
Docker鏡像解決了DevOps中微服務運行的環境難以在本地環境、測試環境以及線上環境保持一致的難題。如此一來,開發就可以把在本地環境中運行測試通過的代碼,以及依賴的軟件和操作系統本身打包成一個鏡像,然后自動部署在測試環境中進行測試,測試通過后再自動發布到線上環境上去,整個開發、測試和發布的流程就打通了。
同時,無論是使用內部物理機還是公有雲的機器部署服務,都可以利用Docker鏡像把微服務運行環境封裝起來,從而屏蔽機器內部物理機和公有雲機器運行環境的差異,實現同等對待,降低了運維的復雜度。
微服務容器化實踐
Docker能幫助解決服務運行環境可遷移問題的關鍵,就在於Docker鏡像的使用上,實際在使用Docker鏡像的時候往往並不是把業務代碼、依賴的軟件環境以及操作系統本身直接都打包成一個鏡像,而是利用Docker鏡像的分層機制,在每一層通過編寫Dockerfile文件來逐層打包鏡像。這是因為雖然不同的微服務依賴的軟件環境不同,但是還是存在大大小小的相同之處,因此在打包Docker鏡像的時候,可以分層設計、逐層復用,這樣的話可以減少每一層鏡像文件的大小。
下面我就以微博的業務Docker鏡像為例,來實際講解下生產環境中如何使用Docker鏡像。正如下面這張圖所描述的那樣,微博的Docker鏡像大致分為四層。
-
基礎環境層。這一層定義操作系統運行的版本、時區、語言、yum源、TERM等。
-
運行時環境層。這一層定義了業務代碼的運行時環境,比如Java代碼的運行時環境JDK的版本。
-
Web容器層。這一層定義了業務代碼運行的容器的配置,比如Tomcat容器的JVM參數。
-
業務代碼層。這一層定義了實際的業務代碼的版本,比如是V4業務還是blossom業務。
這樣的話,每一層的鏡像都是在上一層鏡像的基礎上添加新的內容組成的,以微博V4鏡像為例,V4業務的Dockerfile文件內容如下:
FROM registry.intra.weibo.com/weibo_rd_content/tomcat_feed:jdk8.0.40_tomcat7.0.81_g1_dns
ADD confs /data1/confs/
ADD node_pool /data1/node_pool/
ADD authconfs /data1/authconfs/
ADD authkey.properties /data1/
ADD watchman.properties /data1/
ADD 200.sh /data1/weibo/bin/200.sh
ADD 503.sh /data1/weibo/bin/503.sh
ADD catalina.sh /data1/weibo/bin/catalina.sh
ADD server.xml /data1/weibo/conf/server.xml
ADD logging.properties /data1/weibo/conf/logging.properties
ADD ROOT /data1/weibo/webapps/ROOT/
RUN chmod +x /data1/weibo/bin/200.sh /data1/weibo/bin/503.sh /data1/weibo/bin/catalina.sh
WORKDIR /data1/weibo/bin
FROM代表了上一層鏡像文件是“tomcat_feed:jdk8.0.40_tomcat7.0.81_g1_dns”,從名字可以看出上一層鏡像里包含了Java運行時環境JDK和Web容器Tomcat,以及Tomcat的版本和JVM參數等;ADD就是要在這層鏡像里添加的文件, 這里主要包含了業務的代碼和配置等;RUN代表這一層鏡像啟動時需要執行的命令;WORKDIR代表了這一層鏡像啟動后的工作目錄。這樣的話就可以通過Dockerfile文件在上一層鏡像的基礎上完成這一層鏡像的制作。
總結
今天我給你講解了微服務拆分后相比於傳統的單體應用所帶來的兩個問題,一個是測試和發布工作量的提升,另一個是在彈性擴縮容時不同微服務所要求的軟件運行環境差異帶來的機器初始化復雜度的提升,而Docker利用Docker鏡像對軟件運行環境的完美封裝正好解決了這兩個問題。
正是因為Docker可以做到一處通過、到處運行,所以對業務的價值極大,解決了以前應用程序在開發環境、測試環境以及生產環境之間的移植難的問題,極大提高了運維自動化的水平,也為DevOps理念的流行和業務上雲提供了基礎。
可見容器化改造對微服務是十分必要的,但Docker也不是“銀彈”,同樣會產生新的復雜度問題,比如引入Docker后舊的針對物理機的運維模式就無法適應了,需要一種新的針對容器的運維模式。所以接下來,我將分三期,給你詳細講解微服務容器化后該如何運維。