在 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 |
前言 - 為什麼容器要傳入指令執行?
在此之前需要介紹一下為什麼要讓容器透過 CMD 或是 ENTRYPOINT 執行指令?容器並非是虛擬機器,需要有一個程序在裡面運作,一旦該程序運作完畢後,此容器就會自動退出離開,又有一些容器在設計上會執行一個永遠不中斷程序,比如 Nginx ( Dockerfile 如下圖 ),已經規範好會啟動 Nginx 服務,且不用傳入任何指令,只要執行docker run nginx
,該程序將會不斷的執行下去。
不過如果是使用像是 Ubuntu 的容器( Dockerfile 如下圖 ),在執行docker run ubuntu
,會發現程式立刻呈現結束狀態,因僅執行一個 Bash ,如果沒有傳送任何東西給 Bash ,會認為已經完成程序了,容器將會停止並退出。
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 可以更佳靈活彈性,不用一直重複製造輪子。