RUN vs CMD vs ENTRYPOINT - 每天5分鐘玩轉 Docker 容器技術(17)

weixin_33766168發表於2017-11-13

RUN、CMD 和 ENTRYPOINT 這三個 Dockerfile 指令看上去很類似很容易混淆。本節將通過實踐詳細討論它們的區別。

簡單的說

  1. RUN 執行命令並建立新的映象層RUN 經常用於安裝軟體包。

  2. CMD 設定容器啟動後預設執行的命令及其引數但 CMD 能夠被 docker run 後面跟的命令列引數替換。

  3. ENTRYPOINT 配置容器啟動時執行的命令。

下面我們詳細分析。

Shell 和 Exec 格式

我們可用兩種方式指定 RUN、CMD 和 ENTRYPOINT 要執行的命令Shell 格式和 Exec 格式二者在使用上有細微的區別。

Shell 格式

<instruction> <command>


例如

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 有兩種格式

  1. Shell 格式RUN

  2. 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 沒有指定其他命令時執行。

  1. 如果 docker run 指定了其他命令CMD 指定的預設命令將被忽略。

  2. 如果 Dockerfile 中有多個 CMD 指令只有最後一個 CMD 有效。

CMD 有三種格式

  1. Exec 格式CMD ["executable","param1","param2"]
    這是 CMD 的推薦格式。

  2. CMD ["param1","param2"] 為 ENTRYPOINT 提供額外的引數此時 ENTRYPOINT 必須使用 Exec 格式。

  3. 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/bashCMD 會被忽略掉命令 bash 將被執行

root@10a32dc7d3d3:/#

 

ENTRYPOINT

ENTRYPOINT 指令可讓容器以應用程式或者服務的形式執行。

ENTRYPOINT 看上去與 CMD 很像它們都可以指定要執行的命令及其引數。不同的地方在於 ENTRYPOINT 不會被忽略一定會被執行即使執行 docker run 時指定了其他命令。

ENTRYPOINT 有兩種格式

  1. Exec 格式ENTRYPOINT ["executable", "param1", "param2"] 這是 ENTRYPOINT 的推薦格式。

  2. 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 提供的引數。

最佳實踐

  1. 使用 RUN 指令安裝應用和軟體包構建映象。

  2. 如果 Docker 映象的用途是執行應用程式或服務比如執行一個 MySQL應該優先使用 Exec 格式的 ENTRYPOINT 指令。CMD 可為 ENTRYPOINT 提供額外的預設引數同時可利用 docker run 命令列替換預設引數。

  3. 如果想為容器設定預設的啟動命令可使用 CMD 指令。使用者可在 docker run 命令列中替換此預設命令。

到這裡我們已經具備編寫 Dockerfile 的能力了。如果大家還覺得沒把握推薦一個快速掌握 Dockerfile 的方法去 Docker Hub 上參考那些官方映象的 Dockerfile

好了我們已經學習完如何建立自己的 image下一節討論如何分發 image。

二維碼+指紋.png



本文轉自CloudMan6 51CTO部落格,原文連結:http://blog.51cto.com/cloudman/1927363

相關文章