簡體   English   中英

CMD 和 Dockerfile 中的 ENTRYPOINT 有什么區別?

[英]What is the difference between CMD and ENTRYPOINT in a Dockerfile?

在 Dockerfiles 中有兩個和我類似的命令: CMDENTRYPOINT 但我猜想它們之間存在(細微的?)差異——否則對於同一件事有兩個命令是沒有任何意義的。

CMD的文檔狀態 -

CMD 的主要目的是為執行容器提供默認值。

對於ENTRYPOINT

ENTRYPOINT 可幫助您配置可以作為可執行文件運行的容器。

那么,這兩個命令有什么區別呢?

Docker 有一個默認入口點,即/bin/sh -c但沒有默認命令。

當你像這樣運行 docker 時: docker run -i -t ubuntu bash入口點是默認的/bin/sh -c ,圖像是ubuntu並且命令是bash

該命令通過入口點運行。 即,實際執行的是/bin/sh -c bash 這使得 Docker 能夠依靠 shell 的解析器快速實現RUN

后來,人們要求能夠自定義這個,所以引入了ENTRYPOINT--entrypoint

圖像名稱之后的所有內容,如上例中的ubuntu ,都是命令並被傳遞到入口點。 使用CMD指令時,就好像你在執行
docker run -i -t ubuntu <cmd>
入口點的參數是<cmd>

如果您改為鍵入此命令docker run -i -t ubuntu ,您也會得到相同的結果:bash shell 將在容器中啟動,因為在ubuntu Dockerfile中指定了默認CMD
CMD ["bash"]

當一切都傳遞到入口點時,您可以從圖像中獲得非常好的行為。 @Jiri 示例很好,它展示了如何將圖像用作“二進制”。 當使用["/bin/cat"]作為入口點,然后執行docker run img /etc/passwd時,你明白了, /etc/passwd是命令並被傳遞到入口點,所以最終結果執行只是/bin/cat /etc/passwd

另一個示例是將任何 cli 作為入口點。 例如,如果你有一個 redis 映像,而不是docker run redisimg redis -H something -u toto get key ,你可以簡單地使用ENTRYPOINT ["redis", "-H", "something", "-u", "toto"]然后像這樣運行以獲得相同的結果: docker run redisimg get key

ENTRYPOINT指定了在容器啟動時始終執行的命令。

CMD指定將提供給ENTRYPOINT的參數。

如果要制作專用於特定命令的圖像,您將使用ENTRYPOINT ["/path/dedicated_command"]

否則,如果您想為通用目的制作圖像,您可以不指定ENTRYPOINT並使用CMD ["/path/dedicated_command"]因為您將能夠通過向docker run提供參數來覆蓋設置。

例如,如果您的 Dockerfile 是:

FROM debian:wheezy
ENTRYPOINT ["/bin/ping"]
CMD ["localhost"]

在沒有任何參數的情況下運行圖像將 ping 本地主機:

$ docker run -it test
PING localhost (127.0.0.1): 48 data bytes
56 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.096 ms
56 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.088 ms
56 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.088 ms
^C--- localhost ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max/stddev = 0.088/0.091/0.096/0.000 ms

現在,使用參數運行圖像將 ping 參數:

$ docker run -it test google.com
PING google.com (173.194.45.70): 48 data bytes
56 bytes from 173.194.45.70: icmp_seq=0 ttl=55 time=32.583 ms
56 bytes from 173.194.45.70: icmp_seq=2 ttl=55 time=30.327 ms
56 bytes from 173.194.45.70: icmp_seq=4 ttl=55 time=46.379 ms
^C--- google.com ping statistics ---
5 packets transmitted, 3 packets received, 40% packet loss
round-trip min/avg/max/stddev = 30.327/36.430/46.379/7.095 ms

作為比較,如果您的 Dockerfile 是:

FROM debian:wheezy
CMD ["/bin/ping", "localhost"]

在沒有任何參數的情況下運行圖像將 ping 本地主機:

