成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

如何優雅的關閉容器,看本文就夠了

系統 Linux
要說如何優雅的關閉容器,那就不得不提到信號(Signal)的理念,以及 Dockerfile 中 ENTRYPOINT 和 CMD 指令了。在具體說優雅關閉之前,先了解一下信號這個 Linux 中的基礎概念。

 最近把 Docker 官方的 Docker Reference 文檔又讀了一遍,發現有些細節深究起來,還是有很多可挖的。針對寫 Dockerfile ,大部分時候只要照葫蘆畫瓢,基本也不會有什么大的問題,但是如果再深入理解一下那就更有意思了。

[[438741]]

要說如何優雅的關閉容器,那就不得不提到信號(Signal)的理念,以及 Dockerfile 中 ENTRYPOINT 和 CMD 指令了。在具體說優雅關閉之前,先了解一下信號這個 Linux 中的基礎概念。

1 信號

信號是事件發生時對進程的通知機制,有時也稱之為軟件中斷。

信號有不同的類型,Linux 對標準信號的編號為 1~31,可以通過 kill -l 獲取信號名稱: 

  1. # kill -l  
  2.  1) SIGHUP       2) SIGINT       3) SIGQUIT     
  3.  4) SIGILL       5) SIGTRAP      6) SIGABRT    
  4.  7) SIGBUS       8) SIGFPE       9) SIGKILL 
  5.  10) SIGUSR1    11) SIGSEGV     12) SIGUSR2 
  6.  13) SIGPIPE    14) SIGALRM     15) SIGTERM 
  7. ... ... 

實際列出的信號超過了 31 個,有些是其它名稱的同義詞,有些則是定義但未使用的。以下介紹幾個常用的信號:

  •  1) SIGHUP 當終端斷開(掛機)時,將發送該信號給終端控制進程。SIGHUP 信號還可用于守護進程(比如,init 等)。許多守護進程會在收到 SIGHUP 信號時重新進行初始化并重讀配置文件。
  •  2) SIGINT 當用戶鍵入終端中斷字符(通常為 Control-C ) 時,終端驅動程序將發送該信號給前臺進程組。該信號的默認行為是終止進程。
  •  3) SIGQUIT 當用戶在鍵盤上鍵入退出字符(通常為 Control-\ )時,該信號將發往前臺進程組。默認情況下,該信號終止進程,并生成用于調試的核心轉儲文件。進程如果陷入無限循環,或者不再響應時,使用 SIGQUIT 信號就很合適。
  •  9) SIGKILL 此信號為 “必殺(sure kill)” 信號,處理器程序無法將其阻塞、忽略或者捕獲,故而 “一擊必殺”,總能終止程序。
  •  15) SIGTERM 這是用來終止進程的標準信號,也是 kill 、 killall 、 pkill 命令所發送的默認信號。精心設計的應用程序應當為 SIGTERM 信號設置處理器程序,以便其能夠預先清除臨時文件和釋放其它資源,從而全身而退。因此,總是應該先嘗試使用 SIGTERM 信號來終止進程,而把 SIGKILL 作為最后手段,去對付那些不響應 SIGTERM 信號的失控進程。
  •  20) SIGTSTP 這是作業控制的停止信號,當用戶在鍵盤上輸入掛起字符(通常為 Control-Z )時,將該信號給前臺進程組,使其停止運行。

值得注意的是, Control-D 不會發起信號,它表示 EOF(End-Of-File),關閉標準輸入(stdin)管道(比如可以通過 Control-D 退出當前 shell)。如果程序不讀取當前輸入的話,是不受 Control-D 影響的。

程序可以針對信號捕捉,然后執行相應函數:

 

以上知識大部分都來自 《Linux/UNIX 系統編程手冊》,想要了解更多的,可以查看該書上冊的 20、21、22 章節。

2 ENTRYPOINT 、 CMD

可能有人會問,說了半天,那信號和優雅的關閉容器有半毛錢的關系啊?話說,這和錢確實沒關系,但是和如何優雅關閉容器卻關系密切。

接著說 Dockerfile 中的 ENTRYPOINT 和 CMD 指令,它們的主要功能是指定容器啟動時執行的程序。

CMD 有三種格式:

  •  CMD ["executable","param1","param2"] (exec 格式, 推薦使用這種格式)
  •  CMD ["param1","param2"] (作為 ENTRYPOINT 指令參數)
  •  CMD command param1 param2 (shell 格式,默認 /bin/sh -c )

