本文講的是<b>Dockerfile之優化經驗淺談</b>,【編者的話】本文主要講述如何優化Dockerfile,來縮短docker鏡像建構需要的時間,以及Dockerfile的一些編輯規範,推薦所有的Docker愛好者閱讀,非常基礎的文章,本文也許會給你一些啟發和指導。
Docker鏡像應該是小而快的。然而,假設你在BusyBox鏡像中預編譯GO二進制檔案,他們就會變得又大又複雜。如果不能建構一個良好的Dockerfile來幫助你提高建構緩存命中率,那麼你的鏡像建構過程将會變得相當的緩慢。
比如一個用于軟體安裝的bash腳本,裡面堆砌着大量的curl、wget等指令語句,大家在寫Dockerfile的時候通常就會像寫這個bash腳本一樣,将一系列的Docker指令堆砌在其中,這種Dockerfile在建構鏡像的時候是比較低效和緩慢的。
當你正在為一個應用程式建構一個新的Dockerfile,在決定需要引哪些包、運作什麼指令的時候肯定會進行很多次嘗試,也會遇到很多的問題。優化你的Dockerfile確定命中“建構緩存”的機率越來越大,這樣之後的每一次建構中會比前一次要更快一些。
一般的規律是有頻率的改變Dockerfile中指令的排序,觀察分析運作指令所耗費的時間及與其他鏡像共享資源的方式。
這就意味着像WORKDIR、CMD、ENV這些指令應該在底部,然而一個<code>RUN apt-get -y update</code>更新應該在上面,因為它需要更長時間來運作,也可以與你所有的鏡像共享。
最後任何ADD(或其它緩存失效的指令)指令應該盡可能地在Dockerfile底部,在那裡你有可能做出很多改變,然後後續指令緩存失效。
在如Ubuntu這樣的作業系統鏡像和Python或Java7中一個特定的應用程式中,有很多基礎鏡像可供選擇。常識告訴你使用Ruby2來運作基于Ruby應用程式并且使用Python3運作Python應用程式。但是現在你有兩個幾乎沒有共同之處的基礎鏡像,是以你需要下載下傳和建構。相反,如果你使用Ubuntu運作這兩個程式,你隻需要下載下傳一次基礎鏡像。
在一個Dockerfile中每個指令都會在原來的基礎上生成一層鏡像。你可以很快的在三十多層的時候就結束了,這未必是一個問題,但也可以通過組合RUN指令,并使用一行EXPOSE指令列出你所有的開放端口,這樣可以有效減少鏡像的層數。
通過将RUN指令分組,可以在容器間分享更多的層。當然如果你有一組指令可以多個容器通用,那麼你應該建立一個獨立的基礎鏡像,它包含你建立的所有鏡像。
對于每一層來說你都可以跨多個鏡像分享,這樣可以節省大量的磁盤空間。
在建立容器并考慮到體積問題的時候,不要為了節省空間去使用體積小的鏡像,盡量使用你将要提供資料的應用程式打成的鏡像。如果你這樣做了,并且送出了磁盤資料,你不僅在容器中儲存了你的資料,而且對實際應用程式的調試也非常有用。
當你已經建構了一個鏡像,在運作它的時候發現有一個package缺少了,把它添加到Dockerfile的底部,而不是添加到頂部的run apt-get指令那裡。這意味着你能盡快的重新建構這個鏡像了。一旦你的鏡像可以正常工作,你可以再送出源碼之前重新優化整理Dockerfile。
如果一個Dockerfile是由類似于一個bash腳本寫出來的,那麼它可能會是這樣的:
然而這個Dockerfile的優化版本是基于之前所讨論的内容的,它看起來是這樣的:
這是最常見的共享層,在同一個主機上運作所有鏡像應該從它開始。你可以看到我已經添加了一些諸如curl和git的操作,他們不是必須的,但是對調試很有用。而且因為他們在分享層,是以它們不會占用太多空間。
現在說一下我們的語言規範。在這裡我已經包含了python和apache的部分,因為到底把誰放在第一位并不十厘清楚。如果我們把apache放在第一位,我們可以獲得一個包含層和免費得到apache的ruby應用程式。
我把這些單獨說出來是為了以下這些原因。
首先,鏡像中添加Apache部分之後直接建構其他需要的部分子產品,以便于在建構多個鏡像的過程中盡量多應用公共緩存。你也許認為這并不重要,因為類似EVN的調用是很便宜的,但是我見到過随機的ENV調用耗費了10秒鐘甚至更多時間。
有一個很好的例子:你可能想要在容器的底部啟動,但這些指令不能被改變的,那麼最好把他們移到略微靠前一點的地方。
其次,我真希望Docker能夠在同一行指定多個環境,這樣可以減少層數,最終提供了一種最簡化的建構方式。
這包含了一些特定的apt和pip等資源包。你可以在一個單一的指令中加入他們,利用&&符号最為分隔符,如果需要修改隻需要修改這條組合指令即可。
這包含了一大堆簡易的指令,如ADD和VOLUME,比起以前安裝的包,他們更不可能改變,但運作的效率也不慢,是以在這些指令的緩存失效以後就會變得并不那麼重要了。
是以建議把類似的這些指令放在Dockerfile的底部。
你應該在最後面使用ADD指令。
将這些類似ADD指令的操作放入最後一層。
希望這篇文章能夠幫你編寫一個更好的Dockerfile檔案。這些都是我在建構我自己的鏡像的時候所經曆過的,雖然他們可能并不适用于所有情況(或可能是錯誤的),但他們确實提高了我的開發經驗。
===========================
譯者介紹
王康,現就職于sion,進階軟體工程師,負責雲圖部署服務的相關工作。曾就職于momo。個人比較熱愛新興技術,熱愛共享學習。
原文釋出時間為:2015-03-15
本文作者:大自然保護協會志願者-王康
本文來自雲栖社群合作夥伴DockerOne,了解相關資訊可以關注DockerOne。
原文标題:Dockerfile之優化經驗淺談