精簡為王:Docker鏡像體積詳解
不知道大家讀過使用Docker容器的十大誤區(沒讀過的可以戳這個鏈接 http://www.baiduhome.net/lib/view/open1463108483467.html )如果讀過,相信大家已經對跑Docker容器時的幾個注意事項有了大概的了解了。不過文中關于“不要把鏡像體積建得很大”這一點講得不是很詳細,還有這一句:“千萬不要在鏡像中安裝些沒必要的東西,在構建鏡像的時候要避免使用yum這種update命令,免得系統自動下載很多不相干的文件到新鏡像層中”,大家可能還有一些問題。比方說,為什么簡簡單單一句“yum update”命令就會構建出一個超大體積的鏡像?可能很多人都還不清楚其中的原理。為了幫助大家理解這個問題,下面我們就來深入探討一下docker鏡像的構建機制,此外還有一些小技巧,可以幫助大家在不影響鏡像內容更新的條件下,縮減鏡像的體積。
本文用到的基礎鏡像是最新版的 Fedora 23(用RHEL也可以),大家可以用 docker pull fedora:23 命令來pull這個鏡像。
Pull下來之后運行 docker images ,可以看到鏡像大小是204.7MB。然后我們用從GitHub下載的Dockerfile來創建一個包含JDK 1.8好WildFly 9.0.2.Final的自定義鏡像,大家可以自己去GitHub下載Dockerfile。
鏡像創建完以后體積已經達到了567.3 MB,大家可以運行一下“docker history <鏡像名>”,查看每個層的大小。我的鏡像里面,JDK 1.8有203.5MB,WildFly有159.1 MB,體積相當大。
這還不算完,創建這個鏡像之后,我們最終肯定是要升級到WildFly 10.0.0.Final版的,如果不想重新安裝 JDK 1.8的話,很多人都會選擇在現有鏡像的基礎上,用10.0.0.Final替換WildFly 9.0.2.Final。如果你以為升級下來鏡像大小還是567 MB的話,那就大錯特錯了,實際上升級后的鏡像體積是728.1 MB。就算你在往鏡像中添加WildFly 10.0.0.Final之前先刪除掉WildFly 9.0.2.Final,最后得到的鏡像還是要比原先大160MB。文件體積的增大并不是因為WildFly版本之間的差異造成的。
寫入時拷貝
那么為什么鏡像文件會增大呢?原因就在于Docker容器的文件系統采用了寫入時拷貝技術,這項技術的作用是加快容器的啟動時間,跟一般的虛擬機相比,容器的啟動速度確實非常快。雖然該技術在很大程度上提升了docker容器的運行效率,但是也會增加磁盤讀寫方面的開銷。所以,為了避免文件體積過大,有幾個問題需要我們在構建鏡像的時候注意一下。
這里要先說一下,每次在Dockerfile中執行RUN命令的時候系統都會在鏡像中新建一個層,每個鏡像層都會占用一定的磁盤空間,所以,在upgrade WildFly的時候我們其實是創建了一個新的層。因此為了盡量減少鏡像的層數,最好把移動、提取、刪除等所有文件操作都寫在同一行RUN命令下。
大家可以參照下面的命令行寫法,這個命令把WildFly 10.0.0.Final的安裝、下載、提取、移動和清理命令都寫在一行里,這樣創建下來的鏡像體積就只有569.1 MB:
"cd /tmp && curl -O https://download.jboss.org/wildfly/ $WILDFLY_VERSION/wildfly-$WILDFLY_VERSION.tar.gz && tar xf wildfly-$WILDFLY_VERSION.tar.gz && mv /tmp/wildfly-$WILDFLY_VERSION /opt/jboss/wildfly && rm /tmp/wildfly-$WILDFLY_VERSION.tar.gz"
如果我們每寫一條命令都執行一次RUN的話,鏡像就會多出4個層,最后得到的鏡像大小將是867.1 MB。如下圖所示,我們可以用“docker history”命令來查看鏡像的層以及每個層的大小:
Yum Update
那么問題來了,“yum update”到底是做什么用的呢?為了幫助大家更好地理解“yum update”的作用,我建了fedora:22和fedora:23兩個鏡像。這兩個自定義鏡像都是用 RUN dnf -y update 命令從Dockerfile創建的,fedora:22 的大小是531.2 MB,fedora:23是358.2 MB。
在這個命令下,Fedora:22會自動更新到跟Fedora 23一樣的版本,因此會下載安裝很多文件,最后的體積也要大些。所以在構建鏡像的時候,如果能用現有平臺上最新的基礎鏡像(比如說構建的時候用Fedora 23,不要用Fedora 22)來建的話,就可以避免往中間層寫入很多額外的文件,這樣做不僅能加快鏡像創建速度,還可以縮減鏡像大小。
總之我想說的就是,執行“updates”命令的時候會下載一些新的rpm包,所以對鏡像文件的改動還是蠻大的。雖然之前的鏡像層無法修改,但rpm cache是可以清理的。我們只需要以寫“RUN dnf -y update && dnf clean all”命令就可以清理cache了,執行該命令后,鏡像大小從358.2 MB變到了216.2 MB,之比原來的fedora:23鏡像大11.5MB。這個辦法很好用,大家在執行update之后,一定要記得做清理,這樣就能最大限度地節省空間。
綜上,“yum/dnf update”命令本身并沒有什么問題,導致鏡像文件過大的原因主要還是因為我們對docker的分層式文件系統不太了解,很多人都不知道這種文件系統會對鏡像體積造成很大的影響。如果我們只有幾個容器的話,這些都還算可以忍受,但如果是在集群里面部署大量容器的話,鏡像體積過大就會很麻煩,但是我們又不可能完全不使用update命令,不然我們的linux容器就會產生bug或者安全漏洞等等問題。
最好的解決辦法就是把update和清理命令都放在同一行RUN底下,在執行update的同時釋放多余的空間,這樣我們就可以有效地縮減鏡像體積。
via: http://dockone.io/article/1302