$ docker run -it test
PING localhost (127.0.0.1): 48 data bytes
56 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.076 ms
56 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.087 ms
56 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.090 ms
^C--- localhost ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max/stddev = 0.076/0.084/0.090/0.000 ms

但是使用參數運行圖像將運行參數:

docker run -it test bash
root@e8bb7249b843:/#

有關更多詳細信息,請參閱 Brian DeHamer 的這篇文章: https ://www.ctl.io/developers/blog/post/dockerfile-entrypoint-vs-cmd/

根據docker docs

CMD 和 ENTRYPOINT 指令都定義了運行容器時執行的命令。 很少有規則描述他們的合作。

  1. Dockerfile 應至少指定CMDENTRYPOINT命令之一。
  2. 將容器用作可執行文件時應定義ENTRYPOINT
  3. CMD應該用作為ENTRYPOINT命令或在容器中執行臨時命令定義默認參數的一種方式。
  4. 當使用替代參數運行容器時, CMD將被覆蓋。

下表顯示了針對不同ENTRYPOINT / CMD組合執行的命令

-- No ENTRYPOINT

╔════════════════════════════╦═════════════════════════════╗
║ No CMD                     ║ error, not allowed          ║
╟────────────────────────────╫─────────────────────────────╢
║ CMD ["exec_cmd", "p1_cmd"] ║ exec_cmd p1_cmd             ║
╟────────────────────────────╫─────────────────────────────╢
║ CMD ["p1_cmd", "p2_cmd"]   ║ p1_cmd p2_cmd               ║
╟────────────────────────────╫─────────────────────────────╢
║ CMD exec_cmd p1_cmd        ║ /bin/sh -c exec_cmd p1_cmd  ║
╚════════════════════════════╩═════════════════════════════╝

-- ENTRYPOINT exec_entry p1_entry

╔════════════════════════════╦══════════════════════════════════╗
║ No CMD                     ║ /bin/sh -c exec_entry p1_entry   ║
╟────────────────────────────╫──────────────────────────────────╢
║ CMD ["exec_cmd", "p1_cmd"] ║ /bin/sh -c exec_entry p1_entry   ║
╟────────────────────────────╫──────────────────────────────────╢
║ CMD ["p1_cmd", "p2_cmd"]   ║ /bin/sh -c exec_entry p1_entry   ║
╟────────────────────────────╫──────────────────────────────────╢
║ CMD exec_cmd p1_cmd        ║ /bin/sh -c exec_entry p1_entry   ║
╚════════════════════════════╩══════════════════════════════════╝

-- ENTRYPOINT ["exec_entry", "p1_entry"]

╔════════════════════════════╦═════════════════════════════════════════════════╗
║ No CMD                     ║ exec_entry p1_entry                             ║
╟────────────────────────────╫─────────────────────────────────────────────────╢
║ CMD ["exec_cmd", "p1_cmd"] ║ exec_entry p1_entry exec_cmd p1_cmd             ║
╟────────────────────────────╫─────────────────────────────────────────────────╢
║ CMD ["p1_cmd", "p2_cmd"]   ║ exec_entry p1_entry p1_cmd p2_cmd               ║
╟────────────────────────────╫─────────────────────────────────────────────────╢
║ CMD exec_cmd p1_cmd        ║ exec_entry p1_entry /bin/sh -c exec_cmd p1_cmd  ║
╚════════════════════════════╩═════════════════════════════════════════════════╝

是的,這是個好問題。 我還沒有完全理解,但是:

我知道ENTRYPOINT是正在執行的二進制文件。 您可以通過 --entrypoint="" 覆蓋入口點。

docker run -t -i --entrypoint="/bin/bash" ubuntu

CMD 是容器的默認參數。 沒有入口點,默認參數是執行的命令。 使用入口點, cmd 作為參數傳遞給入口點。 您可以使用入口點模擬命令。

# no entrypoint
docker run ubuntu /bin/cat /etc/passwd

# with entry point, emulating cat command
docker run --entrypoint="/bin/cat" ubuntu /etc/passwd

