Docker鏡像優(yōu)化指南:如何有效地壓縮鏡像體積并縮短構(gòu)建時(shí)間?
譯文時(shí)至今日,大家已經(jīng)能夠從多種Docker支持的存儲(chǔ)驅(qū)動(dòng)程序中做出選擇,從而確保其與我們的實(shí)際環(huán)境以及用例切實(shí)吻合——然而,除非深入理解鏡像層(更不用提鏡像與容器本身),否則一般用戶根本不會(huì)考慮這方面問(wèn)題。很明顯,這些簡(jiǎn)單而且缺乏吸引力的技術(shù)元素層雖然是構(gòu)成鏡像的基本條件,但卻往往得不到高度關(guān)注——因?yàn)殚W亮的新型工具往往比基本信息更能抓人眼球。
在今天的文章中,我們將探討鏡像體積及構(gòu)建時(shí)間方面的話題——而這兩項(xiàng)工作也已經(jīng)成為用戶們迫切需要實(shí)現(xiàn)的目標(biāo)。
讓我們首先著眼于鏡像與層,對(duì)其概念加以闡述:
- Docker鏡像是一套由只讀層外加部分元數(shù)據(jù)構(gòu)成的標(biāo)簽化結(jié)構(gòu)。
- 每個(gè)層都擁有自己的UUID,而且每個(gè)連續(xù)層都建立在其下的層之上。
- 每個(gè)Dockerfile指令都會(huì)生成一個(gè)新層。
看起來(lái)基本理念非常簡(jiǎn)單,且不需要再做過(guò)多解釋,不過(guò)我曾經(jīng)遇上過(guò)這樣一個(gè)讓人如墜霧里的Dockerfile:
- FROM centos:7.1.1503
- RUN yum -y install java-1.8.0-openjdk-devel-1:1.8.0.65-2.b17.el7_1.x86_64
- RUN yum clean all
這個(gè)Dockerfile到底出了什么問(wèn)題?這個(gè)嘛,其中第二個(gè)RUN命令并沒(méi)能真正影響到鏡像體積——雖然它看起來(lái)確實(shí)應(yīng)該有效削減鏡像大小。讓我們重新審視Docker鏡像與層的定義,并著重強(qiáng)調(diào)其中的語(yǔ)法表達(dá):
Docker鏡像是一套由只讀層外加部分元數(shù)據(jù)構(gòu)成的標(biāo)簽化結(jié)構(gòu)。
每個(gè)層都擁有自己的UUID,而且每個(gè)連續(xù)層都建立在其下的層之上。
每個(gè)Dockerfile指令都會(huì)生成一個(gè)新層。
現(xiàn)在,可以明確看到該Dockerfile并沒(méi)能優(yōu)化鏡像體積。無(wú)論如何,讓我們進(jìn)行深入探討,看看這些yum緩存是如何被從鏡像層當(dāng)中移除出去的。
不過(guò)為了保證文章淺顯易懂的特性,我們首先將關(guān)注范疇限定在AUFS存儲(chǔ)驅(qū)動(dòng)程序身上。AUFS存儲(chǔ)驅(qū)動(dòng)程序能夠添加一個(gè)疏排文件以覆蓋鏡像中底部只讀層內(nèi)文件的存在,從而將其從層內(nèi)刪除出去。除此之外,大家可以推斷出鏡像的大小相當(dāng)于各層體積的總和,而添加到Dockerfile中的每條附加指令都會(huì)進(jìn)一步增加鏡像的體積。
只要將兩項(xiàng)RUN指令加以合并,我們就能輕松對(duì)上面提到的Dockfile進(jìn)行修復(fù):
- FROM centos:7.1.1503
- RUN yum -y install java-1.8.0-openjdk-devel-1:1.8.0.65-2.b17.el7_1.x86_64 && \
- yum clean all
下面讓我們構(gòu)建并檢查這兩套鏡像以證明其瘦身效果。大家需要執(zhí)行docker build -t
- $ docker images
- REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
- combined-layers latest defa7a199555 4 seconds ago 407 MB
- separate-layers latest b605eff36c7b About a minute ago 471.8 MB
大家應(yīng)該能夠輕松判斷哪套鏡像是通過(guò)哪個(gè)Dockerfile構(gòu)建而成,但讓我們進(jìn)一步查看兩套鏡像各自包含的層:
- $ docker history --no-trunc separate-layers
- IMAGE CREATED CREATED BY SIZE
- b605eff36c7b418aa30e315dc0a1d809d08a1ebb8e574e934b11f5ad7cd490dc 2 minutes ago /bin/sh -c yum clean all 2.277 MB
- 4c363330dc9057ce6285be496aa02212759ecfc75c4ad3a9a74e6e2f3dacb1dd 2 minutes ago /bin/sh -c yum -y install java-1.8.0-openjdk-devel-1:1.8.0.65-2.b17.el7_1.x86_64 257.5 MB
- 173339447b7ec3e8cb93edc61f3815ff754ec66cfadf48f1953ab3ead6a754c5 8 weeks ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0 B
- 4e1d113aa16e0631795a4b31c150921e35bd1a3d4193b22c3909e29e6f7c718d 8 weeks ago /bin/sh -c #(nop) ADD file:d68b6041059c394e0f95effd6517765405402b4302fe16cf864f658ba8b25a97 in / 212.1 MB
- a2c33fe967de5a01f3bfc3861add604115be0d82bd5192d29fc3ba97beedb831 7 months ago /bin/sh -c #(nop) MAINTAINER The CentOS Project - ami_creator 0 B
- $ docker history --no-trunc combined-layers
- IMAGE CREATED CREATED BY SIZE
- defa7a199555834ac5c906cf347eece7fa33eb8e90b30dfad5f9ab1380988ade 48 seconds ago /bin/sh -c yum -y install java-1.8.0-openjdk-devel-1:1.8.0.65-2.b17.el7_1.x86_64 && yum clean all 195 MB
- 173339447b7ec3e8cb93edc61f3815ff754ec66cfadf48f1953ab3ead6a754c5 8 weeks ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0 B
- 4e1d113aa16e0631795a4b31c150921e35bd1a3d4193b22c3909e29e6f7c718d 8 weeks ago /bin/sh -c #(nop) ADD file:d68b6041059c394e0f95effd6517765405402b4302fe16cf864f658ba8b25a97 in / 212.1 MB
- a2c33fe967de5a01f3bfc3861add604115be0d82bd5192d29fc3ba97beedb831 7 months ago /bin/sh -c #(nop) MAINTAINER The CentOS Project - ami_creator 0 B
這清楚地表明鏈?zhǔn)矫钅軒椭覀冊(cè)趯?duì)各層進(jìn)行提交前進(jìn)行清理工作,不過(guò)這并不意味著我們應(yīng)當(dāng)將一切都塞進(jìn)單一層當(dāng)中。如果大家重新審視以上命令的輸出結(jié)果,就會(huì)發(fā)現(xiàn)兩套鏡像的3個(gè)***層擁有著同樣的UUID,這意味著兩套鏡像共享這些層(有鑒于此,docker images命令才能夠報(bào)告各鏡像的虛擬體積)。
有人可能會(huì)說(shuō),如今磁盤空間的使用成本已經(jīng)如此低廉,我們真的有必要如此糾結(jié)于所謂鏡像體積嗎?但除了緩存鏡像層之外,大家還有其它方面需要關(guān)注。其中最重要的一點(diǎn)在于鏡像的構(gòu)建時(shí)間。簡(jiǎn)單來(lái)講,如果大家能夠復(fù)用某個(gè)層,則無(wú)需對(duì)其進(jìn)行重新構(gòu)建。另外,如果大家的鏡像需要通過(guò)網(wǎng)絡(luò)進(jìn)行傳輸(例如在流程中分階段進(jìn)行構(gòu)建推進(jìn)),那么更為袖珍的鏡像體積與緩存層運(yùn)用能夠顯著節(jié)約傳輸時(shí)長(zhǎng)(并降低網(wǎng)絡(luò)流量負(fù)載),因?yàn)樾枰獙?shí)際傳輸?shù)溺R像層中有相當(dāng)一部分已經(jīng)被整合在新版本鏡像當(dāng)中。
現(xiàn)在結(jié)論已經(jīng)顯而易見(jiàn),大家應(yīng)當(dāng)盡可能減少Dockerfile頂層的指令數(shù)量,從而提高緩存復(fù)用比例并努力著眼于底層對(duì)Dockerfile進(jìn)行變更。
考慮到以上各項(xiàng)因素,有些朋友可以認(rèn)為優(yōu)化程度***的解決方案應(yīng)當(dāng)是將全部發(fā)生變更的元素塞進(jìn)各自不同的層當(dāng)中,從而清理每個(gè)層的執(zhí)行流程; 但一般來(lái)講,這種作法并不會(huì)簡(jiǎn)化工作負(fù)擔(dān)。首先,Docker對(duì)層數(shù)做出了限制,即不可超過(guò)127個(gè),而且大家***與上限之間保持一定距離。包含有大量層的Dockerfile既不易于后期維護(hù),也不太可能排除那些不必要的數(shù)據(jù); 相反,其結(jié)果是我們會(huì)面對(duì)一套非常臃腫的鏡像,且***將其拆分成多個(gè)不同鏡像。而更重要的是,層的實(shí)現(xiàn)并非毫無(wú)成本,具體取決于我們使用的存儲(chǔ)驅(qū)動(dòng)程序,由此造成的額外負(fù)擔(dān)也有所區(qū)別。舉例來(lái)說(shuō),在AUFS當(dāng)中,每個(gè)層都會(huì)在面向鏡像層堆棧中各現(xiàn)有文件的***寫入時(shí)給容器寫入性能造成延遲,特別在文件體積龐大且存在于大量鏡像層之下的情況當(dāng)中。
因此在文章***,我們需要再次強(qiáng)調(diào):要想切實(shí)提升鏡像體積優(yōu)化效果并壓縮構(gòu)建時(shí)間,大家必須了解自己想要優(yōu)化什么,并有意識(shí)地做出必要妥協(xié)。
1. 如果大家需要了解或者深入掌握容器中的層概念,請(qǐng)點(diǎn)擊此處查看Docker說(shuō)明文檔。
2. 查看存儲(chǔ)驅(qū)動(dòng)程序說(shuō)明文檔以了解與其性能表現(xiàn)相關(guān)的各項(xiàng)細(xì)節(jié)信息。
3. 如果大家發(fā)現(xiàn)自己需要處理高強(qiáng)度寫入工作負(fù)載,可以考慮使用數(shù)據(jù)分卷(它們會(huì)繞開(kāi)存儲(chǔ)驅(qū)動(dòng)程序)。
原文標(biāo)題:Optimizing Docker Images for Image Size and Build Time
【51CTO.com獨(dú)家譯稿,合作站點(diǎn)轉(zhuǎn)載請(qǐng)注明來(lái)源】