鯨品堂|如何(hé)快速打通(tōng)鏡像發(fā)布流程?

2022-08-09 76

近期(qī)Q-eye發(fā)布了一篇“鏡像瘦身”的分享,引起大家的關注,很多人來谘詢“鏡像瘦身”的方案。這篇分享從(cóng)實例出發,證明了鏡像優化的重要性和有效性,起到了很(hěn)好的拋磚引玉作用,但(dàn)是其內容較(jiào)短,原理性說明較少,希望通過本文來對“鏡像瘦身”過程進行(háng)梳理,以實(shí)戰案例進一步闡明該方案(àn)。


容器化發布通過將應用以及應用所依賴(lài)的環境(比如JRE、動態庫,環境(jìng)變(biàn)量,係統目錄等)一起打包為(wéi)鏡像,解決了發布的一致性問題,即能夠Build Once,Run Everywhere,但是這種方案也帶來一個副作用,就是鏡像太大,容器鏡像提供了分層(céng)機(jī)製,每次(cì)隻需要傳輸變更的層(céng),一定程度上降低了傳輸量。


為了解釋清楚容器鏡像的壓縮和(hé)傳輸方法,首先簡單介紹一下容(róng)器分層:





容器分層文件係統




當我(wǒ)們在主機上創建一個新(xīn)的容器時,會為這個容器單獨創建一(yī)個文件係統,這個分層的文件係統(tǒng)是將鏡像中的(de)文件層(céng)作(zuò)為隻讀層,並新建一個可讀可寫的(de)文件(jiàn)層疊加到鏡像的隻讀層上麵,容器內的(de)所有操作都發生在讀寫層。這樣做可以實現:



鏡像文件都是隻讀(dú)的,可以使用一個(gè)鏡像創建N個容器,每(měi)個(gè)容器之間都相互隔離。


多個容器共享一個隻讀層,能夠利用(yòng)文件係統的緩存機製,加速數(shù)據的(de)讀取。



當我們製作一(yī)個新的鏡像時,過程(chéng)也是(shì)類似(sì)的,會在基礎鏡像文件層的基礎(chǔ)上新疊加(jiā)一個讀寫層,並在讀寫層(céng)上放入新內容,然後將新的讀寫層變成一個新的隻讀層,形成一個新(xīn)的鏡像。所以新手容易碰到的一個(gè)問(wèn)題是:我在Dockerfile裏麵使用rm刪除了(le)基礎鏡像中的一些文件,但是(shì)為(wéi)什麽最終鏡像沒有減小?理解了這個(gè)讀(dú)寫層的機製就會知道,任何操作都發生在讀寫層(céng),因此刪除文件時並不能真的去刪除以前的文件,隻是在讀寫層上做一個(gè)特殊(shū)標記,讓文件(jiàn)係統看不到這個文(wén)件而已,因此鏡像不會縮小。


因此分層機製有如下的弊端:



每次做(zuò)修改都會新建一個(gè)層(céng),這樣層級就非常(cháng)多。


一旦寫入文件並打成了鏡像,後續基於這個鏡像製作的鏡像,就無法刪除這個文件所對應的空(kōng)間。






鏡像分層傳輸(shū)機製




一個(gè)容器鏡像是由描述文(wén)件和一係列(liè)數據文件組成,每個數據文件對應一個文件層。當我們拉取或者推送鏡像時,首先會獲取描述文件,然後根據描述(shù)文件判斷哪些層本地已經有了(或(huò)者遠端已經有了),然後就隻傳輸不存在的層即可,大幅(fú)減少傳輸量。但(dàn)是,如果兩個係統之間無法直連,就無法判斷哪些鏡像層對端已經有(yǒu)了,因此這個機製就會失效。


了解了這些原理後,就可(kě)以進一步討論如何優化鏡像的大小了。





Dockerfile上(shàng)的(de)優化




為了降低容器鏡像的大小,在編寫Dockerfile時注意參考如(rú)下經驗:



各團隊盡量使(shǐ)用統一的基礎鏡像。建立並維護公司統一的基礎鏡像列表。


減少Dockerfile的行數,使用“&”連接(jiē)多個命令(lìng),因為每一行(háng)命令都會生成一個層。


將增(zēng)加文件和清理文件的動作放到一(yī)行裏麵,比如yum install和yum clean all,如果分為兩(liǎng)行(háng),第二條清理動作就無法真正刪除文件。


隻複製需要的文件,如果整個目錄複製,一定要仔細檢查目(mù)錄下是否有隱藏文件(jiàn)、臨時文件等(děng)不需要的內容。


容器鏡像(xiàng)自(zì)身有壓縮機製,因此(cǐ)把文件壓縮成壓縮文件然後打入容器,容器啟動時(shí)解壓的方法並不會有什麽效果。


避免向生產鏡像打入一(yī)些不必要的工具,比如有的團隊打入了sshd,不應該使用這種方案,增加安全風險。


盡量精簡安裝的內容(róng),比如隻安(ān)裝工具的運行時,無須帶上幫助文檔、源碼、樣例等等。






鏡像分層(céng)上的優化




一(yī)個典型的java應用(yòng)的鏡像(xiàng)的大小是在500M左右,其中300M左右是基(jī)礎(chǔ)鏡像(包(bāo)含OS/JRE等),還有200M是應用相關的文件。雖然看上去並不多,但是在微服(fú)務架構下,應用的數量比較多,假(jiǎ)設我們每個(gè)版本發布30個鏡像(xiàng),則總傳(chuán)輸量會(huì)有300M + 200M* 30,大概要6G左右,還是會比較大。


可以通過將應用進一步分層來(lái)減少每次發布量,有兩種劃分分層的方法:





相似的(de)幾個產品共享一個基礎鏡像(xiàng),將公共的包放到基礎鏡像層

假設A/B/C三個產品都(dōu)使用了相同(tóng)的技術架(jià)構,比如都(dōu)使用了20個相同的jar包,這些jar包一共100M。如果這三個產品創建一個共享的中(zhōng)間(jiān)層基礎鏡像,然後基(jī)於這個基礎鏡像再打各自的鏡像,則每次應用層的(de)發布數據量會由原來的 200M * 3 變成 100M + 100M * 3,這樣就可(kě)以(yǐ)減少發布量(liàng)。



根據冷熱進(jìn)行分(fèn)離,將不變的部分做到基礎鏡像(xiàng)

假設(shè)A應用一共200M,但(dàn)是三方jar包就有180M,自己的應用(yòng)隻有20M,則可以創建一個中間層(céng)基礎鏡像(xiàng),這樣每次(cì)發(fā)版本時,如果三方jar包沒有變動,則(zé)中(zhōng)間(jiān)層不需要重新傳遞,隻傳遞20M的自有應用,也(yě)可以(yǐ)降低發布的(de)量。



我通(tōng)常把這種為了(le)減少發布量的分層叫做offload層。效果非常直接,但(dàn)是(shì)offload層(céng)也會帶來很多管(guǎn)理問題:



對(duì)於(yú)第一類,多個產品共(gòng)享一個offload層,需要這幾個(gè)產品保(bǎo)持密切的溝(gōu)通,假設需要更新offload層某個組件的版本,則幾個產品需要同時協同一起(qǐ)變更和發布。如果存在不一致則可能會引入問題。


第二個也會存在offload層更新的問題。比如:目前Java應用流行(háng)使用maven來管理依賴,如何根(gēn)據(jù)maven的輸出及時更新offload層?如果更新(xīn)不及時,也會造成不(bú)一致。



Offload層的更新問題如(rú)果(guǒ)依賴管理(lǐ)流程或者人(rén)為檢查是不可靠的,我們建議將offload層當成cache一樣使用,實現方案如下:



按照業務特(tè)點抽取出公(gōng)共文件和冷文件做成offload層


