『現學現忘』Docker基礎 — 36、CMD指令和ENTRYPOINT指令的區別

繁華似錦Fighting發表於2022-03-31

CMD指令和ENTRYPOINT指令作用都是指定一個容器啟動時要執行的命令。

1、CMD指令和ENTRYPOINT指令說明

CMD指令, 支援三種格式:

CMD ["executable","param1","param2"] :使用 exec 執行,推薦方式;

CMD command param1 param2 :在 /bin/sh 中執行,提供給需要互動的應用;

CMD ["param1","param2"] :提供給 ENTRYPOINT 的預設引數;

每個Dockerfile只能生效一條CMD指令。如果指定了多條CMD指令,只有最後一條生效。

如果使用者啟動容器時候指定了執行的命令,則會覆蓋掉CMD指定的命令。

ENTRYPOINT指令,支援兩種格式:

ENTRYPOINT ["executable", "param1", "param2"]

ENTRYPOINT command param1 param2 :shell中執行

每個Dockerfile中只能生效一個ENTRYPOINT指令,當有多個ENTRYPOINT指令時,只有最後一個起效。

如果使用者啟動容器時候指定了執行的命令,不會被docker run執行的命令引數覆蓋。(追加效果:docker run命令之後的引數,會被當做引數傳遞給ENTRYPOINT,之後形成新的命令組合)

2、CMD指令只有最後一條生效的原因

CMD指令是一個容器中預設的可執行命令。也就是容器啟動以後,預設執行的命令。(預設是重點)

換句話說,給出了CMD指令的一個角色定位,它主要作用是定義預設的容器啟動後執行的命令

提示:

這也就是網上所說,只有最後一條CMD指令會生效,之前的都會被覆蓋掉。

就是因為CMD指令的角色定位就是預設,如果你不進行額外指定,那麼就執行CMD指令。否則,你要自己指定了命令,那麼就不會執行CMD指令,這也就是相當於CMD指令會被覆蓋。

而ENTRYPOINT指令,才是正統地用於定義容器啟動以後的執行體,從名字上我們也可以看出,entrypoint 入口點的意思,一個容器的“入口”。

3、CMD指令演示

我們以ContOS映象為基礎,對該映象進行修改。

(1)準備測試環境

進入宿主機/home/mydockerfile目錄中,建立Dockerfil檔案Dockerfile_cmd

# mydockerfile目錄中建立Dockerfile_cmd檔案
[root@192 mydockerfile]# pwd
/home/mydockerfile
[root@192 mydockerfile]# touch Dockerfile_cmd

編輯Dockerfile_cmd檔案,內容如下:

FROM centos # 繼承了本地的centos映象
CMD ["ls","-a"] # 執行ls -a 命令

說明:我們之前CMD執行都是/bin/bash,這回改成ls -a命令是一樣的,就是啟動容器後執行ls -a命令。

根據Dockerfile_cmd生成cmd映象。

[root@192 mydockerfile]# docker build -f /home/mydockerfile/Dockerfile_cmd -t wukong/cmd:1.0 .
Sending build context to Docker daemon  3.072kB
Step 1/2 : FROM centos
 ---> 300e315adb2f
Step 2/2 : CMD ["ls","-a"]
 ---> Running in e34a0fc14329
Removing intermediate container e34a0fc14329
 ---> de211d4ca7f3
Successfully built de211d4ca7f3
Successfully tagged wukong/cmd:1.0

(2)進行驗證

執行wukong/cmd映象,檢視結果。

# 檢視本地Docker映象
[root@192 mydockerfile]# docker images
REPOSITORY      TAG       IMAGE ID       CREATED         SIZE
wukong/cmd      1.0       de211d4ca7f3   2 minutes ago   209MB
wokong_centos   1.6       6441f63090d4   3 hours ago     291MB
centos          latest    300e315adb2f   3 months ago    209MB

# 直接執行該容器,檢視結果,輸出了預設工作目錄中的內容。
[root@192 mydockerfile]# docker run wukong/cmd:1.0
.
..
.dockerenv
bin
dev
etc
home
lib
lib64
lost+found
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var

# 容器啟動,執行完ls -a 命令後,就會停止。
[root@192 mydockerfile]# docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