因此,主要優點是使用入口點可以將參數 (cmd) 傳遞給容器。 為此,您需要同時使用:

# Dockerfile
FROM ubuntu
ENTRYPOINT ["/bin/cat"]

docker build -t=cat .

那么你可以使用:

docker run cat /etc/passwd
#              ^^^^^^^^^^^
#                   CMD
#          ^^^      
#          image (tag)- using the default ENTRYPOINT

簡而言之:

  • CMD 設置默認命令和/或參數,可以在 docker 容器運行時從命令行覆蓋。
  • ENTRYPOINT 命令和參數不會被命令行覆蓋。 相反,所有命令行參數都將添加到 ENTRYPOINT 參數之后。

如果您需要更多詳細信息或想查看示例的不同之處,有一篇博文將 CMD 和 ENTRYPOINT 與大量示例進行了全面比較 - https://codewithyury.com/docker-run-vs-cmd-vs-entrypoint/

直覺上 CMD 和 ENTRYPOINT 的區別:

  • ENTRYPOINT:容器啟動時運行的命令。
  • CMD:容器啟動時運行的命令或 ENTRYPOINT 的參數(如果指定)。

是的,這很混亂。

您可以在運行 docker run 時覆蓋其中的任何一個。

通過示例CMD 和 ENTRYPOINT 之間的區別:

docker run -it --rm yourcontainer /bin/bash            <-- /bin/bash overrides CMD
                                                       <-- /bin/bash does not override ENTRYPOINT
docker run -it --rm --entrypoint ls yourcontainer      <-- overrides ENTRYPOINT with ls
docker run -it --rm --entrypoint ls yourcontainer  -la  <-- overrides ENTRYPOINT with ls and overrides CMD with -la

更多關於CMDENTRYPOINT之間的區別:

docker run的參數(例如 /bin/bash)會覆蓋我們在 Dockerfile 中編寫的任何 CMD 命令。

ENTRYPOINT 不能在運行時被 docker docker run [args]等普通命令覆蓋。 docker docker run [args] args的 args 作為 ENTRYPOINT 的參數提供。 通過這種方式,我們可以創建一個類似於普通二進制文件的container ,例如ls

所以 CMD 可以作為 ENTRYPOINT 的默認參數,然后我們可以從 [args] 覆蓋 CMD args。

ENTRYPOINT 可以用--entrypoint覆蓋。

我將添加我的答案作為示例1 ,它可能會幫助您更好地理解差異。

假設我們要創建一個在啟動時始終運行睡眠命令的圖像。 我們將創建自己的圖像並指定一個新命令:

FROM ubuntu
CMD sleep 10

構建圖像:

docker build -t custom_sleep .
docker run custom_sleep
# sleeps for 10 seconds and exits

如果我們想改變秒數怎么辦? 我們將不得不更改Dockerfile ,因為該值是硬編碼的,或者通過提供不同的命令來覆蓋命令:

docker run custom_sleep sleep 20

雖然這可行,但這不是一個好的解決方案,因為我們有一個多余的“睡眠”命令。 為什么要冗余? 因為容器的唯一目的是sleep ,所以必須顯式指定sleep命令有點尷尬。

現在讓我們嘗試使用ENTRYPOINT指令:

FROM ubuntu
ENTRYPOINT sleep

該指令指定容器啟動時將運行的程序

現在我們可以運行:

docker run custom_sleep 20

默認值呢? 嗯,你猜對了:

FROM ubuntu
ENTRYPOINT ["sleep"]
CMD ["10"]

ENTRYPOINT是將要運行的程序,傳遞給容器的值將附加到它上面。

可以通過指定ENTRYPOINT標志來覆蓋--entrypoint ,然后指定要使用的新入口點。

不是我的,我曾經看過一個提供這個例子的教程

有一些很好的答案。 我想通過每個文檔的演示來解釋它

  • CMD定義容器的默認命令和/或參數。 如果您需要用戶可以輕松覆蓋的默認命令,CMD 是最好使用的指令。 如果一個 Dockerfile 有多個 CMD,它只應用最后一個的指令。
  • 當您要定義具有特定可執行文件的容器時,首選ENTRYPOINT

