Dockerfile之優化經驗淺談
本文主要講述如何優化Dockerfile,來縮短docker鏡像構建需要的時間,以及Dockerfile的一些編輯規范,推薦所有的Docker愛好者閱讀,非常基礎的文章,本文也許會給你一些啟發和指導。
優化您的Dockerfiles
Docker鏡像應該是小而快的。然而,假設你在BusyBox鏡像中預編譯GO二進制文件,他們就會變得又大又復雜。如果不能構建一個良好的Dockerfile來幫助你提高構建緩存命中率,那么你的鏡像構建過程將會變得相當的緩慢。比如一個用于軟件安裝的bash腳本,里面堆砌著大量的curl、wget等命令語句,大家在寫Dockerfile的時候通常就會像寫這個bash腳本一樣,將一系列的Docker命令堆砌在其中,這種Dockerfile在構建鏡像的時候是比較低效和緩慢的。
秩序
當你正在為一個應用程序構建一個新的Dockerfile,在決定需要引哪些包、運行什么命令的時候肯定會進行很多次嘗試,也會遇到很多的問題。優化你的Dockerfile確保命中“構建緩存”的概率越來越大,這樣之后的每一次構建中會比前一次要更快一些。一般的規律是有頻率的改變Dockerfile中命令的排序,觀察分析運行命令所耗費的時間及與其他鏡像共享資源的方式。
這就意味著像WORKDIR、CMD、ENV這些命令應該在底部,然而一個RUN apt-get -y update更新應該在上面,因為它需要更長時間來運行,也可以與你所有的鏡像共享。
最后任何ADD(或其它緩存失效的命令)命令應該盡可能地在Dockerfile底部,在那里你有可能做出很多改變,然后后續命令緩存失效。
明智地選擇你的基礎鏡像
在如Ubuntu這樣的操作系統鏡像和Python或Java7中一個特定的應用程序中,有很多基礎鏡像可供選擇。常識告訴你使用Ruby2來運行基于Ruby應用程序并且使用Python3運行Python應用程序。但是現在你有兩個幾乎沒有共同之處的基礎鏡像,所以你需要下載和構建。相反,如果你使用Ubuntu運行這兩個程序,你只需要下載一次基礎鏡像。將層作為你的優勢
在一個Dockerfile中每個命令都會在原來的基礎上生成一層鏡像。你可以很快的在三十多層的時候就結束了,這未必是一個問題,但也可以通過組合RUN命令,并使用一行EXPOSE命令列出你所有的開放端口,這樣可以有效減少鏡像的層數。通過將RUN命令分組,可以在容器間分享更多的層。當然如果你有一組命令可以多個容器通用,那么你應該創建一個獨立的基礎鏡像,它包含你建立的所有鏡像。
對于每一層來說你都可以跨多個鏡像分享,這樣可以節省大量的磁盤空間。
容器的體積
在創建容器并考慮到體積問題的時候,不要為了節省空間去使用體積小的鏡像,盡量使用你將要提供數據的應用程序打成的鏡像。如果你這樣做了,并且提交了磁盤數據,你不僅在容器中儲存了你的數據,而且對實際應用程序的調試也非常有用。消耗
當你已經構建了一個鏡像,在運行它的時候發現有一個package缺少了,把它添加到Dockerfile的底部,而不是添加到頂部的run apt-get命令那里。這意味著你能盡快的重新構建這個鏡像了。一旦你的鏡像可以正常工作,你可以再提交源碼之前重新優化整理Dockerfile。案例
如果一個Dockerfile是由類似于一個bash腳本寫出來的,那么它可能會是這樣的:FROM ubuntu:trusty MAINTAINER Paul Czarkowski "paul@paulcz.net"RUN apt-get -yq update
Apache
RUN \ apt-get -yqq install \ apache2 \ apache2-utils \ libapache2-mod-python \ python-dev \ python-pip \ python-cairo \ python-pysqlite2 \ python-mysqldb \ python-jinja2 sqlite3 \ curl \ wget \ git \ software-properties-common
RUN \ curl -sSL https://bootstrap.pypa.io/get-pip.py | python && \ pip install whisper \ carbon \ graphite-web \ 'Twisted<12.0' \ 'django<1.6' \ django-tagging
Add start scripts etc
ADD . /app
RUN mkdir -p /app/wsgi RUN useradd -d /app -c 'application' -s '/bin/false' graphite RUN chmod +x /app/bin/ RUN chown -R graphite:graphite /app RUN chown -R graphite:graphite /opt/graphite RUN rm -f /etc/apache2/sites-enabled/
ADD ./apache-graphite.conf /etc/apache2/sites-enabled/apache-graphite.conf
Expose ports.
EXPOSE 80 EXPOSE 2003 EXPOSE 2004 EXPOSE 7002
ENV APACHE_CONFDIR /etc/apache2 ENV APACHE_ENVVARS $APACHE_CONFDIR/envvars ENV APACHE_RUN_USER www-data ENV APACHE_RUN_GROUP www-data ENV APACHE_RUN_DIR /var/run/apache2 ENV APACHE_PID_FILE $APACHE_RUN_DIR/apache2.pid ENV APACHE_LOCK_DIR /var/lock/apache2 ENV APACHE_LOG_DIR /var/log/apache2
WORKDIR /app
Define default command.
CMD ["/app/bin/start_graphite"]</pre>
然而這個Dockerfile的優化版本是基于之前所討論的內容的,它看起來是這樣的:# 1 - Common Header / Packages FROM ubuntu:trusty MAINTAINER Paul Czarkowski "paul@paulcz.net"RUN apt-get -yq update \ && apt-get -yqq install \ wget \ curl \ git \ software-properties-common
2 - Python
RUN \ apt-get -yqq install \ python-dev \ python-pip \ python-pysqlite2 \ python-mysqldb
3 - Apache
RUN \ apt-get -yqq install \ apache2 \ apache2-utils
4 - Apache ENVs
ENV APACHE_CONFDIR /etc/apache2 ENV APACHE_ENVVARS $APACHE_CONFDIR/envvars ENV APACHE_RUN_USER www-data ENV APACHE_RUN_GROUP www-data ENV APACHE_RUN_DIR /var/run/apache2 ENV APACHE_PID_FILE $APACHE_RUN_DIR/apache2.pid ENV APACHE_LOCK_DIR /var/lock/apache2 ENV APACHE_LOG_DIR /var/log/apache2
5 - Graphite and Deps
RUN \ apt-get -yqq install \ libapache2-mod-python \ python-cairo \ python-jinja2 \ sqlite3
RUN \ pip install whisper \ carbon \ graphite-web \ 'Twisted<12.0' \ 'django<1.6' \ django-tagging
6 - Other
EXPOSE 80 2003 2004 7002
WORKDIR /app
VOLUME /opt/graphite/data
Define default command.
CMD ["/app/bin/start_graphite"]
7 - First use of ADD
ADD . /app
8 - Final setup
RUN mkdir -p /app/wsgi \ && useradd -d /app -c 'application' -s '/bin/false' graphite \ && chmod +x /app/bin/ \ && chown -R graphite:graphite /app \ && chown -R graphite:graphite /opt/graphite \ && rm -f /etc/apache2/sites-enabled/ \ && mv /app/apache-graphite.conf /etc/apache2/sites-enabled/apache-graphite.conf</pre>
1 - Common Header / Packages
這是最常見的共享層,在同一個主機上運行所有鏡像應該從它開始。你可以看到我已經添加了一些諸如curl和git的操作,他們不是必須的,但是對調試很有用。而且因為他們在分享層,所以它們不會占用太多空間。
2 - Python, 3 - Apache
現在說一下我們的語言規范。在這里我已經包含了python和apache的部分,因為到底把誰放在第一位并不十分清楚。如果我們把apache放在第一位,我們可以獲得一個包含層和免費得到apache的ruby應用程序。
4 - Apache Envs
我把這些單獨說出來是為了以下這些原因。
首先,鏡像中添加Apache部分之后直接構建其他需要的部分模塊,以便于在構建多個鏡像的過程中盡量多應用公共緩存。你也許認為這并不重要,因為類似EVN的調用是很便宜的,但是我見到過隨機的ENV調用耗費了10秒鐘甚至更多時間。
有一個很好的例子:你可能想要在容器的底部啟動,但這些命令不能被改變的,那么最好把他們移到略微靠前一點的地方。
其次,我真希望Docker能夠在同一行指定多個環境,這樣可以減少層數,最終提供了一種最簡化的構建方式。
5 - Graphite and Deps
這包含了一些特定的apt和pip等資源包。你可以在一個單一的命令中加入他們,利用&&符號最為分隔符,如果需要修改只需要修改這條組合命令即可。
6 - Other
這包含了一大堆簡易的命令,如ADD和VOLUME,比起以前安裝的包,他們更不可能改變,但運行的效率也不慢,所以在這些命令的緩存失效以后就會變得并不那么重要了。
所以建議把類似的這些命令放在Dockerfile的底部。
7 - First ADD
你應該在最后面使用ADD命令。
8 - Final setup
將這些類似ADD命令的操作放入最后一層。
最后
希望這篇文章能夠幫你編寫一個更好的Dockerfile文件。這些都是我在構建我自己的鏡像的時候所經歷過的,雖然他們可能并不適用于所有情況(或可能是錯誤的),但他們確實提高了我的開發經驗。
原文鏈接:Optimizing your Dockerfiles (翻譯:王康 翻譯:李穎杰)
來自:http://dockerone.com/article/255