接下來,我們在docker run命令後,加入-l命令,我們再來檢視一下結果。

[root@192 mydockerfile]# docker run wukong/cmd:1.0 -l
docker: Error response from daemon: OCI runtime create failed: container_linux.go:367: starting container process caused: exec: "-l": executable file not found in $PATH: unknown.

說明:

我的意思是想追加一個-l引數,讓容器啟動後,執行ls -al命令。結果我們看到是,可執行檔案找不到的報錯, exec: "-l":executable file not found

這就是我們之前說過的,跟在映象名後面的是command(命令),執行時會替換CMD的預設值。也就是-l引數替換了Dockerfile檔案中的CMD ["ls","-a"]命令,而不是新增在原來的命令裡。

而單獨的-l引數不是Linux命令,不能單獨的命令,自然找不到,所以就報錯了。

所以綜上所述,CMD指令會被docker run之後的引數替換。

那麼如果我們希望加入-l這個引數,就必須重新完整的輸入整個命令,如下:

[root@192 mydockerfile]# docker run wukong/cmd:1.0 ls -al
total 0
drwxr-xr-x.   1 root root   6 Mar 20 11:13 .
drwxr-xr-x.   1 root root   6 Mar 20 11:13 ..
-rwxr-xr-x.   1 root root   0 Mar 20 11:13 .dockerenv
lrwxrwxrwx.   1 root root   7 Nov  3 15:22 bin -> usr/bin
drwxr-xr-x.   5 root root 340 Mar 20 11:13 dev
drwxr-xr-x.   1 root root  66 Mar 20 11:13 etc
drwxr-xr-x.   2 root root   6 Nov  3 15:22 home
lrwxrwxrwx.   1 root root   7 Nov  3 15:22 lib -> usr/lib
lrwxrwxrwx.   1 root root   9 Nov  3 15:22 lib64 -> usr/lib64
drwx------.   2 root root   6 Dec  4 17:37 lost+found
drwxr-xr-x.   2 root root   6 Nov  3 15:22 media
drwxr-xr-x.   2 root root   6 Nov  3 15:22 mnt
drwxr-xr-x.   2 root root   6 Nov  3 15:22 opt
dr-xr-xr-x. 122 root root   0 Mar 20 11:13 proc
dr-xr-x---.   2 root root 162 Dec  4 17:37 root
drwxr-xr-x.  11 root root 163 Dec  4 17:37 run
lrwxrwxrwx.   1 root root   8 Nov  3 15:22 sbin -> usr/sbin
drwxr-xr-x.   2 root root   6 Nov  3 15:22 srv
dr-xr-xr-x.  13 root root   0 Mar 20 06:42 sys
drwxrwxrwt.   7 root root 145 Dec  4 17:37 tmp
drwxr-xr-x.  12 root root 144 Dec  4 17:37 usr
drwxr-xr-x.  20 root root 262 Dec  4 17:37 var

4、ENTRYPOINT指令演示

和上面CMD指令演示相似。

我們以ContOS映象為基礎,對該映象進行修改。

(1)準備測試環境

進入宿主機/home/mydockerfile目錄中,建立Dockerfil檔案Dockerfile_entrypoint

# mydockerfile目錄中建立Dockerfile_entrypoint檔案
[root@192 mydockerfile]# pwd
/home/mydockerfile
[root@192 mydockerfile]# touch Dockerfile_entrypoint

編輯Dockerfile_entrypoint檔案,內容如下:

FROM centos # 繼承了本地的centos映象
ENTRYPOINT ["ls","-a"] # 執行ls -a 命令

根據Dockerfile_entrypoint生成entrypoint映象。

[root@192 mydockerfile]# docker build -f /home/mydockerfile/Dockerfile_entrypoint -t wukong/entrypoint:1.0 .
Sending build context to Docker daemon  4.096kB
Step 1/2 : FROM centos
 ---> 300e315adb2f
Step 2/2 : ENTRYPOINT ["ls","-a"]
 ---> Running in ecd4a636d8be
Removing intermediate container ecd4a636d8be
 ---> e641401b4ea5
Successfully built e641401b4ea5
Successfully tagged wukong/entrypoint:1.0

(2)進行驗證

