Dockerfile之CMD、ENTRYPOINT指令
這里對Dockerfile中的CMD、ENTRYPOINT指令進(jìn)行介紹

CMD指令
該指令可以用于指定容器被啟動(dòng)時(shí)需要運(yùn)行的命令。具體地,其支持shell、exec兩種形式的語法
# shell格式
CMD command param1 param2
# exec格式
CMD ["command", "param1", "param1"]
通常Docker中推薦使用exec格式語法,原因有二。一方面,shell格式語法下會通過/bin/sh -c來執(zhí)行命令;另一方面,某些鏡像甚至不包含Shell,致使shell格式下的命令無法被正常執(zhí)行。但使用exec格式時(shí),會無法獲取環(huán)境變量的值。此時(shí)則可以考慮使用shell格式語法
shell格式
下面通過Dockerfile定義一個(gè)名為demo1:test的鏡像
# 鏡像 demo1:test
FROM busybox:1.35.0
# 使用 shell 格式的CMD
CMD ping baidu.com
然后我們創(chuàng)建一個(gè)容器
docker run --name demo1A --rm -it demo1:test
如下所示,其會使用CMD指令設(shè)置的命令、參數(shù)執(zhí)行

對于CMD指定的命令而言,不可以通過 docker run命令行實(shí)現(xiàn)傳遞參數(shù)。因?yàn)?CMD指令的命令、參數(shù) 會被 docker run命令行參數(shù)中指定的命令、參數(shù) 完全覆蓋。例如我們創(chuàng)建一個(gè)容器,期望執(zhí)行 ping weibo.com。則需要重新傳遞命令、參數(shù)
# 錯(cuò)誤方式:
docker run --name demo1B --rm -it demo1:test weibo.com
# 正確方式
docker run --name demo1B --rm -it demo1:test ping weibo.com
效果如下所示

又比如按下述方式創(chuàng)建命令,同理
docker run --name demo1C --rm -it demo1:test top -H
效果如下所示

exec格式
下面通過Dockerfile定義一個(gè)名為demo2:test的鏡像
# 鏡像 demo2:test
FROM busybox:1.35.0
# 使用 exec 格式的CMD
CMD ["ping", "baidu.com"]
然后我們創(chuàng)建一個(gè)容器
docker run --name demo2A --rm -it demo2:test
如下所示,其會使用CMD指令設(shè)置的命令、參數(shù)執(zhí)行

對于CMD指定的命令而言,不可以通過 docker run命令行實(shí)現(xiàn)傳遞參數(shù)。因?yàn)?CMD指令的命令、參數(shù) 會被 docker run命令行參數(shù)中指定的命令、參數(shù) 完全覆蓋。例如我們創(chuàng)建一個(gè)容器,期望執(zhí)行 ping weibo.com。則需要重新傳遞命令、參數(shù)
# 錯(cuò)誤方式
docker run --name demo2B --rm -it demo2:test weibo.com
# 正確方式
docker run --name demo2B --rm -it demo2:test ping weibo.com
效果如下所示

又比如按下述方式創(chuàng)建命令,同理
docker run --name demo2C --rm -it demo2:test top -H
效果如下所示

ENTRYPOINT指令
該指令同樣可以用于指定容器被啟動(dòng)時(shí)需要運(yùn)行的命令。同理,其同樣支持shell、exec兩種形式的語法
# shell格式
ENTRYPOINT command param1 param2
# exec格式
ENTRYPOINT ["command", "param1", "param1"]
對于ENTRYPOINT指令而言,Docker中同樣推薦使用exec格式語法,理由與CMD指令同理
shell格式
下面通過Dockerfile定義一個(gè)名為demo3:test的鏡像
# 鏡像 demo3:test
FROM busybox:1.35.0
# 使用 shell 格式的ENTRYPOINT
ENTRYPOINT top -b
然后我們創(chuàng)建一個(gè)容器
docker run --name demo3A --rm -it demo3:test
如下所示,其會使用ENTRYPOINT指令設(shè)置的命令、參數(shù)執(zhí)行

ENTRYPOINT指令 所設(shè)置命令、參數(shù)可被 docker run命令行參數(shù)中指定要運(yùn)行的命令 覆蓋, 但需要使用 --entrypoint 選項(xiàng)進(jìn)行顯式覆蓋。否則將會忽略命令行參數(shù)
# 錯(cuò)誤方式
docker run --name demo3B --rm -it demo3:test ifconfig
# 正確方式
docker run --name demo3C --rm -it --entrypoint ifconfig demo3:test
效果如下所示

