如何優(yōu)雅的關(guān)閉容器
1 信號(hào)
信號(hào)是事件發(fā)生時(shí)對(duì)進(jìn)程的通知機(jī)制,有時(shí)也稱之為軟件中斷。
# kill -l1) SIGHUP 2) SIGINT 3) SIGQUIT4) SIGILL 5) SIGTRAP 6) SIGABRT7) SIGBUS 8) SIGFPE 9) SIGKILL10) SIGUSR1 11) SIGSEGV 12) SIGUSR213) SIGPIPE 14) SIGALRM 15) SIGTERM... ...
1) SIGHUP?當(dāng)終端斷開(kāi)(掛機(jī))時(shí),將發(fā)送該信號(hào)給終端控制進(jìn)程。SIGHUP 信號(hào)還可用于守護(hù)進(jìn)程(比如,init 等)。許多守護(hù)進(jìn)程會(huì)在收到 SIGHUP 信號(hào)時(shí)重新進(jìn)行初始化并重讀配置文件。2) SIGINT?當(dāng)用戶鍵入終端中斷字符(通常為?Control-C?) 時(shí),終端驅(qū)動(dòng)程序?qū)l(fā)送該信號(hào)給前臺(tái)進(jìn)程組。該信號(hào)的默認(rèn)行為是終止進(jìn)程。3) SIGQUIT?當(dāng)用戶在鍵盤(pán)上鍵入退出字符(通常為?Control-\?)時(shí),該信號(hào)將發(fā)往前臺(tái)進(jìn)程組。默認(rèn)情況下,該信號(hào)終止進(jìn)程,并生成用于調(diào)試的核心轉(zhuǎn)儲(chǔ)文件。進(jìn)程如果陷入無(wú)限循環(huán),或者不再響應(yīng)時(shí),使用 SIGQUIT 信號(hào)就很合適。9) SIGKILL?此信號(hào)為 “必殺(sure kill)” 信號(hào),處理器程序無(wú)法將其阻塞、忽略或者捕獲,故而 “一擊必殺”,總能終止程序。15) SIGTERM?這是用來(lái)終止進(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)來(lái)終止進(jìn)程,而把?SIGKILL?作為最后手段,去對(duì)付那些不響應(yīng)?SIGTERM?信號(hào)的失控進(jìn)程。20) SIGTSTP?這是作業(yè)控制的停止信號(hào),當(dāng)用戶在鍵盤(pán)上輸入掛起字符(通常為 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)管道(比如可以通過(guò)?Control-D?退出當(dāng)前 shell)。如果程序不讀取當(dāng)前輸入的話,是不受 Control-D 影響的。

以上知識(shí)大部分都來(lái)自 《Linux/UNIX 系統(tǒng)編程手冊(cè)》,想要了解更多的,可以查看該書(shū)上冊(cè)的 20、21、22 章節(jié)。
2 ENTRYPOINT 、 CMD
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)閉了。
? ~ docker stop --helpUsage: docker stop [OPTIONS] CONTAINER [CONTAINER...]Stop one or more running containersOptions:--help Print usage-t, --time int Seconds to wait for stop before killing it (default 10)
docker stop?停掉容器的時(shí)候,默認(rèn)會(huì)發(fā)送一個(gè)?SIGTERM?的信號(hào),默認(rèn) 10s 后容器沒(méi)有停止的話,就?SIGKILL?強(qiáng)制停止容器。通過(guò)?-t?選項(xiàng)可以設(shè)置等待時(shí)間。? ~ docker kill --helpUsage: docker kill [OPTIONS] CONTAINER [CONTAINER...]Kill one or more running containersOptions:--help Print usage-s, --signal string Signal to send to the container (default "KILL")
通過(guò)
docker kill的-s選項(xiàng)還可以指定給容器發(fā)送的信號(hào)。
Dockerfile?中通過(guò) exec 格式執(zhí)行容器啟動(dòng)命令就相安無(wú)事了?那當(dāng)然是,沒(méi)有那么簡(jiǎn)單的了,接下來(lái)我們通過(guò)實(shí)例來(lái)看看具體的效果是怎么樣的。3 實(shí)例
通過(guò) Go 寫(xiě)一個(gè)簡(jiǎn)單的信號(hào)處理器:
? ~ cat signals.gopackage mainimport ("fmt""os""os/signal""syscall")func main() {sigs := make(chan os.Signal, 1)done := make(chan bool, 1)signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)go func() {sig := <-sigsfmt.Println()fmt.Println(sig)done <- true}()fmt.Println("awaiting signal")<-donefmt.Println("exiting")}
3.1 實(shí)例 1
? ~ GOOS=linux GOARCH=amd64 go build signals.go? ~ lsDockerfile signals signals.go? ~ cat DockerfileFROM busyboxCOPY signals /signalsCMD ["/signals"] # exec 格式執(zhí)行? ~ docker build -t signals .
通過(guò) tmux 開(kāi)啟兩個(gè)面板,一個(gè)運(yùn)行容器,一個(gè)執(zhí)行 docker stop :? ~ docker run -it --rm --name signals signalsawaiting signalterminatedexiting
? ~ time docker stop signalssignalsdocker stop signals 0.01s user 0.02s system 4% cpu 0.732 total? ~
可以發(fā)現(xiàn),容器停止之前,程序接收到信號(hào)并輸出相應(yīng)信息,并且停止總耗時(shí)為 0.732 s,達(dá)到了優(yōu)雅的效果。
修改 Dockerfile 中 CMD 執(zhí)行格式,執(zhí)行相同操作:
? ~ cat DockerfileFROM busyboxCOPY signals /signalsCMD /signals # shell 格式執(zhí)行? ~ docker build -t signals .
? ~ docker run -it --rm --name signals signalsawaiting signal? ~
? ~ time docker stop signalssignalsdocker stop signals 0.01s user 0.01s system 0% cpu 10.719 total
通過(guò) shell 格式之后,可以發(fā)現(xiàn)容器停止之前,程序并未接收到任何信號(hào),并且停止時(shí)間為 10.719s,說(shuō)明該容器是被強(qiáng)制停止的。
結(jié)論很明顯,為了優(yōu)雅的退出容器,我們應(yīng)該采用 exec 這種格式。
3.2 實(shí)例 2
? ~ lsDockerfile signals signals.go start.sh? ~ cat DockerfileFROM busyboxCOPY signals /signalsCOPY start.sh /start.sh # 引入 shell 腳本啟動(dòng)CMD ["/start.sh"]? ~ cat start.sh#!/bin/sh/signals? ~
測(cè)試依然引用實(shí)例 1 中的方法:
? ~ docker run -it --rm --name signals signalsawaiting signal? ~
? ~ time docker stop signalssignalsdocker stop signals 0.01s user 0.02s system 0% cpu 10.765 total? ~
? ~ cat start.sh#!/bin/shexec /signals # 加入 exec 執(zhí)行? ~ docker build -t signals .
? ~ docker run -it --rm --name signals signalsawaiting signalterminatedexiting
? ~ time docker stop signalssignalsdocker stop signals 0.02s user 0.02s system 4% cpu 0.744 total? ~
可以看到,加入 exec 命令之后,程序又可以接收到信號(hào)正常退出了。當(dāng)然,如果你 Dockerfile 中的 CMD 是以 shell 格式運(yùn)行的,即使啟動(dòng)腳本中加入 exec 也是無(wú)效的。再者,如果你的程序本身不能針對(duì)信號(hào)做一些處理,也就談不上優(yōu)雅關(guān)閉了。
鏈接:https://blog.opskumu.com/graceful-shutdown-docker.html
(版權(quán)歸原作者所有,侵刪)
/yugemengjing/article/detai

往期推薦
