Dockerfile之優化經驗淺談

n6xb 9年前發布 | 22K 次閱讀 Docker 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

 本文由用戶 n6xb 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!