如何優(yōu)雅的關閉容器
1 信號
信號是事件發(fā)生時對進程的通知機制,有時也稱之為軟件中斷。
# 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?當終端斷開(掛機)時,將發(fā)送該信號給終端控制進程。SIGHUP 信號還可用于守護進程(比如,init 等)。許多守護進程會在收到 SIGHUP 信號時重新進行初始化并重讀配置文件。2) SIGINT?當用戶鍵入終端中斷字符(通常為?Control-C?) 時,終端驅動程序將發(fā)送該信號給前臺進程組。該信號的默認行為是終止進程。3) SIGQUIT?當用戶在鍵盤上鍵入退出字符(通常為?Control-\?)時,該信號將發(fā)往前臺進程組。默認情況下,該信號終止進程,并生成用于調試的核心轉儲文件。進程如果陷入無限循環(huán),或者不再響應時,使用 SIGQUIT 信號就很合適。9) SIGKILL?此信號為 “必殺(sure kill)” 信號,處理器程序無法將其阻塞、忽略或者捕獲,故而 “一擊必殺”,總能終止程序。15) SIGTERM?這是用來終止進程的標準信號,也是?kill?、?killall?、?pkill?命令所發(fā)送的默認信號。精心設計的應用程序應當為 SIGTERM 信號設置處理器程序,以便其能夠預先清除臨時文件和釋放其它資源,從而全身而退。因此,總是應該先嘗試使用?SIGTERM?信號來終止進程,而把?SIGKILL?作為最后手段,去對付那些不響應?SIGTERM?信號的失控進程。20) SIGTSTP?這是作業(yè)控制的停止信號,當用戶在鍵盤上輸入掛起字符(通常為 Control-Z )時,將該信號給前臺進程組,使其停止運行。
值得注意的是,?Control-D?不會發(fā)起信號,它表示 EOF(End-Of-File),關閉標準輸入(stdin)管道(比如可以通過?Control-D?退出當前 shell)。如果程序不讀取當前輸入的話,是不受 Control-D 影響的。

以上知識大部分都來自 《Linux/UNIX 系統(tǒng)編程手冊》,想要了解更多的,可以查看該書上冊的 20、21、22 章節(jié)。
2 ENTRYPOINT 、 CMD
CMD?有三種格式:
CMD ["executable","param1","param2"]?(exec 格式, 推薦使用這種格式)CMD ["param1","param2"]?(作為 ENTRYPOINT 指令參數(shù))CMD command param1 param2?(shell 格式,默認 /bin/sh -c )
ENTRYPOINT?有兩種格式:
ENTRYPOINT ["executable", "param1", "param2"]?(exec 格式,推薦優(yōu)先使用這種格式)ENTRYPOINT command param1 param2?(shell 格式)
其中,不管你?Dockerfile?用其中哪個指令,兩個指令都推薦使用 exec 格式,而不是 shell 格式。原因就是因為使用 shell 格式之后,程序會以?/bin/sh -c?的子命令啟動,并且 shell 格式下不會傳遞任何信號給程序。這也就導致,在?docker stop?容器的時候,以這種格式運行的程序捕捉不到發(fā)送的信號,也就談不上優(yōu)雅的關閉了。
? ~ 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?停掉容器的時候,默認會發(fā)送一個?SIGTERM?的信號,默認 10s 后容器沒有停止的話,就?SIGKILL?強制停止容器。通過?-t?選項可以設置等待時間。? ~ 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")
通過
docker kill的-s選項還可以指定給容器發(fā)送的信號。
Dockerfile?中通過 exec 格式執(zhí)行容器啟動命令就相安無事了?那當然是,沒有那么簡單的了,接下來我們通過實例來看看具體的效果是怎么樣的。3 實例
通過 Go 寫一個簡單的信號處理器:
? ~ 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 實例 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 .
通過 tmux 開啟兩個面板,一個運行容器,一個執(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),容器停止之前,程序接收到信號并輸出相應信息,并且停止總耗時為 0.732 s,達到了優(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
通過 shell 格式之后,可以發(fā)現(xiàn)容器停止之前,程序并未接收到任何信號,并且停止時間為 10.719s,說明該容器是被強制停止的。
結論很明顯,為了優(yōu)雅的退出容器,我們應該采用 exec 這種格式。
3.2 實例 2
? ~ lsDockerfile signals signals.go start.sh? ~ cat DockerfileFROM busyboxCOPY signals /signalsCOPY start.sh /start.sh # 引入 shell 腳本啟動CMD ["/start.sh"]? ~ cat start.sh#!/bin/sh/signals? ~
測試依然引用實例 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 命令之后,程序又可以接收到信號正常退出了。當然,如果你 Dockerfile 中的 CMD 是以 shell 格式運行的,即使啟動腳本中加入 exec 也是無效的。再者,如果你的程序本身不能針對信號做一些處理,也就談不上優(yōu)雅關閉了。
鏈接:https://blog.opskumu.com/graceful-shutdown-docker.html
(版權歸原作者所有,侵刪)
/yugemengjing/article/detai

往期推薦