除非添加--entrypoint標志,否則在啟動容器時不能覆蓋ENTRYPOINT

  1. 命令

碼頭工人文件

  FROM centos:8.1.1911

  CMD ["echo", "Hello Docker"]

運行結果

$ sudo docker run <image-id>
Hello Docker
$ sudo docker run <image-id> hostname   # hostname is exec to override CMD
244be5006f32
  1. 入口點

碼頭工人文件

  FROM centos:8.1.1911

  ENTRYPOINT ["echo", "Hello Docker"]

運行結果

$ sudo docker run <image-id>
Hello Docker
$ sudo docker run <image-id> hostname   # hostname as parameter to exec
Hello Docker hostname
  1. 在很多情況下,將 CMD 和 ENTRYPOINT 結合起來是 Docker 容器的最佳解決方案。 在這種情況下,可執行文件使用 ENTRYPOINT 定義,而 CMD 指定默認參數。

碼頭工人文件

  FROM centos:8.1.1911

  ENTRYPOINT ["echo", "Hello"]
  CMD ["Docker"]

運行結果

$ sudo docker run <image-id>
Hello Docker
$ sudo docker run <image-id> Ben
Hello Ben

公認的答案在解釋歷史方面非常出色。 我發現這張表從官方文檔中很好地解釋了“CMD 和 ENTRYPOINT 如何交互” 在此處輸入圖像描述

我遇到了這個問題,一開始我發現說實話真的很混亂,我認為這種混亂來自使用“CMD”這個詞,因為實際上那里的內容充當了參數 所以在挖掘了一點之后,我明白了它是如何工作的。 基本上:

ENTRYPOINT --> 您在此處指定的是容器啟動時要執行的命令。 如果你省略這個定義,docker 將使用/bin/sh -c bash來運行你的容器。

CMD --> 這些是附加到 ENTRYPOINT的參數,除非用戶指定了一些自定義參數,即: docker run ubuntu <custom_cmd>在這種情況下,而不是附加在 CMD 部分中圖像上指定的內容,docker 將運行ENTRYPOINT <custom_cmd> 如果沒有指定 ENTRYPOINT,這里的內容將傳遞給/bin/sh -c實際上作為啟動容器時要執行的命令。

作為一切,最好通過示例來解釋發生了什么。 因此,假設我使用以下規范Dockerfile創建了一個簡單的 docker 映像:

From ubuntu
ENTRYPOINT ["sleep"]

然后我通過運行以下命令來構建它:

docker build . -t testimg

這將創建一個容器,每次運行時它都會休眠。 因此,如果我按以下方式運行它:

docker run testimg

我會得到以下信息:

sleep: missing operand
Try 'sleep --help' for more information.

發生這種情況是因為入口點是需要參數的“睡眠”命令。 所以要解決這個問題,我只提供睡眠量:

docker run testimg 5

這將正確運行,因此容器將運行,休眠 5 秒並退出。 正如我們在這個示例中看到的,docker 只是將圖像名稱后面的內容附加到入口點二進制文件docker run testimg <my_cmd> 如果我們想將默認值(默認參數)傳遞給入口點會發生什么? 在這種情況下,我們只需要在CMD部分中指定它,例如:

From ubuntu
ENTRYPOINT ["sleep"]
CMD ["10"]

在這種情況下,如果用戶不傳遞任何參數,容器將使用默認值 (10) 並將其傳遞給入口點 sleep。

現在讓我們只使用 CMD 並省略 ENTRYPOINT 定義:

FROM ubuntu
CMD ["sleep", "5"]

如果我們重建並運行這個鏡像,它基本上會休眠 5 秒。

因此,總而言之,您可以使用ENTRYPOINT使您的容器充當可執行文件。 您可以使用CMD為入口點提供默認參數,或者在啟動容器時運行自定義命令,用戶可以從外部覆蓋該命令。

