Contents

Docker - CMD 與 ENTRYPOINT 差別

在 Dockerfile 在最末端會看到 Image 會有 CMD 及 ENTRYPOINT 不同寫法,兩種都是執行容器第一個PID要執行的指令,不過用法到底差別在哪呢?


Docker 官方文件說明 : “Both CMD and ENTRYPOINT instructions define what command gets executed when running a container. There are few rules that describe their co-operation.”

No ENTRYPOINT ENTRYPOINT exec_entry p1_entry ENTRYPOINT [“exec_entry”, “p1_entry”]
No CMD error, not allowed /bin/sh -c exec_entry p1_entry exec_entry p1_entry
CMD [“exec_cmd”, “p1_cmd”] exec_cmd p1_cmd /bin/sh -c exec_entry p1_entry exec_entry p1_entry exec_cmd p1_cmd
CMD [“p1_cmd”, “p2_cmd”] p1_cmd p2_cmd /bin/sh -c exec_entry p1_entry exec_entry p1_entry p1_cmd p2_cmd
CMD exec_cmd p1_cmd /bin/sh -c exec_cmd p1_cmd /bin/sh -c exec_entry p1_entry exec_entry p1_entry /bin/sh -c exec_cmd p1_cmd
Note
詳細請參閱官方DOC,連結請點我 Docker Documentation )

前言 - 為什麼容器要傳入指令執行?

在此之前需要介紹一下為什麼要讓容器透過 CMD 或是 ENTRYPOINT 執行指令?容器並非是虛擬機器,需要有一個程序在裡面運作,一旦該程序運作完畢後,此容器就會自動退出離開,又有一些容器在設計上會執行一個永遠不中斷程序,比如 Nginx ( Dockerfile 如下圖 ),已經規範好會啟動 Nginx 服務,且不用傳入任何指令,只要執行docker run nginx ,該程序將會不斷的執行下去。

https://i.imgur.com/RLZl7vb.png

不過如果是使用像是 Ubuntu 的容器( Dockerfile 如下圖 ),在執行docker run ubuntu,會發現程式立刻呈現結束狀態,因僅執行一個 Bash ,如果沒有傳送任何東西給 Bash ,會認為已經完成程序了,容器將會停止並退出。

https://i.imgur.com/vTaG79A.png


1. Dockerfile - CMD 用法

1.1 製作簡易 Dockerfile

在此之前我們先製作一個簡單的 ubuntu 為機底 dockerfile,並產生 image,加上自定義的 CMD 參數。

1
2
3
4
5
FROM ubuntu
MAINTAINER howhow

# 要求容器再啟動時執行 sleep 指令,並等待 10 秒後停止
CMD ["sleep", "10"]

創建好上述的 dockerfile後,使用 docker build 方式產生 image 並命名為 myubuntu01。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
root@howhow:~/docker-playground# docker build -t myubuntu01 .
Sending build context to Docker daemon  2.048kB
Step 1/3 : FROM ubuntu
 ---> 54c9d81cbb44
Step 2/3 : MAINTAINER howhow
 ---> Using cache
 ---> afaf5998f491
Step 3/3 : CMD ["sleep","10"]
 ---> Using cache
 ---> e92456c1bb6a
Successfully built e92456c1bb6a
Successfully tagged myubuntu01:latest

1.2 執行容器

執行 docker run -d myubuntu01 後持續觀察 docker ps ,查看 10 秒後容器自動停止。

1
2
3
4
5
root@howhow:~/docker-playground# docker run -d myubuntu01
c37015540cbf2a9b9e3a60d44415306b0df2fd6c43497bcd367c1877be994b6f
root@howhow:~/docker-playground# docker ps
CONTAINER ID   IMAGE        COMMAND      CREATED         STATUS        PORTS     NAMES
c37015540cbf   myubuntu01   "sleep 10"   2 seconds ago   Up 1 second             exciting_sinoussi

1.3 執行容器並調整執行時間

接下來如果要調整執行時間的話,並需傳入兩個參數 docker run -d myubuntu01 sleep 30,告訴容器還是使用 sleep 指令,這次時長不使用預設的 10 秒改為 30 秒。

1
2
3
4
5
root@howhow:~/docker-playground# docker run -d myubuntu01 sleep  30
0465d8ab11f884e7d460400175afe8964aa00aec316b5168f621be58f79b8847
root@howhow:~/docker-playground# docker ps
CONTAINER ID   IMAGE        COMMAND      CREATED         STATUS        PORTS     NAMES
0465d8ab11f8   myubuntu01   "sleep 30"   3 seconds ago   Up 1 second             optimistic_shockley

不過上述的這種作法很不直覺,每次都要加上兩個參數,此時就可以改用 ENTRYPOINT 方式來處理。


2. Dockerfile - ENTRYPOINT 用法

2.1 製作簡易 Dockerfile

將之前製作的 dockerfile 修改一下還是使用 ubuntu 為機底,並產生 image,加上自定義的 ENTRYPOINT 參數(只要一個就行)。

1
2
3
4
5
FROM ubuntu
MAINTAINER howhow

# 要求容器再啟動時執行 sleep 指令,並等待 10 秒後停止
ENTRYPOINT ["sleep"]

創建好上述的 dockerfile後,使用 docker build 方式產生 image 並命名為 myubuntu02。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
root@howhow:~/docker-playground# clear
root@howhow:~/docker-playground# docker build -t myubuntu02 .
Sending build context to Docker daemon  2.048kB
Step 1/3 : FROM ubuntu
 ---> 54c9d81cbb44
