簡介
? ? docker容器技術(shù)在17年可謂是熾手可熱鲫骗,docker不僅僅改變了傳統(tǒng)軟件服務(wù)的交付流程,更是為云計算和微服務(wù)大規(guī)模集群管理部署踩晶,提供了強(qiáng)有力的技術(shù)支撐执泰。當(dāng)今各大公司企業(yè)也是把容器化技術(shù)作為不可或缺的技術(shù)戰(zhàn)略,于17年初我們也開始步入docker技術(shù)的生態(tài)圈渡蜻,嘗試進(jìn)行對當(dāng)前服務(wù)做容器化的改造术吝,而持續(xù)交付作為項目開發(fā)流程中較為核心的一步,也是落地實(shí)踐最早一步茸苇。
我們的問題
? ? 17年初排苍,基于微服務(wù)的架構(gòu),僅我們團(tuán)隊內(nèi)的服務(wù)單元已經(jīng)過百学密,特別是經(jīng)歷網(wǎng)站的促銷活動淘衙,機(jī)器擴(kuò)容,資源分配等問題真的是苦不堪言腻暮,除此之外更是有一些困繞已久的問題等待解決:
?? 1.?發(fā)布系統(tǒng)不統(tǒng)一
? ??因?yàn)樯婕暗讲煌Z言的系統(tǒng)開發(fā)彤守,各個系統(tǒng)的構(gòu)建流程都有不同,涉及到node哭靖,C#具垫,Java還有python,發(fā)布到不同服務(wù)器上试幽,需要通過對應(yīng)的發(fā)布系統(tǒng)筝蚕,各個系統(tǒng)之間記錄關(guān)聯(lián)很難核對,不便于追蹤铺坞。
?2.?多環(huán)境的管理
? ? 服務(wù)器資源新增起宽,修改部署的配置比較多,包括打通與發(fā)布系統(tǒng)的環(huán)境相關(guān)聯(lián)济榨,基礎(chǔ)服務(wù)的安裝配燎含,包括node,jdk和tomcat服務(wù)器等腿短。
?3.?環(huán)境的一致性
? ??由于各種歷史問題屏箍,導(dǎo)致項目發(fā)布的測試環(huán)境、集成環(huán)境和產(chǎn)線環(huán)境配置不一致橘忱,給系統(tǒng)問題的排查定位帶來一定的難度赴魁,項目部署目錄,服務(wù)啟動用戶钝诚,甚至包括jdk版本的問題颖御,這些問題同時加重了運(yùn)維工作的復(fù)雜度。
?4.?資源的分配
? ??主要是軟資源的計算分配,因?yàn)榉?wù)單元非常多潘拱,每個服務(wù)單元的內(nèi)存疹鳄,端口分配問題會隨著服務(wù)數(shù)量的增加,越來越難以計算和管理芦岂,CMDB統(tǒng)計不夠?qū)崟r瘪弓,造成最終的結(jié)果就是資源利用率比較低。
微服務(wù)架構(gòu)
? ?選擇docker一個重要的原因禽最,就不得不提到微服務(wù)了腺怯,微服務(wù)相比傳統(tǒng)的服務(wù)采用單體的架構(gòu)方式,部署方式和開發(fā)模式上有很大優(yōu)勢川无。單體架構(gòu)的方式呛占,服務(wù)系統(tǒng)過于龐大臃腫,項目發(fā)布部署風(fēng)險較高懦趋,迭代開發(fā)周期較長晾虑,而微服務(wù)按照業(yè)務(wù)維度進(jìn)行功能模塊拆分,使其各自成為一個可獨(dú)立提供服務(wù)仅叫,可獨(dú)立部署運(yùn)行的單元模塊走贪,通過指定的網(wǎng)絡(luò)協(xié)議模塊之間進(jìn)行通信,并且在開發(fā)模式上惑芭,也是相互獨(dú)立坠狡,大幅提高了項目開發(fā)上線的周期,做到快速迭代遂跟。但是隨著則業(yè)務(wù)復(fù)雜性的提升逃沿,微服務(wù)的集群數(shù)量也會急速上升,帶來的問題就是面對如此多服務(wù)如何高效的管理幻锁,部署凯亮,監(jiān)控等一些問題,而docker處理這些卻有著得天獨(dú)厚的優(yōu)勢哄尔。
我們微服務(wù)架構(gòu)核心采用的框架是基于Spring Boot和Dubbo假消,并且是支持雙協(xié)議的通信,http/https和Dubbo RPC的調(diào)用方式岭接,Dubbo的主要是內(nèi)部模塊調(diào)用依賴富拗,而http的接口主要是提供給異構(gòu)系統(tǒng)的接入。整體的項目架構(gòu)圖如下:
容器化之路
如今再次說到docker的時候鸣戴,我們往往已經(jīng)不僅僅談?wù)摰闹皇莇ocker本身啃沪,因?yàn)閐ocker engine 只是改變了應(yīng)用運(yùn)行時的狀態(tài)管理,更多的我們還關(guān)注到服務(wù)的調(diào)度窄锅,編排创千,網(wǎng)絡(luò)管理,存儲,監(jiān)控追驴,配置管理等等一系列的服務(wù)械哟,docker 儼然已經(jīng)是一個龐大的技術(shù)生態(tài)圈,這個生態(tài)圈里面殿雪,大家比較關(guān)心的其中一個核心服務(wù)的就是容器編排了暇咆,因?yàn)槿萜骶幣艔氐椎母淖兞藗鹘y(tǒng)軟件服務(wù)交付流程,而容器編排則又以k8s 和mesos 為主的居多冠摄,更確切的說糯崎,k8s已經(jīng)不僅僅是作為一個編排工具了几缭,此處暫時不做討論河泳,兩者的選型介紹可后續(xù)關(guān)注,此次也不做過多討論年栓。
我們選擇的容器編排是以mesos為主的拆挥,簡單介紹一個以mesos為核心的各個組件以及服務(wù)依賴。
zk ?注冊中心某抓,這個注冊并不是服務(wù)的注冊纸兔,而是用來管理mesos集群狀態(tài)數(shù)據(jù)的中心服務(wù),通過zk來構(gòu)建一套高可用的mesos集群否副。
mesos-slave ?部署在每個node(主機(jī))節(jié)點(diǎn)的服務(wù)汉矿,用來采集服務(wù)器數(shù)據(jù)信息,執(zhí)行指令备禀。
mesos-master 管理服務(wù)器資源信息洲拇,分派mesos-slave資源調(diào)度指令。
marathon ?資源計算服務(wù)曲尸,根據(jù)應(yīng)用部署需要的資源信息赋续,提供資源分配,并且提供了一套UI另患,通過UI可方便的進(jìn)行管理操作纽乱。
各個組件服務(wù)結(jié)構(gòu)如下圖:
鏡像
? ? 我們的服務(wù),主要的包括Java和node兩種類型的服務(wù)昆箕,所以基礎(chǔ)鏡像也根據(jù)不同的版本號鸦列,提供了多種類型。鏡像基礎(chǔ)使用的是alpine的版本鹏倘,鏡像文件非常小敛熬,jdk使用的openjdk,加上啟動服務(wù)的腳本以及其他配置第股,配置內(nèi)容应民,鏡像最終大小100M左右,加上項目打包好以后的鏡像,在150M上下诲锹,當(dāng)然為了一些項目的特殊需求繁仁,也提供了oracle jdk的鏡像版本,但是至今未使用過归园。
構(gòu)建
我們的技術(shù)框架最初是基于Spring Boot黄虱,通過Spring Boot提供的打包插件,可以方便的把項目構(gòu)建成一個運(yùn)行時需要的jar包庸诱,啟動jar包和指定的運(yùn)行時的一些參數(shù)信息捻浦,是通過gitlab統(tǒng)一管理的一些shell文件,通過jenkins構(gòu)建過程桥爽,動態(tài)的把項目的jar包和啟動腳本朱灿,封裝在一起,然后發(fā)布到應(yīng)用服務(wù)器钠四,解壓啟動盗扒。但是如果采用docker部署,這種方式就不可行了缀去,因?yàn)閐ocker部署面向的是鏡像侣灶。
? ? docker鏡像的構(gòu)建方式有多種,最合理的一種莫過于Dockerfile了缕碎,但此時我們產(chǎn)線運(yùn)行的項目已經(jīng)非常多了褥影,一個個往里面添加Dockerfile已經(jīng)不現(xiàn)實(shí),所以就提供了一個Dockerfile模板咏雌,里面標(biāo)注了基礎(chǔ)鏡像信息凡怎,在項目構(gòu)建的流程中,通過獲取一些項目參數(shù)处嫌,動態(tài)生成一個Dockerfile文件栅贴,最后通過docker build生成鏡像文件,然后提交到鏡像庫熏迹,這個過程對于開發(fā)人員完全無感知檐薯,就這樣悄無聲息的完成了項目格式的轉(zhuǎn)化,并且對于不同類型的項目注暗,提供了不同類型的Dockerfile模板坛缕,甚至可以通過項目做定制化。
上面說Dockerfile是動態(tài)生成并且對開發(fā)人員無感知的捆昏,而這個構(gòu)建過程赚楚,也是通過執(zhí)行容器實(shí)現(xiàn)的,通過容器掛載的方式骗卜,把構(gòu)建的結(jié)果顯示在宿主機(jī)上宠页,這整個的過程都做成了docker鏡像左胞,并且使用Dockerfile管理這些鏡像,通過不同的Dockerfile輕而易舉的自定義各個類型項目的構(gòu)建過程举户,比如node項目的構(gòu)建和Java的構(gòu)建不同烤宙,就定義兩個Dockerfile就可以了,統(tǒng)一在gitlab上管理俭嘁,維護(hù)成本變得非常低躺枕。
發(fā)布
? ? 項目構(gòu)建完成,容器的的編排部署供填,主要是通過marathon操作拐云。marathon本身提供了一些對外開放的api接口,這個開放接口也是我們做自動化基石近她。項目構(gòu)建完畢叉瘩,流程并沒有結(jié)束。marathon管理的服務(wù)編排泄私,主要是配置文件房揭,定義應(yīng)用备闲,內(nèi)存晌端,端口,以及健康監(jiān)測都是在配置文件里面管理的恬砂,所以基于此咧纠,我按照格式定義了一個項目,專門來管理這些項目的配置泻骤,并且把這些配置提交到gitlab上進(jìn)行管理漆羔,項目結(jié)構(gòu)如下:
每一個環(huán)境對應(yīng)的目錄下,都是一個marathon的配置文件模板狱掂,之所以是模板演痒,因?yàn)槊看螛?gòu)建生成不同的鏡像tag是系統(tǒng)自動生成的,會動態(tài)替換參數(shù)趋惨,并且調(diào)用marathon的api接口進(jìn)行服務(wù)更新鸟顺,服務(wù)的更新策略也都配置在marathon配置文件中的,整個的構(gòu)建和發(fā)布流程器虾,分為各自獨(dú)立的兩模塊讯嫂,通過一套shell完成,也可以獨(dú)立使用兆沙,但還是為了考慮操作的簡單性欧芽,接入了jenkins,并且通過jenkins增強(qiáng)了權(quán)限控制葛圃,并且對操作記錄可追蹤千扔。構(gòu)建流程結(jié)構(gòu)如下圖:
運(yùn)行
? ? 一開始我們采用docker憎妙,只是拿了部分服務(wù)測試,并非一蹴而就曲楚,全部直接推廣使用尚氛。項目測試運(yùn)行剛上線,開始遇到第一個問題洞渤。寫到這里阅嘶,簡單普及一下docker的網(wǎng)絡(luò)模式,docker的網(wǎng)絡(luò)模式默認(rèn)的四種方式host载迄,bridge讯柔,none和自定義的網(wǎng)絡(luò)模式,host的方式就是容器和主機(jī)共享網(wǎng)絡(luò)空間护昧,容器和主機(jī)共用網(wǎng)絡(luò)端口魂迄,這種方式的弊端不言而喻,就是部署應(yīng)用的時候要關(guān)注服務(wù)器的端口分配惋耙。bridge的方式是采用的獨(dú)立的網(wǎng)絡(luò)空間捣炬,通過虛擬網(wǎng)橋的方式利用主機(jī)的網(wǎng)卡進(jìn)行通信,容器的網(wǎng)絡(luò)空間是獨(dú)立的绽榛,容器內(nèi)部服務(wù)端口和主機(jī)映射端口是隨機(jī)的湿酸。none的形式就是關(guān)閉網(wǎng)絡(luò)模式,這種適合于計算的服務(wù)類型灭美。而自定義的網(wǎng)絡(luò)模式募寨,主要是來解決跨主機(jī)網(wǎng)絡(luò)通信的問題懊缺,每個容器網(wǎng)絡(luò)空間也是各自獨(dú)立的祥国,而我們的服務(wù)主要采用的就是種方式往核,問題也恰恰出在這里。
由于采用的是部分服務(wù)做docker化犁苏,老的服務(wù)可能需要訪問新的服務(wù)硬萍,但是這個時候,老服務(wù)在利用Dubbo調(diào)用新服務(wù)的通信的時候围详,發(fā)現(xiàn)對方注冊是IP地址無法訪問朴乖,原因就是docker容器內(nèi)的Dubbo提供的服務(wù),注冊上去的其實(shí)是個虛擬IP地址短曾,和原有老的服務(wù)根本就不同一個網(wǎng)絡(luò)內(nèi)寒砖。所以網(wǎng)絡(luò)單向是不通的,網(wǎng)絡(luò)調(diào)用結(jié)構(gòu)如下圖:
既然是網(wǎng)絡(luò)的問題嫉拐,那么針對該問題的解決方案也列了幾個:
方案一:所有服務(wù)進(jìn)行docker容器化部署哩都,這樣的話所有服務(wù)就處于同一個虛擬網(wǎng)絡(luò)段內(nèi),但是這個遷移的成本和風(fēng)險非常大婉徘。
方案二:基于物理層面網(wǎng)絡(luò)做修改配置漠嵌,但是這種維護(hù)的成本較大咐汞,可擴(kuò)展性不好。
方案三:更改容器內(nèi)服務(wù)運(yùn)行的網(wǎng)絡(luò)儒鹿,采用host的方式配置化撕,這種方式其實(shí)并沒有充分利用容器化帶來的便利之處,但是確實(shí)最簡單解決這個問題的方法约炎,所以最終考慮植阴,選擇這個方案解決,而后期的優(yōu)化方案圾浅,去Dubbo化掠手。
其實(shí)針對容器網(wǎng)絡(luò)的模塊,這里也只是冰山一角狸捕,未來面臨更復(fù)雜的產(chǎn)線環(huán)境喷鸽,將會遇到更多網(wǎng)絡(luò)的配置問題。
總結(jié)
? ??容器化之路是灸拍,這里僅僅只是起點(diǎn)做祝,本次只是簡單的介紹了最開始容器化項目構(gòu)建交付的流程,實(shí)際上在實(shí)踐中遇到的問題不止如此鸡岗,希望對大家有所幫助混槐,思路上也希望能有共鳴,后續(xù)對相關(guān)細(xì)節(jié)問題還會再次介紹纤房。