代碼中對 EntryPoint 函數的注釋

// 入口點 /usr/sbin/nginx.

// 將入口點(默認為 sh -c)設置為 /usr/sbin/nginx。

// 將接受 CMD 作為 /usr/sbin/nginx 的參數。

文檔中的另一個參考

您可以使用 ENTRYPOINT 的 exec 形式設置相當穩定的默認命令和參數,然后使用 CMD 設置更可能更改的其他默認值。

例子:

FROM ubuntu:14.04.3
ENTRYPOINT ["/bin/ping"]
CMD ["localhost", "-c", "2"]

構建: sudo docker build -t ent_cmd 。

CMD arguments are easy to override.

NO argument (sudo docker -it ent_cmd)                :  ping localhost 
argument    (sudo docker run -it ent_cmd google.com) :  ping google.com

.

To override EntryPoint argument, you need to supply entrypoint
sudo docker run -it --entrypoint="/bin/bash" ent_cmdd

ps:在存在EntryPoint 的情況下,CMD 將保存參數以饋送到EntryPoint。 在沒有 EntryPoint 的情況下,CMD 將是將要運行的命令。

我已閱讀所有答案,我想總結一下,以便乍一看更好地理解,如下所示:

首先,在容器中執行的整個命令包括兩部分:命令參數

  • ENTRYPOINT定義容器啟動時調用的可執行文件(用於命令)

  • CMD指定傳遞給 ENTRYPOINT 的參數(用於參數)

Kubernetes In Action一書中,有一個重要的說明。 (第七章)

雖然您可以使用CMD指令指定在運行映像時要執行的命令,但正確的方法是通過ENTRYPOINT指令執行此操作,並且僅在要定義默認參數時指定CMD

您也可以通過簡單的方式閱讀這篇文章以獲得很好的解釋

命令:

  • CMD ["executable","param1","param2"] : ["executable","param1","param2"]是第一個進程。
  • CMD command param1 param2 : /bin/sh -c CMD command param1 param2是第一個進程。 CMD command param1 param2是從第一個進程派生的。
  • CMD ["param1","param2"] :此表單用於為ENTRYPOINT提供默認參數。

ENTRYPOINT(以下列表不考慮 CMD 和 ENTRYPOINT 一起使用的情況):

  • ENTRYPOINT ["executable", "param1", "param2"] : ["executable", "param1", "param2"]是第一個進程。
  • ENTRYPOINT command param1 param2 : /bin/sh -c command param1 param2是第一個進程。 command param1 param2是從第一個進程派生的。

正如creack所說,CMD 是首先開發的。 然后 ENTRYPOINT 被開發用於更多的定制。 由於它們不是一起設計的,所以 CMD 和 ENTRYPOINT 之間存在一些功能重疊,這常常使人們感到困惑。

Dockerfile 最佳實踐的官方文檔很好地解釋了這些差異。 Dockerfile 最佳實踐

命令:

CMD 指令應用於運行圖像包含的軟件以及任何參數。 CMD 應該幾乎總是以CMD ["executable", "param1", "param2"…]的形式使用。 因此,如果圖像用於服務,例如 Apache 和 Rails,您將運行CMD ["apache2","-DFOREGROUND"]類的東西。 事實上,這種形式的指令推薦用於任何基於服務的圖像。

入口點:

ENTRYPOINT 的最佳用途是設置圖像的主命令,允許該圖像像該命令一樣運行(然后使用 CMD 作為默認標志)。

大多數人在這里解釋得很好,所以我不會重復所有的答案。 但是為了獲得良好的感覺,我建議您通過查看容器中的進程來自己測試它。

創建一個小的 Dockerfile 形式:

FROM ubuntu:latest
CMD /bin/bash

構建它,使用 docker run -it docker run -it theimage ,然后在容器中運行ps -eo ppid,pid,args 將此輸出與您在使用時從 ps 收到的輸出進行比較:

  • docker run -it theimage bash
  • 重建圖像但使用ENTRYPOINT /bin/bash並以兩種方式運行它
  • 使用CMD ["/bin/bash"]
  • ...