Step 2/3 : MAINTAINER howhow
 ---> Using cache
 ---> afaf5998f491
Step 3/3 : ENTRYPOINT ["sleep"]
 ---> Using cache
 ---> f15dabcc5875
Successfully built f15dabcc5875
Successfully tagged myubuntu02:latest

2.2 執行容器

2.2.1 傳入參數 10

執行 docker run -d myubuntu02 10 (傳入10秒參數)後持續觀察 docker ps ,查看 10 秒後容器自動停止。

1
2
3
4
5
root@howhow:~/docker-playground# docker run -d myubuntu02 10
67f6f7e1de7b971ea18c843e9d3851d05d2c144b739ff6634d52b4d5a32e114d
root@howhow:~/docker-playground# docker ps
CONTAINER ID   IMAGE        COMMAND      CREATED         STATUS        PORTS     NAMES
67f6f7e1de7b   myubuntu02   "sleep 10"   2 seconds ago   Up 1 second             competent_sanderson

上述執行結果將會與 CMD 出現一樣的效果,此時可以任意調整想要停止的時間

2.2.2 不傳入參數

不傳入任何參數,會啟動 sleep 指令,不過並沒有告訴該指令要怎麼執行,就要報錯誤並停止

1
2
3
4
5
root@howhow:~/docker-playground# docker run -d myubuntu02 
a88b6618050fa9abc2c319dee74464a148f5e6f756eece7d51afd3fa2259fc1f
root@howhow:~/docker-playground# docker logs a88b6618050fa9abc2c319dee74464a148f5e6f756eece7d51afd3fa2259fc1f
sleep: missing operand
Try 'sleep --help' for more information.

那如果希望不傳參數可以使用預設值讓容器不會執行失敗的方式呢?那就可以把 ENTRYPOINT 結合 CMD 兩種方式結合使用


3. Dockerfile - ENTRYPOINT 結合 CMD 用法

3.1 製作簡易 Dockerfile

將之前製作的 dockerfile 修改一下將 ENTRYPOINT 及 CMD 組合起來

1
2
3
4
5
FROM ubuntu
MAINTAINER howhow

ENTRYPOINT ["sleep"]
CMD ["10"]

創建好上述的 dockerfile後,使用 docker build 方式產生 image 並命名為 myubuntu03。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
root@howhow:~/docker-playground#  docker build -t myubuntu03 .
Sending build context to Docker daemon  2.048kB
Step 1/4 : FROM ubuntu
 ---> 54c9d81cbb44
Step 2/4 : MAINTAINER howhow
 ---> Using cache
 ---> afaf5998f491
Step 3/4 : ENTRYPOINT ["sleep"]
 ---> Using cache
 ---> f15dabcc5875
Step 4/4 : CMD ["10"]
 ---> Using cache
 ---> ab93d0318b02
Successfully built ab93d0318b02
Successfully tagged myubuntu03:latest

3.2 執行容器

3.2.1 不傳入參數

執行 docker run -d myubuntu03 (不傳入任何參數) 後持續觀察 docker ps ,查看 10 秒後容器自動停止。

1
2
3
4
5
root@howhow:~/docker-playground# docker run -d myubuntu03 
4419a2d78e505ed0fd261e31b1b35648e50296c3ab092d50136994d74e456609
root@howhow:~/docker-playground# docker ps
CONTAINER ID   IMAGE        COMMAND      CREATED         STATUS        PORTS     NAMES
4419a2d78e50   myubuntu03   "sleep 10"   2 seconds ago   Up 1 second             youthful_albattani

現在不傳入任何參數就會使用預設 10 秒,如果傳參數就使用使用者指定 ,此方式是不是彈性非常多了呢?

3.2.2 傳入參數 30

此時傳入參數 30 秒,容器就會依要求等待30秒後才會退出。

1
2
3
4
5
root@howhow:~/docker-playground# docker run -d myubuntu03 30
6f1d6f7d7fe72b2e75d777cc41cbd054891ac3934048588107c527e1ece7c4a9
root@howhow:~/docker-playground# docker ps
CONTAINER ID   IMAGE        COMMAND      CREATED         STATUS         PORTS     NAMES
6f1d6f7d7fe7   myubuntu03   "sleep 30"   3 seconds ago   Up 2 seconds             compassionate_kirch

3.2.3 使用其他指令當參數

如果想使用其它指令 ,但是 ENTRYPOINT 已經設定為 sleep, 可以傳送 –entrypoint 改變指令

1
2
3
4
5
root@howhow:~# docker run -d --entrypoint tail  myubuntu03  -f /dev/null
d9a4897abb53ca11c19af1b2693636fcd4327058827328bd85b4782bfd222e31
root@howhow:~# docker ps
CONTAINER ID   IMAGE        COMMAND               CREATED         STATUS        PORTS     NAMES
d9a4897abb53   myubuntu03   "tail -f /dev/null"   2 seconds ago   Up 1 second             eager_colden

小結

CMD 與 ENTRYPOINT 在實務上沒有一定要採取哪一個方案,如果只是單純的啟動容器混者用倒是無妨,透過上述幾些例子再撰寫 Dockerfile 可以更佳靈活彈性,不用一直重複製造輪子。



如果你還沒有註冊 Like Coin,你可以在文章最下方看到 Like 的按鈕,點下去後即可申請帳號,透過申請帳號後可以幫我的文章按下 Like,而 Like 最多可以點五次,而你不用付出任何一塊錢,就能給我寫這篇文章的最大的回饋!