當(dāng)我們使用 --entrypoint 選項(xiàng)進(jìn)行顯式覆蓋命令時(shí),還可以傳遞參數(shù)
docker run --name demo3D --rm -it --entrypoint ping demo3:test bing.com.cn
效果如下所示

對于shell格式的ENTRYPOINT指令設(shè)置的命令而言,如果沒有使用--entrypoint 選項(xiàng)。當(dāng)通過 docker run命令行傳遞參數(shù)時(shí), 其會被忽略
docker run --name demo3E --rm -it demo3:test -H -m
效果如下所示

exec格式
下面通過Dockerfile定義一個(gè)名為demo4:test的鏡像
# 鏡像 demo4:test
FROM busybox:1.35.0
# 使用 exec 格式的ENTRYPOINT
ENTRYPOINT ["top", "-b"]
然后我們創(chuàng)建一個(gè)容器
docker run --name demo4A --rm -it demo4:test
如下所示,其會使用ENTRYPOINT指令設(shè)置的命令、參數(shù)執(zhí)行

ENTRYPOINT指令 所設(shè)置命令、參數(shù)可被 docker run命令行參數(shù)中指定要運(yùn)行的命令 覆蓋, 但需要使用 --entrypoint 選項(xiàng)進(jìn)行顯式覆蓋。否則將會忽略命令行參數(shù)
# 錯(cuò)誤方式
docker run --name demo4B demo4:test ifconfig
# 正確方式
docker run --name demo4C --rm -it --entrypoint ifconfig demo4:test
效果如下所示

當(dāng)我們使用 --entrypoint 選項(xiàng)進(jìn)行顯式覆蓋命令時(shí),還可以傳遞參數(shù)
docker run --name demo4D --rm -it --entrypoint ping demo4:test weibo.com
效果如下所示

對于exec格式的ENTRYPOINT指令設(shè)置的命令而言,如果沒有使用--entrypoint 選項(xiàng)。當(dāng)通過 docker run命令行實(shí)現(xiàn)傳遞參數(shù)時(shí), 其會被追加
docker run --name demo4E --rm -it demo4:test -H -m
效果如下所示

組合使用
對于大多數(shù)場景下,CMD、ENTRYPOINT指令都是互相通用的,而且一般也會只使用其中一種指令。具體地,CMD指令方便鏡像使用者更改容器運(yùn)行的命令,故適用于較為靈活的場景;而如果不期望鏡像使用者去輕易更改容器運(yùn)行的命令,故推薦使用ENTRYPOINT指令。同時(shí)如前文所述,exec格式較shell格式更為推薦。而對于CMD、ENTRYPOINT指令二者組合使用時(shí),其效果可參考下圖

事實(shí)上對于組合使用二者來說,更為常見的一種實(shí)踐方式是通過exec格式的ENTRYPOINT設(shè)置固定的命令、參數(shù),而利用exec格式的CMD 設(shè)置默認(rèn)的可變參數(shù)
下面通過Dockerfile定義一個(gè)名為demo5:test的鏡像
# 鏡像 demo5:test
FROM busybox:1.35.0
# 使用 exec 格式的ENTRYPOINT 設(shè)置固定的命令、參數(shù)
ENTRYPOINT ["top", "-b"]
# 使用 exec 格式的CMD 設(shè)置默認(rèn)的可變參數(shù)
CMD ["-H"]
然后我們創(chuàng)建一個(gè)容器
docker run --name demo5A --rm -it demo5:test
效果如下所示

由于此場景下CMD指令提供的是一個(gè)默認(rèn)的可變參數(shù),故我們可以通過docker run命令行參數(shù) 來覆蓋 CMD指定的默認(rèn)可變參數(shù)
docker run --name demo5B --rm -it demo5:test -m
效果如下所示

同理,ENTRYPOINT指令 所設(shè)置命令、參數(shù)可被 docker run命令行參數(shù)中指定要運(yùn)行的命令 覆蓋, 使用 --entrypoint 選項(xiàng)進(jìn)行顯式覆蓋即可。當(dāng)然此時(shí)CMD指令設(shè)置的默認(rèn)可變參數(shù)也將失效
docker run --name demo5C --rm -it --entrypoint ifconfig demo5:test
效果如下所示

當(dāng)然使用 --entrypoint 選項(xiàng)進(jìn)行顯式覆蓋命令時(shí),依然可以傳遞參數(shù)
docker run --name demo5D --rm -it --entrypoint ping demo5:test weibo.com
效果如下所示

參考文獻(xiàn)
第一本Docker書·修訂版 James Turnbull著 深入淺出Docker Nigel Poulton著
