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

以上知識大部分都來自 《Linux/UNIX 系統(tǒng)編程手冊》,想要了解更多的,可以查看該書上冊的 20、21、22 章節(jié)。
2 ENTRYPOINT 、 CMD
可能有人會問,說了半天,那信號和優(yōu)雅的關(guān)閉容器有半毛錢的關(guān)系???話說,這和錢確實沒關(guān)系,但是和如何優(yōu)雅關(guān)閉容器卻關(guān)系密切。接著說 Dockerfile 中的 ENTRYPOINT 和 CMD 指令,它們的主要功能是指定容器啟動時執(zhí)行的程序。
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 格式下不會傳遞任何信號給程序。這也就導(dǎo)致,在docker stop容器的時候,以這種格式運行的程序捕捉不到發(fā)送的信號,也就談不上優(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停掉容器的時候,默認會發(fā)送一個SIGTERM的信號,默認 10s 后容器沒有停止的話,就SIGKILL強制停止容器。通過-t選項可以設(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")
通過 docker kill的-s選項還可以指定給容器發(fā)送的信號。
Dockerfile中通過 exec 格式執(zhí)行容器啟動命令就相安無事了?那當(dāng)然是,沒有那么簡單的了,接下來我們通過實例來看看具體的效果是怎么樣的。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),容器停止之前,程序接收到信號并輸出相應(yīng)信息,并且停止總耗時為 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,說明該容器是被強制停止的。
結(jié)論很明顯,為了優(yōu)雅的退出容器,我們應(yīng)該采用 exec 這種格式。
3.2 實例 2
通過實例 1 我們都會在 Dockerfile 中都會通過 exec 這種格式來執(zhí)行程序了,那如果執(zhí)行的程序本身也是一個 shell 腳本呢?? ~ 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? ~
可以發(fā)現(xiàn),即使 Dockerfile 中的 CMD 指令使用的是 exec 格式,容器中的程序依然沒有接收到信號,最后被強制關(guān)閉。因為 shell 腳本中執(zhí)行的原因,導(dǎo)致信號依然沒有被傳遞,我們需要針對 shell 腳本做一些改造:? ~ 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 命令之后,程序又可以接收到信號正常退出了。當(dāng)然,如果你 Dockerfile 中的 CMD 是以 shell 格式運行的,即使啟動腳本中加入 exec 也是無效的。再者,如果你的程序本身不能針對信號做一些處理,也就談不上優(yōu)雅關(guān)閉了。
原文標(biāo)題:如何優(yōu)雅的關(guān)閉容器
文章出處:【微信公眾號:馬哥Linux運維】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
-
Linux
+關(guān)注
關(guān)注
88文章
11760瀏覽量
219047 -
信號
+關(guān)注
關(guān)注
12文章
2914瀏覽量
80144 -
容器
+關(guān)注
關(guān)注
0文章
531瀏覽量
22968 -
Docker
+關(guān)注
關(guān)注
0文章
532瀏覽量
14248
原文標(biāo)題:如何優(yōu)雅的關(guān)閉容器
文章出處:【微信號:magedu-Linux,微信公眾號:馬哥Linux運維】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
一文詳解DockerFile基礎(chǔ)知識
系統(tǒng)之上的進程級別虛擬化技術(shù)——Docker
全面詳解Dockerfile文件
鏡像構(gòu)建Dockerfile的介紹
Dockerfile的最佳實踐
使用匿名管道技術(shù)獲取CMD命令的執(zhí)行結(jié)果
Dockerfile定義Docker鏡像的構(gòu)建過程
如何使用dockerfile創(chuàng)建鏡像
提升DevOps效率,從基礎(chǔ)到進階的Dockerfile編寫技巧
Dockerfile鏡像制作與Docker-Compose容器編排
基于Docker鏡像逆向生成Dockerfile
信號的理念以及Dockerfile中ENTRYPOINT和CMD指令
評論