ENTRYPOINT 有兩種格式:

  •  ENTRYPOINT ["executable", "param1", "param2"] (exec 格式,推薦優先使用這種格式)
  •  ENTRYPOINT command param1 param2 (shell 格式)

其中,不管你 Dockerfile 用其中哪個指令,兩個指令都推薦使用 exec 格式,而不是 shell 格式。原因就是因為使用 shell 格式之后,程序會以 /bin/sh -c 的子命令啟動,并且 shell 格式下不會傳遞任何信號給程序。這也就導致,在 docker stop 容器的時候,以這種格式運行的程序捕捉不到發送的信號,也就談不上優雅的關閉了。 

  1. ➜  ~ docker stop --help  
  2. Usage:  docker stop [OPTIONS] CONTAINER [CONTAINER...]  
  3. Stop one or more running containers  
  4. Options:  
  5.       --help       Print usage  
  6.   -t, --time int   Seconds to wait for stop before killing it (default 10) 

docker stop 停掉容器的時候,默認會發送一個 SIGTERM 的信號,默認 10s 后容器沒有停止的話,就 SIGKILL 強制停止容器。通過 -t 選項可以設置等待時間。 

  1. ➜  ~ docker kill --help  
  2. Usage:  docker kill [OPTIONS] CONTAINER [CONTAINER...]  
  3. Kill one or more running containers  
  4. Options:  
  5.       --help            Print usage  
  6.   -s, --signal string   Signal to send to the container (default "KILL") 

通過 docker kill 的 -s 選項還可以指定給容器發送的信號。

所以,說了那么多,只要 Dockerfile 中通過 exec 格式執行容器啟動命令就相安無事了?那當然是,沒有那么簡單的了,接下來我們通過實例來看看具體的效果是怎么樣的。

3 實例