這樣,您將很容易地看到自己所有可能的方法之間的差異。

Dockerfile文件中提到的CMD命令可以通過 docker docker run命令覆蓋,而ENTRYPOINT不能。

• Dockerfile 應至少指定一條 CMD 或 ENTRYPOINT 指令

• 僅使用 Dockerfile 中的最后一個 CMD 和 ENTRYPOINT

• 將容器用作可執行文件時應定義 ENTRYPOINT

• 您應該使用 CMD 指令為定義為 ENTRYPOINT 的命令或在容器中執行臨時命令定義默認參數

• 當使用替代參數運行容器時,CMD 將被覆蓋

• ENTRYPOINT 設置每次使用映像創建容器時使用的具體默認應用程序

• 如果您將 ENTRYPOINT 與 CMD 結合,您可以從 CMD 中刪除一個可執行文件,只保留將傳遞給 ENTRYPOINT 的參數

• ENTRYPOINT 的最佳用途是設置映像的主命令,允許該映像像該命令一樣運行(然后使用 CMD 作為默認標志)

我想以輕松的方式區分CMD、RUN 和 ENTRYPOINT之間的差異。

讓我們以 node 的 npm init 為例。

指令:

假設下面是我們在 dockerfile 中添加的初始命令

CMD [ "npm", "init" ]

現在,如果我運行docker run -t node npm install

它將覆蓋 dockerfile 中的 npm init 命令。

CMD [ "npm", "init" ] This will become  CMD [ "npm", "install" ]

它將執行npm install命令而不是npm init ,因為它會被 npm install 覆蓋。

現在,讓我們談談

入口點 :

假設在 docker 文件中添加了相同的命令,但使用 ENTRYPOINT

ENTRYPOINT [ "npm", "init" ]

現在,如果我運行docker run -t node install

它將在 dockerfile 中附加 npm init 命令和 npm install。

ENTRYPOINT [ "npm", "init" ] This will become  ENTRYPOINT [ "npm", "init", "install" ]

它將執行 npm init 和 npm install 命令。

總結一下 :

RUN:這將在圖像生成時執行。 用於安裝任何依賴項,如 node_modules。 前任。 RUN npm install

CMD :當你想覆蓋完整的命令時使用

ENTRYPOINT:當你想附加一些額外的命令時使用。

通過從頭開始重建操作系統映像,我了解到,

如果您未在 dockerfile 中指定 ENTRYPOINT 和 CMD,則 docker 將使用

/bin/sh -c

作為默認入口點,如果您在 docker 文件中定義 CMD 或在運行容器時傳遞命令行參數(這將覆蓋定義的 CMD),則將采用 CMD。

假設您傳遞一個參數(或在 dockerfile 中定義ls ),那么它將被輸入到 ENTRYPOINT。 那是,

/bin/sh -c ls

/bin/sh -c將運行傳遞給它的任何參數。 您將獲得“ls”命令的 output,然后容器將退出。


ubuntu 圖像包含定義的 CMD 即bash 。這意味着當你運行
以下命令運行容器,

docker container run -it ubuntu

Docker 實際上使用 ENTRYPOINT 作為/bin/sh -c然后用bash提供它,最終運行的是

/bin/sh -c bash

這將啟動交互式 bash 終端(僅當如上所述指定-i標志和可選 -t 以獲得類似本機終端的體驗時)

當您通過命令行提供 arguments 時, bash將被您傳遞的任何內容替換,並且 output 就是這樣,即

/bin/sh -c passed_argument

您可以定義自定義 ENTRYPOINT 來覆蓋默認值,但隨后您需要相應地使用 CMD。

在 dockerfile 中的RUN命令的情況下,它不考慮定義的ENTRYPOINTCMD ,而是運行指定的命令,因為它們提供給中間容器終端中的交互式 ZD574D4BB40C84861791A694A999CCE69 終端

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM