最近把 Docker 官方的 Docker Reference 文檔又讀了一遍,發(fā)現(xiàn)有些細(xì)節(jié)深究起來,還是有很多可挖的。針對(duì)寫 Dockerfile ,大部分時(shí)候只要照葫蘆畫瓢,基本也不會(huì)有什么大的問題,但是如果再深入理解一下那就更有意思了。
要說如何優(yōu)雅的關(guān)閉容器,那就不得不提到信號(hào)(Signal)的理念,以及 Dockerfile 中 ENTRYPOINT 和 CMD 指令了。在具體說優(yōu)雅關(guān)閉之前,先了解一下信號(hào)這個(gè) Linux 中的基礎(chǔ)概念。
1 信號(hào)
信號(hào)是事件發(fā)生時(shí)對(duì)進(jìn)程的通知機(jī)制,有時(shí)也稱之為軟件中斷。
信號(hào)有不同的類型,Linux 對(duì)標(biāo)準(zhǔn)信號(hào)的編號(hào)為 1~31,可以通過 kill -l 獲取信號(hào)名稱:
#kill-l 1)SIGHUP2)SIGINT3)SIGQUIT 4)SIGILL5)SIGTRAP6)SIGABRT 7)SIGBUS8)SIGFPE9)SIGKILL 10)SIGUSR111)SIGSEGV12)SIGUSR2 13)SIGPIPE14)SIGALRM15)SIGTERM ......
實(shí)際列出的信號(hào)超過了 31 個(gè),有些是其它名稱的同義詞,有些則是定義但未使用的。以下介紹幾個(gè)常用的信號(hào):
SIGHUP 當(dāng)終端斷開(掛機(jī))時(shí),將發(fā)送該信號(hào)給終端控制進(jìn)程。SIGHUP 信號(hào)還可用于守護(hù)進(jìn)程(比如,init 等)。許多守護(hù)進(jìn)程會(huì)在收到 SIGHUP 信號(hào)時(shí)重新進(jìn)行初始化并重讀配置文件。
SIGINT 當(dāng)用戶鍵入終端中斷字符(通常為 Control-C ) 時(shí),終端驅(qū)動(dòng)程序?qū)l(fā)送該信號(hào)給前臺(tái)進(jìn)程組。該信號(hào)的默認(rèn)行為是終止進(jìn)程。
SIGQUIT 當(dāng)用戶在鍵盤上鍵入退出字符(通常為 Control- )時(shí),該信號(hào)將發(fā)往前臺(tái)進(jìn)程組。默認(rèn)情況下,該信號(hào)終止進(jìn)程,并生成用于調(diào)試的核心轉(zhuǎn)儲(chǔ)文件。進(jìn)程如果陷入無限循環(huán),或者不再響應(yīng)時(shí),使用 SIGQUIT 信號(hào)就很合適。
SIGKILL 此信號(hào)為 “必殺(sure kill)” 信號(hào),處理器程序無法將其阻塞、忽略或者捕獲,故而 “一擊必殺”,總能終止程序。
SIGTERM 這是用來終止進(jìn)程的標(biāo)準(zhǔn)信號(hào),也是 kill 、 killall 、 pkill 命令所發(fā)送的默認(rèn)信號(hào)。精心設(shè)計(jì)的應(yīng)用程序應(yīng)當(dāng)為 SIGTERM 信號(hào)設(shè)置處理器程序,以便其能夠預(yù)先清除臨時(shí)文件和釋放其它資源,從而全身而退。因此,總是應(yīng)該先嘗試使用 SIGTERM 信號(hào)來終止進(jìn)程,而把 SIGKILL 作為最后手段,去對(duì)付那些不響應(yīng) SIGTERM 信號(hào)的失控進(jìn)程。
SIGTSTP 這是作業(yè)控制的停止信號(hào),當(dāng)用戶在鍵盤上輸入掛起字符(通常為 Control-Z )時(shí),將該信號(hào)給前臺(tái)進(jìn)程組,使其停止運(yùn)行。
值得注意的是, Control-D 不會(huì)發(fā)起信號(hào),它表示 EOF(End-Of-File),關(guān)閉標(biāo)準(zhǔn)輸入(stdin)管道(比如可以通過 Control-D 退出當(dāng)前 shell)。如果程序不讀取當(dāng)前輸入的話,是不受 Control-D 影響的。
程序可以針對(duì)信號(hào)捕捉,然后執(zhí)行相應(yīng)函數(shù):