通過 Go 寫一個簡單的信號處理器: 

  1. ➜  ~ cat signals.go  
  2. package main  
  3. import ( 
  4.     "fmt"  
  5.     "os"  
  6.     "os/signal"  
  7.     "syscall"  
  8. func main() {  
  9.     sigs :make(chan os.Signal, 1)  
  10.     done :make(chan bool, 1)  
  11.     signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)  
  12.     go func() {  
  13.         sig :<-sigs  
  14.         fmt.Println()  
  15.         fmt.Println(sig)  
  16.         done <- true  
  17.     }()  
  18.     fmt.Println("awaiting signal")  
  19.     <-done  
  20.     fmt.Println("exiting")  

3.1 實例 1 

  1. ➜  ~ GOOS=linux GOARCH=amd64 go build signals.go  
  2. ➜  ~ ls  
  3. Dockerfile signals    signals.go  
  4. ➜  ~ cat Dockerfile  
  5. FROM busybox  
  6. COPY signals /signals  
  7. CMD ["/signals"]    # exec 格式執行  
  8. ➜  ~ docker build -t signals . 

通過 tmux 開啟兩個面板,一個運行容器,一個執行 docker stop : 

  1. ➜  ~ docker run -it --rm --name signals signals  
  2. awaiting signal  
  3. terminated  
  4. exiting  
  1. ➜  ~ time docker stop signals  
  2. signals  
  3. docker stop signals  0.01s user 0.02s system 4% cpu 0.732 total 
  4. ➜  ~ 

可以發現,容器停止之前,程序接收到信號并輸出相應信息,并且停止總耗時為 0.732 s,達到了優雅的效果。

修改 Dockerfile 中 CMD 執行格式,執行相同操作: 

  1. ➜  ~ cat Dockerfile  
  2. FROM busybox  
  3. COPY signals /signals  
  4. CMD /signals        # shell 格式執行  
  5. ➜  ~ docker build -t signals .  
  1. ➜  ~ docker run -it --rm --name signals signals  
  2. awaiting signal  
  3. ➜  ~  
  1. ➜  ~ time docker stop signals  
  2. signals  
  3. docker stop signals  0.01s user 0.01s system 0% cpu 10.719 total 

通過 shell 格式之后,可以發現容器停止之前,程序并未接收到任何信號,并且停止時間為 10.719s,說明該容器是被強制停止的。

結論很明顯,為了優雅的退出容器,我們應該采用 exec 這種格式。

3.2 實例 2

通過實例 1 我們都會在 Dockerfile 中都會通過 exec 這種格式來執行程序了,那如果執行的程序本身也是一個 shell 腳本呢? 

  1. ➜  ~ ls  
  2. Dockerfile signals    signals.go start.sh  
  3. ➜  ~ cat Dockerfile  
  4. FROM busybox  
  5. COPY signals /signals  
  6. COPY start.sh /start.sh     # 引入 shell 腳本啟動  
  7. CMD ["/start.sh"]  
  8. ➜  ~ cat start.sh  
  9. #!/bin/sh  
  10. /signals  
  11. ➜  ~ 

測試依然引用實例 1 中的方法: 

  1. ➜  ~ docker run -it --rm --name signals signals  
  2. awaiting signal  
  3. ➜  ~  
  1. ➜  ~ time docker stop signals  
  2. signals  
  3. docker stop signals  0.01s user 0.02s system 0% cpu 10.765 total  
  4. ➜  ~ 

可以發現,即使 Dockerfile 中的 CMD 指令使用的是 exec 格式,容器中的程序依然沒有接收到信號,最后被強制關閉。因為 shell 腳本中執行的原因,導致信號依然沒有被傳遞,我們需要針對 shell 腳本做一些改造: 

  1. ➜  ~ cat start.sh  
  2. #!/bin/sh 
  3. exec /signals   # 加入 exec 執行  
  4. ➜  ~ docker build -t signals .  
  1. ➜  ~ docker run -it --rm --name signals signals  
  2. awaiting signal  
  3. terminated  
  4. exiting  
  1. ➜  ~ time docker stop signals  
  2. signals  
  3. docker stop signals  0.02s user 0.02s system 4% cpu 0.744 total  
  4. ➜  ~ 

可以看到,加入 exec 命令之后,程序又可以接收到信號正常退出了。當然,如果你 Dockerfile 中的 CMD 是以 shell 格式運行的,即使啟動腳本中加入 exec 也是無效的。再者,如果你的程序本身不能針對信號做一些處理,也就談不上優雅關閉了。 

 

責任編輯:龐桂玉 來源: Linux公社
相關推薦

2020-12-11 08:04:01

腳本動態 Async

2020-02-24 19:28:23

Rust語言Web框架應用領域

2021-01-19 10:35:49

JVM場景函數

2019-10-09 10:06:48

容器監控軟件

2022-04-11 08:17:07

JVMJava進程

2021-03-28 09:17:18

JVM場景鉤子函數

2017-12-19 10:03:44

JavaLinux代碼

2018-09-12 15:16:19

數據中心網絡機房

2024-11-13 16:37:00

Java線程池

2018-08-17 09:14:43

餓了么容器演進

2024-02-04 09:08:00

Autofac容器.NET

2023-11-22 07:54:33

Xargs命令Linux

2025-02-14 08:53:24

2023-10-17 08:15:28

API前后端分離

2021-04-24 23:06:47

JavaScript編程語言

2017-10-23 11:56:35

路由器升級更新

2022-03-13 09:31:43

MQ消息隊列ActiveMQ

2019-08-16 09:41:56

UDP協議TCP

2021-09-30 07:59:06

zookeeper一致性算法CAP

2021-12-21 15:17:53

Kubernetes緩存Linux
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 日韩成人免费视频 | 成人av影院 | 成年男女免费视频网站 | 久久久激情 | 国产99久久精品 | 亚洲精品久久久久久久久久久久久 | 欧美一级观看 | 巨大荫蒂视频欧美另类大 | 亚洲视频在线观看免费 | 一区二区在线 | 99久久精品免费看国产免费软件 | 粉嫩粉嫩芽的虎白女18在线视频 | 91超碰在线观看 | 欧美性大战久久久久久久蜜臀 | 一级毛片色一级 | 国产精品视频yy9299一区 | 99re99 | 亚洲 中文 欧美 日韩 在线观看 | 亚洲精品在线视频 | 99免费在线观看视频 | 91中文字幕在线 | 日韩欧美网 | 亚洲久草视频 | 国产成人精品一区二区三区视频 | 国产精品久久久久久久久久三级 | 欧美大片久久久 | 欧美黄色小视频 | 久久精品国产一区二区电影 | 国内久久精品 | 国产精品爱久久久久久久 | 完全免费在线视频 | 国产精品爱久久久久久久 | 日韩免费在线 | 免费观看一级毛片 | 欧美一区免费 | 亚洲成人精品视频 | 超碰精品在线 | 久草视频2 | 成人在线精品视频 | 91av视频在线免费观看 | 国产精品大全 |