Docker系列之镜像瘦身(五)
本節(jié)我們來講講在我們在構(gòu)建鏡像過程中不出問題,同時使得最后所構(gòu)建的鏡像文件大小盡可能最小。
緩存(cache)
Docker的優(yōu)勢之一在于提供了緩存,加速鏡像迭代構(gòu)建,我們知道構(gòu)建鏡像使用docker build命令,也就是說通過docker build的緩存機制實現(xiàn)了鏡像的復(fù)用,不僅節(jié)省鏡像存儲空間,也為鏡像構(gòu)建節(jié)省了大量時間。
?
Docker會由上至下逐步執(zhí)行Dockerfile中的指令,按順序執(zhí)行每個指令,在檢查每條指令時,Docker在其緩存中查找現(xiàn)有的中間圖像,可能會重用而不是創(chuàng)建新的中間圖像,如果緩存無效,則使其無效的指令和后續(xù)所有Dockerfile指令都會重新生成新的中間鏡像。
一旦緩存失效,就可以使用Dockerfile中的其余指令,所以從Dockerfile的頂部開始,如果基礎(chǔ)映像(父鏡像)已經(jīng)在緩存中,則重用。
然后繼續(xù)執(zhí)行下一條指令與從該基礎(chǔ)圖像導(dǎo)出的高速緩存中所有子鏡像進較,比較每個緩存的中間鏡像以查看指令是否找到并緩存是否命中,如果高速緩存未命中,則高速緩存無效,如此重復(fù)執(zhí)行以上過程,最終到達Dockerfile的末尾。
大多數(shù)新指令只是與中間鏡像中的指令進行比較,如果匹配,則使用緩存副本。比如,當在Dockerfile中執(zhí)行RUN pip install -r test.txt指令時,Docker會在其本地緩存的中間圖像中搜索相同的指令,但是舊的和新的test.txt文件中的內(nèi)容不會進行比較。如果我們使用新軟件包更新test.txt文件并使用RUN pip install指令希望使用新軟件包名稱重新運行軟件包安裝,如果是這種情況可能會出現(xiàn)問題。
同時呢,ADD和COPY與其他Docker指令不同,ADD和COPY指令需要Docker查看文件中的內(nèi)容以確定是否存在緩存命中,Docker將會引用文件內(nèi)容的校驗和與現(xiàn)有中間圖像中的校驗和進行比較,如果文件內(nèi)容或元數(shù)據(jù)已更改,則緩存無效。
?
從如上我們對Dokcer緩存機制的大致講解,若我們對指令進行了更改,那么該指令的后續(xù)每一層可能會經(jīng)常重建,所以為了更好的利用緩存,我們需要將可能需要更改的指令盡可能的放在比較低的位置。當我們在構(gòu)建鏡像時我們可以指定參數(shù)(--no-cache=true)來關(guān)閉緩存。當我們需要更新源時,我們通過鏈式調(diào)用apt-get-update和apt-get-install來避免緩存丟失問題。
在Docker中我們可以通過命令來監(jiān)控文件鏡像和容器文件大小,我們知道docker container ls命令查看容器列表,docker container ls -s?則可以查看容器大小,如下:
為了查看鏡像構(gòu)建中中間鏡像歷史記錄,使用命令docker image history imagename:tag?,為了更清晰看到鏡像中每一層的內(nèi)容,我們可以使用庫https://github.com/wagoodman/dive,比如我們上一節(jié)所構(gòu)建的鏡像為webapi。我們安裝完dive庫后,然后查看鏡像中的每一層,通過如下命令。
文件忽略(.dockerignore)
我們知道Docker有兩個組件:客戶端和守護進程,當我們編寫docker命令時,我們使用客戶端向Docker deamon發(fā)送命令,該命令執(zhí)行所有工作,這里我們需要知道客戶端和守護進程可以單獨部署到兩臺獨立機器上,為了讓Docker守護進程使用docker build從Dockerfile文件中構(gòu)建映像,客戶端需要向其發(fā)送執(zhí)行命令的“上下文”, 上下文基本上可以說是傳遞給docker build命令的目錄中的所有文件,使用Dockerfile構(gòu)建時,我們在客戶端可以清楚看到發(fā)送的上下文,例如如下:
對于大型項目而言,上下文可能會變得非常大,毫無疑問這將會減緩?fù)ㄟ^Dockerfile構(gòu)建鏡像的速度,因為我們必須等待客戶端將所有文件發(fā)送到守護進程。比如在ASP.NET Core應(yīng)用程序中,在項目根目錄下可能包含大量文件,這些文件對于大多數(shù)Dockerfile構(gòu)建來說都不是必需的,比如Git文件或者SVN文件,obj、bin等文件, 所有這些附加文件在作為上下文的一部分發(fā)送時會減慢構(gòu)建速度。docker為我們提供了解決方案,我們可以通過在根目錄中創(chuàng)建.dockerignore文件來忽略不需要的文件,.dockerignore類似于.gitignore文件,其中包含Docker與文件名匹配的模式列表,并在制作鏡像時忽略,比如如下忽略
當我們在客戶端執(zhí)行docker build命令來創(chuàng)建映像時,Docker會檢查.dockerignore文件,如果存在此文件,它會逐行瀏覽文件并使用Go的filepath.Match規(guī)則以及Docker自身的一些規(guī)則來匹配文件名以此來進行忽略。比如在.dockerignore文件中包含* .vs將排除帶有.vs擴展名的文件。我們可以使用以#開頭的注釋來解釋在.dockerignore中正在執(zhí)行的操作。.dockerignore可以幫助減小鏡像大小,文件越少意味著鏡像越小,鏡像越快,減少構(gòu)建緩存失效,如果日志或其他文件正在更改,并且我們的鏡像因其緩存而使其緩存失效,則會降低構(gòu)建周期。綜上我們針對需要使得我們所構(gòu)建的鏡像可以注意以下幾方面:
1.使用官方鏡像,可能很多文章或博客講解的鏡像已過時。
2.若可能,盡可能使用Alpine鏡像,以保持鏡像輕量級。
3.如果使用apt,在同一指令中將RUN apt-get update與apt-get install結(jié)合使用, 然后在該鏈式指令中鏈接多個包, 使用\字符在多行中按字母順序列出包,最后使用rm -rf /var/lib/apt/lists/* 的目的在于清除apt緩存而不會使其緩存保存到鏡像層上。例如:RUN apt-get update && apt-get install -y \package-one \package-two && rm -rf /var/lib/apt/lists/*
4.將可能需要更改的指令盡量放在Dockerfile比較低的位置(可能的話)
5..使用.dockerignore文件將不需要的和不必要的文件留在鏡像之外。
6.可以嘗試結(jié)合使用dive庫監(jiān)控鏡像層,然后合理優(yōu)化鏡像大小。
.NET Core使用Docker鏡像優(yōu)化
接下來我們來一起看看針對上一節(jié)所寫的Dockerfile文件進行分析,看看是否可以為鏡像瘦身而進一步優(yōu)化,上一節(jié)的構(gòu)建鏡像文件說明書如下:
首先我們獲取基礎(chǔ)鏡像,然后只是復(fù)制項目文件而不是復(fù)制所有文件,這可以避免額外無關(guān)的還原,并允許我們重用中間鏡像層,如果我們更改項目文件,只是會再次來一遍重新復(fù)制,這也就意味著更快的構(gòu)建!如果是自包含的應(yīng)用程序,我們下載對應(yīng)的鏡像而不包含運行時鏡像,這樣鏡像大小最小,同時最后運行時自包含應(yīng)用程序則無需使用dotnet命令,直接切換到項目根目錄通過CMD執(zhí)行即可。
.NET Core使用Docker移除未使用代碼
我們使用預(yù)發(fā)布的包工具《https://www.nuget.org/packages/Microsoft.Packaging.Tools.Trimming/1.1.0-preview1-25818-01》,通過它我們可以查看應(yīng)用程序并刪除沒有調(diào)用的代碼和二進制文件,接下來我們包括Microsoft.Packaging.Tools.Trimming添加包以及忽略文件基于上一節(jié)構(gòu)建鏡像進行如下改造。
由如上第一張圖我們可以看到,當我們添加了忽略文件后,它將上下文從4.206MB減少到16.9KB,而無需花費更多時間來發(fā)送上下文,現(xiàn)在可以說是秒送。?本節(jié)我們詳細講解了從哪幾方面入手來瘦身鏡像,下一節(jié)我們深入講講Docker中的網(wǎng)絡(luò)內(nèi)容,感謝您的閱讀,下節(jié)再會。?
推薦閱讀
(點擊標題可跳轉(zhuǎn)閱讀)
Docker系列之AspNetCore Runtime VS .NetCore Runtime VS SDK(四)
Docker系列之.NET Core入門(三)
Docker系列之烹飪披薩(二)
Docker系列開篇之Virtual Machine VS Container(一)
總結(jié)
以上是生活随笔為你收集整理的Docker系列之镜像瘦身(五)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Docker(二)-在Docker中部署
- 下一篇: VS, VS Code, VS Onli