一、寫在前面
我們在上篇小作文docker容器dockerfile詳解對中dockerfile有了比較全面的認識,我們也提到ENTRYPOINT
和CMD
都可以指定容器啟動命令。因為這兩個命令是掌握dockerfile編寫的核心,所以這邊還是單獨拿出來再講一講。
二、CMD 與 ENTRYPOINT主要區別
我們直接進入主題,CMD 與 ENTRYPOINT都是用於指定啟動容器執行的命令,區別在於:
- 當docker run 命令中有引數時,守護程式會忽略CMD命令。
- 使用ENTRYPOINT指令不會忽略,並且會接收docker run 引數附加到命令列中。
為了使構建的容器可以正常啟動,我們編寫的dockerfile檔案必須包含一個CMD或ENTRYPOINT指令。
三、CMD 與 ENTRYPOINT的結合使用
1.CMD
CMD
指令有三種形式:
CMD ["executable","param1","param2"]
(exec形式,這是首選形式)CMD ["param1","param2"]
(作為ENTRYPOINT 的預設引數)CMD command param1 param2
(shell形式)
dockerfile檔案中包含多個CMD時,只有最後一個被載入使用。
我們在dockerhub中搜尋centos官方映象,看一下的官方dockerfile檔案。
基本上每一個官方映象都會為我們提供各自版本的dockerfile連結,如下:
我們檢視latest
標籤的dockerfile
FROM scratch
ADD centos-8-x86_64.tar.xz /
LABEL org.label-schema.schema-version="1.0" org.label-schema.name="CentOS Base Image" org.label-schema.vendor="CentOS" org.label-schema.license="GPLv2" org.label-schema.build-date="20201204"
CMD ["/bin/bash"]
只有四行,這就是構建一個latest
版本centos8.3.2011映象的dockerfile全部內容。指定基映象(這裡從scratch這個空映象開始構建),新增rootfs內容,打標籤,通過CMD指定啟動命令。
不止centos,其他debian、ubuntu、busybox等映象都只需通過CMD指定啟動命令。比如busybox更為簡約:
FROM scratch
ADD busybox.tar.xz /
CMD ["sh"]
這種基礎類、工具類映象的構建我們只需要指定一個必要CMD來啟動容器即可。但是我們編寫一個dockerfile並不是為了啟動容器而編寫,大多數時候我們要在容器執行我們的app,執行我們的服務。
當然通過CMD也可以啟動,可是如此一來有一個缺陷,我們上面說到的CMD的啟動命令會被docker run 引數代替。
我們有下面Dockerfile
[root@localhost dockerfiles]# cat Dockerfile
FROM centos
CMD ["/bin/top","-b"]
構建後,使用引數ps啟動容器。
[root@localhost dockerfiles]# docker run -it centos_top:v1 ps
PID TTY TIME CMD
1 pts/0 00:00:00 ps
可看看到啟動容器後top -b
已經被替換為ps,並非實現引數的替換。顯然這不是我們想要的。有沒有什麼辦法既可以預設啟動應用,又可以載入到docker run 引數?這就是接下來ENTRYPOINT與CMD的妙用。
2.ENTRYPOINT結合CMD
ENTRYPOINT
的exec和shell形式:
ENTRYPOINT ["executable", "param1", "param2"]
ENTRYPOINT command param1 param2
上面我們提到CMD ["param1","param2"]
形式可以作為ENTRYPOINT引數,同時ENTRYPOINT 指定的命令無法被docker run 引數取代。假如我們把CMD和ENTRYPOINT兩個指令相結合,這樣我們就可以通過CMD來接收docker run 引數,然後把引數傳遞給ENTRYPOINT執行。
我們以nginx官方dockerfile latest版本1.21為例
首先我們檢視Dockerfile
,這裡我們只關注啟動命令,如下:
...
COPY docker-entrypoint.sh /
COPY 10-listen-on-ipv6-by-default.sh /docker-entrypoint.d
COPY 20-envsubst-on-templates.sh /docker-entrypoint.d
COPY 30-tune-worker-processes.sh /docker-entrypoint.d
ENTRYPOINT ["/docker-entrypoint.sh"]
EXPOSE 80
STOPSIGNAL SIGQUIT
CMD ["nginx", "-g", "daemon off;"]
從上面我們可以看到,在啟動nginx容器時首先執行docker-entrypoint.sh
指令碼並把CMD命令中的引數nginx -g "daemon off;"
傳遞進來。即docker run不新增引數時啟動容器相當於執行如下指令碼與預設引數。
#docker-entrypoint.sh nginx -g "daemon off;"
當我們使用docker run 傳入引數會怎樣?
我傳入nginx-debug
#docker run -dt nginx nginx-debug -g "daemon off;"
此時啟動容器相當於執行如下指令碼與引數
#docker-entrypoint.sh nginx-debug -g "daemon off;"
我們通過ps來看一下我們啟動的容器
[root@localhost dockerfiles]# ps -ef|grep nginx
root 6327 6306 0 Aug12 pts/0 00:00:00 nginx: master process nginx -g daemon off;
101 6384 6327 0 Aug12 pts/0 00:00:00 nginx: worker process
101 6385 6327 0 Aug12 pts/0 00:00:00 nginx: worker process
root 16800 16780 3 12:51 pts/0 00:00:00 nginx: master process nginx-debug -g daemon off;
101 16857 16800 0 12:51 pts/0 00:00:00 nginx: worker process
101 16858 16800 0 12:51 pts/0 00:00:00 nginx: worker process
顯然我們兩種引數nginx、nginx-debug的容器都啟動成功!
也就是說我們通過ENTRYPOINT ["/docker-entrypoint.sh"]
指定的命令在啟動時無論如何都會執行,並且可以接收到了docker run 的引數。
docker-entrypoint.sh是什麼?docker-entrypoint.sh這是一個預處理指令碼通常用來過濾命令列引數或者執行exec 來啟動容器為1的程式。
通過ENTRYPOINT+CMD實現命令預設引數或接收docker run 引數是一種非常流行並且有用的dockerfile編寫方式。
希望小作文對你有些許幫助,如果內容有錯誤請指正。
您可以隨意轉載、修改、釋出本文章,無需經過本人同意。 個人blog:iqsing.github.io