執行wukong/entrypoint映象,檢視結果。

# 檢視本地Docker映象
[root@192 mydockerfile]# docker images
REPOSITORY          TAG       IMAGE ID       CREATED         SIZE
wukong/entrypoint   1.0       e641401b4ea5   2 minutes ago   209MB
wukong/cmd          1.0       adda4543a402   3 minutes ago   209MB
wokong_centos       1.6       6441f63090d4   4 hours ago     291MB
centos              latest    300e315adb2f   3 months ago    209MB

# 直接執行該容器,檢視結果,輸出了預設工作目錄中的內容。
[root@192 mydockerfile]# docker run wukong/entrypoint:1.0
.
..
.dockerenv
bin
dev
etc
home
lib
lib64
lost+found
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var

接下來,我們在docker run命令後,加入-l命令,我們再來檢視一下結果。

[root@192 mydockerfile]# docker run wukong/entrypoint:1.0 -l
total 0
drwxr-xr-x.   1 root root   6 Mar 20 12:03 .
drwxr-xr-x.   1 root root   6 Mar 20 12:03 ..
-rwxr-xr-x.   1 root root   0 Mar 20 12:03 .dockerenv
lrwxrwxrwx.   1 root root   7 Nov  3 15:22 bin -> usr/bin
drwxr-xr-x.   5 root root 340 Mar 20 12:03 dev
drwxr-xr-x.   1 root root  66 Mar 20 12:03 etc
drwxr-xr-x.   2 root root   6 Nov  3 15:22 home
lrwxrwxrwx.   1 root root   7 Nov  3 15:22 lib -> usr/lib
lrwxrwxrwx.   1 root root   9 Nov  3 15:22 lib64 -> usr/lib64
drwx------.   2 root root   6 Dec  4 17:37 lost+found
drwxr-xr-x.   2 root root   6 Nov  3 15:22 media
drwxr-xr-x.   2 root root   6 Nov  3 15:22 mnt
drwxr-xr-x.   2 root root   6 Nov  3 15:22 opt
dr-xr-xr-x. 124 root root   0 Mar 20 12:03 proc
dr-xr-x---.   2 root root 162 Dec  4 17:37 root
drwxr-xr-x.  11 root root 163 Dec  4 17:37 run
lrwxrwxrwx.   1 root root   8 Nov  3 15:22 sbin -> usr/sbin
drwxr-xr-x.   2 root root   6 Nov  3 15:22 srv
dr-xr-xr-x.  13 root root   0 Mar 20 06:42 sys
drwxrwxrwt.   7 root root 145 Dec  4 17:37 tmp
drwxr-xr-x.  12 root root 144 Dec  4 17:37 usr
drwxr-xr-x.  20 root root 262 Dec  4 17:37 var

說明:想給Dockerfile檔案中的ENTRYPOINT ["ls","-a"]命令追加一個-l引數,讓容器啟動後執行ls -al命令,結果順利執行。

結論:這就說明了,ENTRYPOINT指令是可以在容器啟動的時候追加命令。

5、總結

  • CMD:CMD設定容器啟動後預設執行的命令及其引數。但CMD設定的指令,能夠被docker run命令後面的命令列引數替換(也就是可被重寫覆蓋)。
  • ENTRYPOINT,表示映象在初始化時需要執行的命令。不會被忽略,一定會被執行,即使執行docker run命令時指定了其他命令(也就是可被重寫覆蓋)。
  • CMD和ENTRYPOINT都只能在檔案中存在一次。如果多個存在,只有最後一個生效,其它無效!
  • 對於 CMD 和 ENTRYPOINT 的設計而言,多數情況下它們應該是單獨使用的。當然,有一個例外是 CMD 為 ENTRYPOINT 提供預設的可選引數。

說明:

對於 Dockerfile 來說,CMD 和 ENTRYPOINT 是非常重要的指令。它們不是在構建映象的過程中執行,而是在啟動容器時執行,所以主要用來指定容器預設執行的命令。

Dockerfile中很多命令的功能都十分的相似,我們需要了解它們的區別,我們最好的學習就是對比他們,然後測試效果。

以上就是CMD指令和ENTRYPOINT指令的區別。

相關文章