以上知識(shí)大部分都來自 《Linux/UNIX 系統(tǒng)編程手冊(cè)》,想要了解更多的,可以查看該書上冊(cè)的 20、21、22 章節(jié)。
2 ENTRYPOINT 、 CMD
可能有人會(huì)問,說了半天,那信號(hào)和優(yōu)雅的關(guān)閉容器有半毛錢的關(guān)系啊?話說,這和錢確實(shí)沒關(guān)系,但是和如何優(yōu)雅關(guān)閉容器卻關(guān)系密切。
接著說 Dockerfile 中的 ENTRYPOINT 和 CMD 指令,它們的主要功能是指定容器啟動(dòng)時(shí)執(zhí)行的程序。
CMD 有三種格式:
CMD ["executable","param1","param2"] (exec 格式, 推薦使用這種格式)
CMD ["param1","param2"] (作為 ENTRYPOINT 指令參數(shù))
CMD command param1 param2 (shell 格式,默認(rèn) /bin/sh -c )
ENTRYPOINT 有兩種格式:
ENTRYPOINT ["executable", "param1", "param2"] (exec 格式,推薦優(yōu)先使用這種格式)
ENTRYPOINT command param1 param2 (shell 格式)
其中,不管你 Dockerfile 用其中哪個(gè)指令,兩個(gè)指令都推薦使用 exec 格式,而不是 shell 格式。原因就是因?yàn)槭褂?shell 格式之后,程序會(huì)以 /bin/sh -c 的子命令啟動(dòng),并且 shell 格式下不會(huì)傳遞任何信號(hào)給程序。這也就導(dǎo)致,在 docker stop 容器的時(shí)候,以這種格式運(yùn)行的程序捕捉不到發(fā)送的信號(hào),也就談不上優(yōu)雅的關(guān)閉了。
?~dockerstop--help Usage:dockerstop[OPTIONS]CONTAINER[CONTAINER...] Stoponeormorerunningcontainers Options: --helpPrintusage -t,--timeintSecondstowaitforstopbeforekillingit(default10)
docker stop 停掉容器的時(shí)候,默認(rèn)會(huì)發(fā)送一個(gè) SIGTERM 的信號(hào),默認(rèn) 10s 后容器沒有停止的話,就 SIGKILL 強(qiáng)制停止容器。通過 -t 選項(xiàng)可以設(shè)置等待時(shí)間。
?~dockerkill--help Usage:dockerkill[OPTIONS]CONTAINER[CONTAINER...] Killoneormorerunningcontainers Options: --helpPrintusage -s,--signalstringSignaltosendtothecontainer(default"KILL")
通過 docker kill的-s選項(xiàng)還可以指定給容器發(fā)送的信號(hào)。
所以,說了那么多,只要 Dockerfile 中通過 exec 格式執(zhí)行容器啟動(dòng)命令就相安無事了?那當(dāng)然是,沒有那么簡單的了,接下來我們通過實(shí)例來看看具體的效果是怎么樣的。
3 實(shí)例
通過 Go 寫一個(gè)簡單的信號(hào)處理器:
?~catsignals.go
packagemain
import(
"fmt"
"os"
"os/signal"
"syscall"
)
funcmain(){
sigs:=make(chanos.Signal,1)
done:=make(chanbool,1)
signal.Notify(sigs,syscall.SIGINT,syscall.SIGTERM)
gofunc(){
sig:=<-sigs
????????fmt.Println()
????????fmt.Println(sig)
????????done?<-?true
????}()
????fmt.Println("awaiting?signal")
????<-done
????fmt.Println("exiting")
}
3.1 實(shí)例 1
?~GOOS=linuxGOARCH=amd64gobuildsignals.go ?~ls Dockerfilesignalssignals.go ?~catDockerfile FROMbusybox COPYsignals/signals CMD["/signals"]#exec格式執(zhí)行 ?~dockerbuild-tsignals.
通過 tmux 開啟兩個(gè)面板,一個(gè)運(yùn)行容器,一個(gè)執(zhí)行 docker stop :
?~dockerrun-it--rm--namesignalssignals awaitingsignal terminated exiting
?~timedockerstopsignals signals dockerstopsignals0.01suser0.02ssystem4%cpu0.732total ?~
可以發(fā)現(xiàn),容器停止之前,程序接收到信號(hào)并輸出相應(yīng)信息,并且停止總耗時(shí)為 0.732 s,達(dá)到了優(yōu)雅的效果。
修改 Dockerfile 中 CMD 執(zhí)行格式,執(zhí)行相同操作:
?~catDockerfile FROMbusybox COPYsignals/signals CMD/signals#shell格式執(zhí)行 ?~dockerbuild-tsignals.
?~dockerrun-it--rm--namesignalssignals awaitingsignal ?~
?~timedockerstopsignals signals dockerstopsignals0.01suser0.01ssystem0%cpu10.719total
通過 shell 格式之后,可以發(fā)現(xiàn)容器停止之前,程序并未接收到任何信號(hào),并且停止時(shí)間為 10.719s,說明該容器是被強(qiáng)制停止的。
結(jié)論很明顯,為了優(yōu)雅的退出容器,我們應(yīng)該采用 exec 這種格式。
3.2 實(shí)例 2
通過實(shí)例 1 我們都會(huì)在 Dockerfile 中都會(huì)通過 exec 這種格式來執(zhí)行程序了,那如果執(zhí)行的程序本身也是一個(gè) shell 腳本呢?
?~ls Dockerfilesignalssignals.gostart.sh ?~catDockerfile FROMbusybox COPYsignals/signals COPYstart.sh/start.sh#引入shell腳本啟動(dòng) CMD["/start.sh"] ?~catstart.sh #!/bin/sh /signals ?~
測(cè)試依然引用實(shí)例 1 中的方法:
?~dockerrun-it--rm--namesignalssignals awaitingsignal ?~
?~timedockerstopsignals signals dockerstopsignals0.01suser0.02ssystem0%cpu10.765total ?~
可以發(fā)現(xiàn),即使 Dockerfile 中的 CMD 指令使用的是 exec 格式,容器中的程序依然沒有接收到信號(hào),最后被強(qiáng)制關(guān)閉。因?yàn)?shell 腳本中執(zhí)行的原因,導(dǎo)致信號(hào)依然沒有被傳遞,我們需要針對(duì) shell 腳本做一些改造:
?~catstart.sh #!/bin/sh exec/signals#加入exec執(zhí)行 ?~dockerbuild-tsignals.
?~dockerrun-it--rm--namesignalssignals awaitingsignal terminated exiting
?~timedockerstopsignals signals dockerstopsignals0.02suser0.02ssystem4%cpu0.744total ?~
可以看到,加入 exec 命令之后,程序又可以接收到信號(hào)正常退出了。當(dāng)然,如果你 Dockerfile 中的 CMD 是以 shell 格式運(yùn)行的,即使啟動(dòng)腳本中加入 exec 也是無效的。再者,如果你的程序本身不能針對(duì)信號(hào)做一些處理,也就談不上優(yōu)雅關(guān)閉了。
審核編輯:湯梓紅
-
Linux
+關(guān)注
關(guān)注
88文章
11760瀏覽量
219018 -
信號(hào)
+關(guān)注
關(guān)注
12文章
2914瀏覽量
80124 -
容器
+關(guān)注
關(guān)注
0文章
531瀏覽量
22965 -
命令
+關(guān)注
關(guān)注
5文章
755瀏覽量
23753 -
Docker
+關(guān)注
關(guān)注
0文章
532瀏覽量
14243
原文標(biāo)題:如何優(yōu)雅地關(guān)閉容器
文章出處:【微信號(hào):magedu-Linux,微信公眾號(hào):馬哥Linux運(yùn)維】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
200.200 尚硅谷 SparkStreaming 優(yōu)雅地關(guān)閉 恢復(fù)數(shù)據(jù)
神舟優(yōu)雅U10R上網(wǎng)本 參考價(jià)格:2499元
熱門低價(jià)3G上網(wǎng)本推薦 神舟優(yōu)雅U20Y 參考售價(jià):2399元
如何優(yōu)雅地完成倒計(jì)時(shí)定時(shí)器自適應(yīng)顯示呢
磁通電容器的資料分享
利用golang優(yōu)雅的實(shí)現(xiàn)單實(shí)例分享
經(jīng)驗(yàn)分享|BI數(shù)據(jù)可視化報(bào)表布局——容器
如何通過ESP模塊打開/關(guān)閉樹莓派GPIO電路?
華為nfc怎么關(guān)閉
單片機(jī)優(yōu)雅的開發(fā)Clion環(huán)境搭建
Tokio 模塊的優(yōu)雅停機(jī)機(jī)制
linux關(guān)閉docker的命令
優(yōu)雅停機(jī)是什么?SpringBoot+Nacos+k8s實(shí)現(xiàn)優(yōu)雅停機(jī)
如何優(yōu)雅地關(guān)閉容器
評(píng)論