您好,登錄后才能下訂單哦!
今天就跟大家聊聊有關ENTRYPOINT于CMD命令在Dockerfile中使用有哪些區別,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。
CMD 指令
CMD 指令指定容器啟動時需要運行的程序。一般用最簡單的方式啟動一個容器時使用docker run 會傳遞參數給docker指令
docker run -it image /bin/bash
后面的/bin/bash 其實是傳遞參數,告知容器啟動時運行一個shell。這個過程可以用CMD 指令等效的替換
CMD ['/bin/bash']
因此在Dockerfile中存在這個CMD指令指定的命令時,啟動容器就可以不進行參數傳遞。
docker run -it image
執行效果一致。
[root@MiWiFi-R3L-srv test]# docker run --name test -it test_image
[root@3a1bb0c9e35c /]#
如果dockerfile中已經指定了容器啟動時運行的程序,同時在使用docker run 啟動容器時使用了命令行參數,那么dockerfile 中的cmd 指令將無效
docker run -it image /bin/ps
發現啟動容器后沒有shell ,只是打印出了當前容器中的進程狀態,cmd 指令效果被覆蓋。
PID TTY TIME CMD 1 ? 00:00:00 ps [root@MiWiFi-R3L-srv test]#
此時可以看到cmd 效果被覆蓋。在一個dockerfile中只有最后一個cmd 指令有效,因此一個dockerfile中只寫一個cmd 指令。
ENTRYPOINT 指令
ENTRYPOINT 指令效果與CMD 非常的類似,比較容易混淆兩者的功能。最大的區別在于使用的方式,ENTRYPOINT 指定的命令需要與docker run 啟動容器進行搭配,將docker run 指令后面跟的內容當做參數作為ENTRYPOINT指令指定的運行命令的參數,ENTRYPOINT 指定的linux命令一般是不會被覆蓋的。
以nginx 鏡像為例說明
首先構建一個nginx鏡像,并且指定容器運行時執行的程序為nginx。
FROM centos MAINTAINER allocator RUN yum install -y nginx RUN echo 'hello world' > /usr/share/nginx/html/index.html EXPOSE 80 ENTRYPOINT ["/usr/sbin/nginx"]
然后啟動鏡像
docker run --name test -p 5000:80 -it test_nginx -g "daemon off"
后面兩個是作為參數傳遞給nginx啟動程序運行,此時nginx作為前臺程序運行,是一個web服務器,可以根據外部綁定的端口,通過瀏覽器正常看到hello world
兩者聯合使用技巧
已經明白了兩者的區別,可以利用兩者的特點構建一個含有默認啟動運行程序的鏡像,并且支持docker run 啟動時人為指定啟動程序運行的參數。
舉個例子。利用ENTRYPOINT 指定啟動時運行啟動nginx程序,并給定默認的運行參數為顯示幫助信息,dockerfile 構建如下:
ENTRYPOINT ["/usr/sbin/nginx"]
CMD ["-h"]
當使用docker run --name test -it test_nginx 不傳遞任何參數時,此時啟動容器會使用cmd 指令后的命令作為默認參數,打印nginx的幫助信息。此時cmd 后的內容并不是一個完整的指令,而是參數,如果其內容是一個完整的指令,那么它將覆蓋掉ENTRYPOINT 中的內容。
如果使用docker run --name test -it test_nginx -g "daemon off" 啟動時,此時給定的運行參數會覆蓋掉CMD 指令對應的內容,此時nginx將作為前臺進程運行,作為一個web服務器使用,通過browser可以看到hello world
補充知識:docker-entrypoint.sh 入口文件編寫技巧
在 docker 的官方 Registry 中(store.docker.com) 流行的第三方應用在自己的頁面中都提供了 dockerfile 的鏈接. 而很多 dockerfile 的 ENTRYPOINT 命令都是這么寫的 ["docker-entrypoint.sh"]
本篇文章就掃盲下 docker-entrypoint.sh 的特殊用法和設計邏輯
set -e
你寫的每個腳本都應該在文件開頭加上set -e, 這句語句告訴bash如果任何語句的執行結果不是true則應該退出. 這樣的好處是防止錯誤像滾雪球般變大導致一個致命的錯誤, 而這些錯誤本應該在之前就被處理掉. 如果要增加可讀性, 可以使用set -o errexit, 它的作用與set -e相同
set -o pipefail
設計用途同上, 就是希望在執行錯誤之后立即退出, 不要再向下執行了. 而 -o pipefail 的作用域是管道, 也就是說在 Linux 腳本中的管道, 如果前面的命令執行出了問題, 應該立即退出
shopt -s nullglob
在使用 Linux 中的通配符時 * ?等 如果沒有匹配到任何文件, 不會報 No such file or directory 而是將命令后面的參數去掉執行
if [ “${1:0:1}” = ‘-‘ ]; then…
這是一個判斷語句, 在官方文件中, 上一行已經給出了注釋: if command starts with an option, prepend mysqld
這個判斷語句是 ${1:0:1} 意思是判斷 $1(調用該腳本的第一個參數), 偏移量0(不偏移), 取一個字符(取字符串的長度)
如果判斷出來調用這個腳本后面所跟的參數第一個字符是-中橫線的話, 就認為后面的所有字符串都是 mysqld 的啟動參數
上面的這個操作類似于 Python 的字符串切片
set – mysqld “$@”
在上面判斷完第一個參數是-開頭之后, 緊接著就執行了 set -- mysqld "$@" 這個命令. 使用了 set -- 的用法. set —會將他后面所有以空格區分的字符串, 按順序分別存儲到$1, $2, $3 變量中, 其中新的$@ 為 set — 后面的全部內容
舉例來說: bash docker-entrypoint.sh -f xxx.conf
在這種情況下, set -- mysqld "$@" 中的 $@ 的值為 -f xxx.conf
當執行完 set -- mysqld "$@" 這條命令后:
$1=mysqld $2=-f $3=xxx.conf $@=mysqld -f xxx.conf
可以看到, 當執行 docker-entrypoint.sh腳本的時候后面加了 -x形式的參數之后, $@的值發生的改變, 在原有$@值的基礎之上, 在前面又預添加了 mysqld 命令
exec “$@”
幾乎在每個 docker-entrypoint.sh 腳本的最后一行, 執行的都是 exec "$@"命令
這個命令的意義在于你已經為你的鏡像預想到了應該有的調用情況, 當實際使用鏡像的人執行了你沒有預料到的可執行命令時, 將會走到腳本的這最后一行, 去執行用戶新的可執行命令
情況判斷
上面直接說了腳本的最后一行, 在之前的腳本中, 需要充分的去考慮你自己的腳本可能會被調用的情況. 還是拿 MySQL 官方的 dockerfile 來說, 他判斷以下情況:
開頭是 - , 認為是參數的情況
開頭是 mysqld, 且用戶 id 為0 (root 用戶) 的情況
開頭是 mysqld 的情況
判斷完自己應用的所有調用形態之后, 最后應該加上exec "$@" 命令兜底
${mysql[@]}
Shell 中的數組, 直接執行 ${mysql[@]} 會把這個數組當做可執行程序來執行
➜ /tmp mysql=( mysql --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" ) ➜ /tmp echo ${mysql[1]} mysql ➜ /tmp echo ${mysql[2]} --protocol=socket ➜ /tmp echo ${mysql[3]} -uroot ➜ /tmp echo ${mysql[4]} -hlocalhost ➜ /tmp echo ${mysql[@]} mysql --protocol=socket -uroot -hlocalhost --socket= exec gosu mysql “$BASH_SOURCE” “$@”
這里的 gosu 命令, 是 Linux 中 sudo 命令的輕量級”替代品”
gosu 是一個 golang 語言開發的工具, 用來取代 shell 中的 sudo 命令. su 和 sudo 命令有一些缺陷, 主要是會引起不確定的 TTY, 對信號量的轉發也存在問題. 如果僅僅為了使用特定的用戶運行程序, 使用 su 或 sudo 顯得太重了, 為此 gosu 應運而生.
gosu 直接借用了 libcontainer 在容器中啟動應用程序的原理, 使用 /etc/passwd 處理應用程序. gosu 首先找出指定的用戶或用戶組, 然后切換到該用戶或用戶組. 接下來, 使用 exec 啟動應用程序. 到此為止, gosu 完成了它的工作, 不會參與到應用程序后面的聲明周期中. 使用這種方式避免了 gosu 處理 TTY 和轉發信號量的問題, 把這兩個工作直接交給了應用程序去完成
看完上述內容,你們對ENTRYPOINT于CMD命令在Dockerfile中使用有哪些區別有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。