在製作(zuò)鏡像(xiàng)時,利用multi-stage builds機製,先將最新的全量的內容複製到(dào)Stage中,然後在Stage進行一次比對,如果offload層是最新的就用,如(rú)果不是最新的,則使用最(zuì)新(xīn)的替代。


以Java為例,Dockerfile寫法如下:









Stage 複製所有的lib包到/app/lib/目錄下RUN一個腳本,檢查(chá)下/app/lib/下的文件和offload層中/app/lib_shared/下的文件,如果(guǒ)兩個文件一致,則刪除文件並建立一個軟(ruǎn)鏈接來替代實際文件
Build 從Stage中複製/app/lib/目錄做成最終的真正的層


這樣即使出現(xiàn)依賴包發生了更新,而offload層未能及時更新的情(qíng)況,隻會造成鏡像offload失敗,鏡像比較大一些而已,不會造成故障。


注意: 目前發現tomcat如果要支持軟鏈(liàn)接,需要打開allowLinking開關,否則會失敗。





鏡像(xiàng)傳輸的(de)優化




前文提到過,鏡像分層傳輸必(bì)須是(shì)源倉庫和(hé)目標倉庫的網絡能(néng)夠互通的情況,但是(shì)實際場景往往比較複雜,比如很多(duō)項目都有嚴格(gé)的(de)網絡管控,不允許服務器直接訪問外網;很多國際項目到國內的網絡連接比較差,速度慢並且經常丟包。所以我(wǒ)們需要(yào)分情況來(lái)討(tǎo)論。





在線的壓縮和傳輸方案




