![](/img/trans.png)
[英]Understanding the difference in sequence of ENTRYPOINT/CMD between Dockerfile and docker run
[英]What is the difference between CMD and ENTRYPOINT in a Dockerfile?
在 Dockerfiles 中有兩個和我類似的命令: CMD
和ENTRYPOINT
。 但我猜想它們之間存在(細微的?)差異——否則對於同一件事有兩個命令是沒有任何意義的。
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 指令都定義了運行容器時執行的命令。 很少有規則描述他們的合作。
- Dockerfile 應至少指定
CMD
或ENTRYPOINT
命令之一。- 將容器用作可執行文件時應定義
ENTRYPOINT
。CMD
應該用作為ENTRYPOINT
命令或在容器中執行臨時命令定義默認參數的一種方式。- 當使用替代參數運行容器時,
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 和 ENTRYPOINT 與大量示例進行了全面比較 - https://codewithyury.com/docker-run-vs-cmd-vs-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
更多關於CMD
和ENTRYPOINT
之間的區別:
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
。
碼頭工人文件
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
碼頭工人文件
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
碼頭工人文件
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
命令的情況下,它不考慮定義的ENTRYPOINT
和CMD
,而是運行指定的命令,因為它們提供給中間容器終端中的交互式 ZD574D4BB40C84861791A694A999CCE69 終端
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.