RUN、CMD 和 ENTRYPOINT 這三個 Dockerfile 指令看上去很相似,很容易混淆。本節將經過實踐詳細討論它們的區別。python
簡單的說:git
RUN 執行命令並建立新的鏡像層,RUN 常常用於安裝軟件包。docker
CMD 設置容器啓動後默認執行的命令及其參數,但 CMD 可以被 docker run
後面跟的命令行參數替換。shell
ENTRYPOINT 配置容器啓動時運行的命令。緩存
下面咱們詳細分析。bash
Shell 和 Exec 格式學習
咱們可用兩種方式指定 RUN、CMD 和 ENTRYPOINT 要運行的命令:Shell 格式和 Exec 格式,兩者在使用上有細微的區別。命令行
Shell 格式3d
<instruction> <command>code
例如:
RUN apt-get install python3
CMD echo "Hello world"
ENTRYPOINT echo "Hello world"
當指令執行時,shell 格式底層會調用 /bin/sh -c <command> 。
例以下面的 Dockerfile 片斷:
ENV name Cloud Man
ENTRYPOINT echo "Hello, $name"
執行 docker run <image> 將輸出:
Hello, Cloud Man
注意環境變量 name
已經被值 Cloud Man
替換。
下面來看 Exec 格式。
Exec 格式
<instruction> ["executable", "param1", "param2", ...]
例如:
RUN ["apt-get", "install", "python3"]
CMD ["/bin/echo", "Hello world"]
ENTRYPOINT ["/bin/echo", "Hello world"]
當指令執行時,會直接調用 <command>,不會被 shell 解析。
例以下面的 Dockerfile 片斷:
ENV name Cloud Man
ENTRYPOINT ["/bin/echo", "Hello, $name"]
運行容器將輸出:
Hello, $name
注意環境變量「name」沒有被替換。
若是但願使用環境變量,照以下修改
ENV name Cloud Man
ENTRYPOINT ["/bin/sh", "-c", "echo Hello, $name"]
運行容器將輸出:
Hello, Cloud Man
CMD 和 ENTRYPOINT 推薦使用 Exec 格式,由於指令可讀性更強,更容易理解。RUN 則兩種格式均可以。
RUN
RUN 指令一般用於安裝應用和軟件包。
RUN 在當前鏡像的頂部執行命令,並經過建立新的鏡像層。Dockerfile 中經常包含多個 RUN 指令。
RUN 有兩種格式:
Shell 格式:RUN
Exec 格式:RUN ["executable", "param1", "param2"]
下面是使用 RUN 安裝多個包的例子:
RUN apt-get update && apt-get install -y \
bzr \
cvs \
git \
mercurial \
subversion
注意:apt-get update 和 apt-get install 被放在一個 RUN 指令中執行,這樣可以保證每次安裝的是最新的包。若是 apt-get install 在單獨的 RUN 中執行,則會使用 apt-get update 建立的鏡像層,而這一層多是好久之前緩存的。
CMD
CMD 指令容許用戶指定容器的默認執行的命令。
此命令會在容器啓動且 docker run 沒有指定其餘命令時運行。
若是 docker run 指定了其餘命令,CMD 指定的默認命令將被忽略。
若是 Dockerfile 中有多個 CMD 指令,只有最後一個 CMD 有效。
CMD 有三種格式:
Exec 格式:CMD ["executable","param1","param2"]
這是 CMD 的推薦格式。
CMD ["param1","param2"] 爲 ENTRYPOINT 提供額外的參數,此時 ENTRYPOINT 必須使用 Exec 格式。
Shell 格式:CMD command param1 param2
Exec 和 Shell 格式前面已經介紹過了。
第二種格式 CMD ["param1","param2"] 要與 Exec 格式 的 ENTRYPOINT 指令配合使用,其用途是爲 ENTRYPOINT 設置默認的參數。咱們將在後面討論 ENTRYPOINT 時舉例說明。
下面看看 CMD 是如何工做的。Dockerfile 片斷以下:
CMD echo "Hello world"
運行容器 docker run -it [image] 將輸出:
Hello world
但當後面加上一個命令,好比 docker run -it [image] /bin/bash,CMD 會被忽略掉,命令 bash 將被執行:
root@10a32dc7d3d3:/#
ENTRYPOINT
ENTRYPOINT 指令可以讓容器以應用程序或者服務的形式運行。
ENTRYPOINT 看上去與 CMD 很像,它們均可以指定要執行的命令及其參數。不一樣的地方在於 ENTRYPOINT 不會被忽略,必定會被執行,即便運行 docker run 時指定了其餘命令。
ENTRYPOINT 有兩種格式:
Exec 格式:ENTRYPOINT ["executable", "param1", "param2"] 這是 ENTRYPOINT 的推薦格式。
Shell 格式:ENTRYPOINT command param1 param2
在爲 ENTRYPOINT 選擇格式時必須當心,由於這兩種格式的效果差異很大。
Exec 格式
ENTRYPOINT 的 Exec 格式用於設置要執行的命令及其參數,同時可經過 CMD 提供額外的參數。
ENTRYPOINT 中的參數始終會被使用,而 CMD 的額外參數能夠在容器啓動時動態替換掉。
好比下面的 Dockerfile 片斷:
ENTRYPOINT ["/bin/echo", "Hello"]
CMD ["world"]
當容器經過 docker run -it [image] 啓動時,輸出爲:
Hello world
而若是經過 docker run -it [image] CloudMan 啓動,則輸出爲:
Hello CloudMan
Shell 格式
ENTRYPOINT 的 Shell 格式會忽略任何 CMD 或 docker run 提供的參數。
最佳實踐
使用 RUN 指令安裝應用和軟件包,構建鏡像。
若是 Docker 鏡像的用途是運行應用程序或服務,好比運行一個 MySQL,應該優先使用 Exec 格式的 ENTRYPOINT 指令。CMD 可爲 ENTRYPOINT 提供額外的默認參數,同時可利用 docker run 命令行替換默認參數。
若是想爲容器設置默認的啓動命令,可以使用 CMD 指令。用戶可在 docker run 命令行中替換此默認命令。
到這裏,咱們已經具有編寫 Dockerfile 的能力了。若是你們還以爲沒把握,推薦一個快速掌握 Dockerfile 的方法:去 Docker Hub 上參考那些官方鏡像的 Dockerfile。
好了,咱們已經學習完如何建立本身的 image,下一節討論如何分發 image。