用Dockerfile構建docker image
dockerfile是為快速構建docker image而設計的,當你使用docker build 命令的時候,docker 會讀取當前目錄下的命名為Dockerfile(首字母大寫)的純文本文件并執(zhí)行里面的指令構建出一個docker image。
而另一種構建docker iamge 的方法是pull一些基礎鏡像下來啟動成容器,然后進入容器內(nèi)安裝各種需要的程序以及配置好需要的環(huán)境,最后commit成一個鏡像。但是相比之 Dockerfile的方法會更加自動化,更加方便快捷,而且功能也更強大。(Docker build方法底層里也是在基礎鏡像下啟動容器然后commit的,但是這些不需要我們手動去commit以及rm,都是自動化的。)
======(注:以下大部分內(nèi)容均參考自官方文檔)
首先,是關于構建docker image的一些優(yōu)化建議
我們希望構建出的image對應的容器應該是可以在服務群中暫停(解耦性)并且快速替換,這要求容器可以在極短的時間內(nèi)完成啟動并配置運行起來。
優(yōu)化手段:
使用.dockerignore文件 .
dockerignore文件的設計是為了在docker build的過程中排除不需要用到的文件以及目錄,目的是為了docker build這個過程可以盡可能地快速高效以及構建出來的image沒有多余的“垃圾”。
不要安裝不必要的程序包
我們希望構建出來的image盡可以的輕小、依賴性小以及構建過程盡可能地快。這就需要你在構建的時候不要安裝不必要的程序,例如,一個存儲數(shù)據(jù)的數(shù)據(jù)庫容器不需要安裝文本編輯器。
單一容器只運行單一的服務
大部分情況下一個容器只建議運行一個服務,這樣的好處在于:減小耦合度、利于容器復用以及提高容器的橫向可擴展性。如果服務之間是需要聯(lián)系的,就應該把服務放在不現(xiàn)的容器內(nèi),然后用container linking來關聯(lián)這些容器以達到目的。
最小化鏡像層數(shù)(layers)
關于鏡像層數(shù)(layers)的概念請參考:docker鏡像與容器存儲結構分析http://www.programfish.com/blog/?p=9
把鏡像層數(shù)減到最少可能加快容器的啟動速度,但是這里也要權衡另一個問題:dockerfile的可讀性。你可以把一個dockerfile寫得很 復雜以達到構建出最小層數(shù)的鏡像,但同時你的dockerfile可讀性也降低了。所以我們要在鏡像層數(shù)和dockerfile可讀性之間做出讓步與妥 協(xié)。
對多行參數(shù)進行排序(一般按字母順序)
對參數(shù)排序可以方便以后修改更新這些參數(shù)以及確保不會重復重復輸入了某些參數(shù)。例如官方的一個例子是:
- RUN apt-get update && apt-get install -y \
- bzr \
- cvs \
- git \
- mercurial \
- Subversion
把要安裝的程序包名按字母排序可以方便管理。
構建的時候使用cache
Docker build期間docker會按你提供的dockerfile文件里面的指令按順序逐條執(zhí)行。Docker在首先檢查每一條指令的時候會去cache里搜 查是否有執(zhí)行過這條指令并且可以復用的鏡像,如果沒有再去構造一個新的鏡像。這是默認的情況,如果你指定不要這個過程可以在docker build里用如下參數(shù):
- –no-cache=true
Docker查找cache的過程:
首先在cache里從base image(詳見:docker鏡像與容器存儲結構分析http://www.programfish.com/blog/?p=9)起,docker會比較dockerfile里的下一條指令與這個base image的每一個子鏡像的構建指令是否匹配,如果匹配則命中,否則cache標為無效。
對于一般指令這樣簡單比較就足夠了,但是有些精確的指令要求更詳細精確的比較或者說明。 (add和copy指令見官方文檔https://docs.docker.com/articles/dockerfile_best-practices/) 一旦cache在dockerifle里某一條指令檢查時被標為無效,執(zhí)行這個dockerfile以后的指令就不再使用cache。
Dockerfile主要指令簡介:
FROM
dockerfile里的第一條指令,后面跟有效的鏡像名(如果該鏡像你的本地倉庫沒有則會從遠程倉庫Pull?。?。后面的指令在些鏡像中執(zhí)行。
- FROM <image>:<tag>
- MAINTAINER
- MAINTAINER <name> 作者信息
RUN
后跟要執(zhí)行的linux命令,每一條RUN指令(可能會有多條linux命令)會在當前容器最上面的可讀寫層執(zhí)行并且提交成一個新的鏡像層,接下來的指令會在這個新的鏡像層里執(zhí)行。
- RUN <command> (the command is run in a shell – /bin/sh -c – shell form)
- RUN ["executable", "param1", "param2"] (exec form)
注意下面的情況:
不要在一條RUN指令里單一使用apt-get update命令,這樣可能會導致以后的apt-get install 安裝出錯。
避免使用RUN apt-get upgrade 或者dist-upgrade,這樣有些重要的軟件包可能更新失敗,如果你確實想要更新某個包A,使用apt-get install install -y A 。這樣會自動更新這個軟件包。
更多請參考官方文檔。
CMD
CMD指令指定你制作出來的鏡像在啟動成容器時運行命令的默認的參數(shù)。
CMD有三種寫法:
- CMD ["executable","param1","param2"] (exec form, this is the preferred form)
- CMD ["param1","param2"] (as default parameters to ENTRYPOINT)
- CMD command param1 param2 (shell form)
第一種是可執(zhí)行文件加參數(shù),第二種是作為ENTRYPOINT的參數(shù),第三種是作為”/bin/sh -c”的參數(shù)。
這里CMD與ENTRYPOINT的區(qū)別強烈推薦你去看 論docker CMD與ENTRYPOINT的大區(qū)別 http://www.cnblogs.com/programfish/p/4101884.html 這篇文章。看完你就懂了。
ENTRYPOINT
ENTRYPOINT字面意思指定容器的進入點。可以把你的容器制作成類似可執(zhí)行文件的用法。這個指令會覆蓋它前面的CMD指令,而多個 ENTRYPOINT指令只有最后一個生效(后面覆蓋前面)。同時你也可以在在啟動container 的時候指定–entrypoint參數(shù)來覆蓋dockerfile里的ENTRYPOINT。詳見官方文檔。
例如我用了這樣的指令制作鏡像名叫echotest:
- ENTRYPOINT ["/bin/echo"]
然后之后這樣運行:
- docker run -it echotest “this is a echo”
實際上是平時這樣的命令:
- docker run -it echotest /bin/echo “this is a echo”
這樣你應該明白了吧。 這樣一個容器的行為就很類似一個可執(zhí)行文件了。 這里CMD與ENTRYPOINT的區(qū)別強烈推薦你去看 論docker CMD與ENTRYPOINT的大區(qū)別 http://www.cnblogs.com/programfish/p/4101884.html 這篇文章??赐昴憔投恕?/p>
EXPOSE
EXPOSE指定容器對外暴露的端口號。
ENV
指定環(huán)境變量的值,例如你要確保CMD[“nginx”]能成功啟動,你應該用ENV PATH /usr/local/nginx/bin:$PATH設定環(huán)境變量。另外你可以設定另外一些變量用于RUN命令里以便于dockerfile文件的維護:
- ENV PG_MAJOR 9.3
- ENV PG_VERSION 9.3.4
- RUN curl -SL http://example.com/postgres-$PG_VERSION.tar.xz | tar -xJC /usr/src/postgress && …
- ENV PATH /usr/local/postgres-$PG_MAJOR/bin:$PATH
這樣多次出現(xiàn)版本號就可以通過一個變量來管理方便維護。
- VOLUME
- VOLUME ["path"] 創(chuàng)建指定的掛載點。
- WORKDIR
進入指定目錄工作。
其它指令詳情見官方文檔:https://docs.docker.com/reference/builder/
這里引用官方的一個dockerfile例子:
- # Nginx
- #
- # VERSION 0.0.1
- FROM ubuntu
- MAINTAINER Victor Vieux <victor@docker.com>
- RUN apt-get update && apt-get install -y inotify-tools nginx apache2 openssh-server
- # Firefox over VNC
- #
- # VERSION 0.3
- FROM ubuntu
- # Install vnc, xvfb in order to create a ‘fake’ display and firefox
- RUN apt-get update && apt-get install -y x11vnc xvfb firefox
- RUN mkdir ~/.vnc
- # Setup a password
- RUN x11vnc -storepasswd 1234 ~/.vnc/passwd
- # Autostart firefox (might not be the best way, but it does the trick)
- RUN bash -c ‘echo “firefox” >> /.bashrc ‘
- EXPOSE 5900
- CMD ["x11vnc", "-forever" , "-usepw", "-create" ]
- # Multiple images example
- #
- #VERSION 0.1
- FROM ubuntu
- RUN echo foo > bar
- # Will output something like ===> 907ad6c2736f
- FROM ubuntu
- RUN echo moo > oink
- # Will output something like ===> 695d7793cbe4
- # You᾿ll now have two images, 907ad6c2736f with /bar, and 695d7793cbe4 with
- # /oink.
#號為注釋符,這里一個dockerfile構建4個鏡像。
寫好Dockerfile文件后就可以在該目錄下運行docker build . 命令了(可以用 -t 參數(shù)指定tag)。