假設能夠找到一台機器,這台機器既(jì)能夠訪(fǎng)問源倉庫也能(néng)夠訪問目標倉庫,可以在這台機器行安(ān)裝一個Docker,然後直接使用docker pull/push的(de)方式,拉取(qǔ)和(hé)上傳的過程都是增量傳輸;但是(shì)這種方(fāng)式需要安裝Docker,並且會占用文件係統空間(鏡像會暫存在本地)。推薦使用(yòng)我們開源的 image-transmit (https://github.com/wct-devops/image-transmit)工具這個工(gōng)具一端連接源倉庫,一端(duān)連接目標倉庫,直(zhí)接將增量數據層轉發過去,中間(jiān)數據不落盤,效率是(shì)最高的。同(tóng)時這個工具是一(yī)個綠色版的界麵化工具,使用(yòng)簡單(也支持(chí)命令行),壓縮(suō)後(hòu)隻有幾M大小,資源(yuán)消耗很少,可以在一些安全跳板機上穩定運行。





離線的壓縮和傳輸方案




很多場景下我們無法實現在線的(de)傳輸,需要先將鏡像保存成一個文(wén)件,然(rán)後(hòu)利用各種手段發(fā)給現場,比(bǐ)如通過百度雲盤中轉、存(cún)到U盤然後快遞過去等。在(zài)離線傳輸模(mó)式下重點是考慮如何能夠把鏡像包壓縮到最小,以及壓縮和解壓縮的時間(jiān)。下麵介(jiè)紹(shào)幾種模式:



docker save|gzip方(fāng)式,這種是最基本的方法,找一台(tái)安裝有Docker的機器,將鏡(jìng)像拉取到本地,然後使用save命令(lìng)保存並壓縮。這種方式非常耗時(shí),同(tóng)時壓縮包也是最大的。


使用(yòng)上文提到的image-transmit工具進行離線打包,默認使用tar算法(fǎ)。這個工具可以直接從倉庫上下載鏡像的數(shù)據文件,合並成壓縮包,比上一種方式減少了保存到本(běn)地然後導(dǎo)出以及壓縮和解壓縮的過(guò)程,速(sù)度可以提升(shēng)20倍,同時如果(guǒ)一次(cì)壓(yā)縮多個鏡像,相同的鏡像層隻會保留(liú)一份,這能夠降低壓縮包的大小,以我們自己的鏡像版本為例,使用工具得到的壓縮包是(shì)docker save方法的1/3到1/5大小。


同時image-transmit還提供了squashfs算法(fǎ),這種壓縮算法在上(shàng)一種方式的基礎上更(gèng)進一步,會把每一(yī)個鏡像(xiàng)層都解壓,對每一個文件(jiàn)進行固實壓(yā)縮,舉個例子,A產品和B產品都用到(dào)了demo.jar,但是這個jar並不在基礎鏡像層,上(shàng)一種方式是無法識別這種重複的,但是(shì)squashfs壓縮(suō)方式可(kě)以識別並將其壓縮(suō)為一(yī)份(fèn),這樣能夠進一步降低壓縮包的(de)大小(xiǎo),以我們自己的(de)鏡像包為例,這種壓縮方式可以(yǐ)在上一(yī)種方式的基礎上再降低30%~50%。但是這種壓縮算法由於需要將所有的鏡像層都解(jiě)壓然後進行壓縮,導致其壓縮時間非常(cháng)長,上一種方式5分鍾可以壓縮完的包(bāo),這種方式要一個(gè)小時左右。



壓縮算法的選擇可以根據實際情況來選擇(zé),甚(shèn)至把(bǎ)兩種(zhǒng)方式都試一下,綜合選擇一(yī)個合適的。離線方(fāng)式因為無法直連倉庫,所以上文中的方(fāng)法是把所有的鏡像層都保存到離線包中了,那麽如何在離線模式(shì)下也能實現增量方式發布呢?


image-transmit實現了這樣一種增量發布方法:在製作壓縮包時,可以根據(jù)上次的壓縮包的信息自動跳過已經發送過的鏡像(xiàng)層,隻發(fā)增量變(biàn)更的部(bù)分,這樣(yàng)就可以進一步降低壓縮包的大小。不過這種方(fāng)式(shì)也有弊端,比如(rú)必須嚴格按照順序下載版本,如果遺漏某個版(bǎn)本可能(néng)會造成現場倉庫缺失一些數(shù)據層,造成失敗。如果版本發布比較多,可以采用類似如下的方案來規避這(zhè)種風險:



每個月發布(bù)一個全量的(de)版本包


每日/周的版本基於月初的全量包來製作增量包


這種準(zhǔn)實(shí)時的增量同步加上定期的全量同步,即可以降低同步量又可以避免缺失一些層造成傳(chuán)輸失敗。





總結和(hé)後續規劃




鏡像傳輸還有一些優化方向亟待研究,比如:



業界有很多鏡像,其容器內隻有應用的二進製程序,因此其鏡像(xiàng)大小與傳統應用發布沒有什麽區別。但是這種鏡像在做問(wèn)題分析時,需要依賴外部的工具,一定程序上提高了故障分析的門檻,可以通過sidecar方式來提供排障工(gōng)具。


鏡像過大不僅僅影響發布,在版本升級(jí)、容器切換等容器重新創建的場景下,消耗大量的網絡帶寬和磁盤IO,鏡像倉庫容易成為瓶頸,需要考慮類似Dragonfly的P2P分發方案。


目前我們主要使用Centos作為基礎鏡像,我們(men)正在(zài)考慮更換更為輕量的,專門為容器設計的基礎鏡像。



“鏡像瘦身”其實(shí)是個係(xì)統性的工程,需要多個團(tuán)隊相互配合,從技術平台到業務應用(yòng)到交(jiāo)付相互配合一起(qǐ)落(luò)地。



官方微信公眾號

国产亚洲熟妇在线视频雲計算科技股份有限(xiàn)公司 版權所有 2003-2023

蘇ICP備10224443號-6       蘇公網安備 32011402011374號

国产亚洲熟妇在线视频-亚洲熟妇AV乱码在线观看-亚州国产AV一区二区三区伊在-中文字幕无码人妻少妇免费视频-欧美 日韩 人妻 高清 中文-熟妇人妻中文字幕无码老熟妇-丰满熟女人妻一区二区三-亚洲精品字幕