在 docker 容器中捕獲訊號

2022-01-13 05:40:51 字數 3549 閱讀 3543

訊號是一種程序間通訊的形式。乙個訊號就是核心傳送給程序的乙個訊息,告訴程序發生了某種事件。當乙個訊號被傳送給乙個程序後,程序會立即中斷當前的執行流並開始執行訊號的處理程式(這麼說不太準確,訊號是在特定的時機被處理)。如果沒有為這個訊號指定處理程式,就執行預設的處理程式。

程序需要為自己感興趣的訊號註冊處理程式,比如為了能讓程式優雅的退出(接到退出的請求後能夠對資源進行清理)一般程式都會處理 sigterm 訊號。與 sigterm 訊號不同,sigkill 訊號會粗暴的結束乙個程序。因此我們的應用應該實現這樣的目錄:捕獲並處理 sigterm 訊號,從而優雅的退出程式。如果我們失敗了,使用者就只能通過 sigkill 訊號這一終極手段了。除了 sigterm 和 sigkill ,還有像 sigusr1 這樣的專門支援使用者自定義行為的訊號。下面的**簡單的說明在 nodejs 中如何為乙個訊號註冊處理程式:

process.on('sigterm', function

() );

關於訊號的更多資訊,筆者在《linux kill 命令》一文中有所提及,這裡不再贅述。

docker 的 stop 和 kill 命令都是用來向容器傳送訊號的。注意,只有容器中的 1 號程序能夠收到訊號,這一點非常關鍵!

stop 命令會首先傳送 sigterm 訊號,並等待應用優雅的結束。如果發現應用沒有結束(使用者可以指定等待的時間),就再傳送乙個 sigkill 訊號強行結束程式。

kill 命令預設傳送的是 sigkill 訊號,當然你可以通過 -s 選項指定任何訊號。

});這個應用是乙個 http 伺服器,監聽埠 3000,為 sigint 和 sigterm 訊號註冊了處理程式。接下來我們將介紹以不同的方式在容器中執行程式時訊號的處理情況。

建立 dockerfile 檔案,把上面的應用打包到映象中:

from iojs:onbuild

copy ./package.json ./package.json

expose

3000

entrypoint [

"node

", "

"]

請注意 entrypoint 指令的寫法,這種寫法會讓 node 在容器中以 1 號程序的身份執行。

接下來建立映象:

然後啟動容器執行應用程式:

$ docker run -it --rm -p 3000:3000 --name="

此時 node 應用在容器中的程序號為 1:

現在我們讓程式退出,執行命令:

$ docker container kill --signal="

sigterm

此時應用會以我們期望的方式退出:

#!/usr/bin/env

bash

然後建立 dockerfile1 檔案,內容如下:

from iojs:onbuild

copy ./package.json ./package.json

run

expose

3000

entrypoint [

""]

接下來建立映象:

然後啟動容器執行應用程式:

$ docker run -it --rm -p 3000:3000 --name="

此時 node 應用在容器中的程序號不再是 1:

我們可以通過:

# or

$ docker container

kill --signal="

sigkill

退出應用,它們最終都是向容器中的 1 號程序傳送了 sigkill 訊號。很顯然這不是我們期望的,我們希望程式能夠收到 sigterm  訊號優雅的退出。

#!/usr/bin/env

bash

set -x

pid=0

# sigusr1-handler

my_handler()

# sigterm-handler

term_handler()

# setup handlers

# on callback,

kill the last background process, which is `tail -f /dev/null

` and execute the specified handler

trap

'kill $; my_handler

'sigusr1

trap

'kill $; term_handler

'sigterm$!"

# wait

forever

while

true

dotail -f /dev/null & wait $

done

這個指令碼檔案在啟動應用程式的同時可以捕獲傳送給它的 sigterm 和 sigusr1 訊號,並為它們新增了處理程式。其中 sigterm 訊號的處理程式就是向我們的 node 應用程式傳送 sigterm 訊號。

然後建立 dockerfile2 檔案,內容如下:

from iojs:onbuild

copy ./package.json ./package.json

run

expose

3000

entrypoint [

""]

接下來建立映象:

然後啟動容器執行應用程式:

$ docker run -it --rm -p 3000:3000 --name="

此時 node 應用在容器中的程序號也不是 1,但是它卻可以接收到 sigterm 訊號並優雅的退出了:

容器中的 1 號程序是非常重要的,如果它不能正確的處理相關的訊號,那麼應用程式退出的方式幾乎總是被強制殺死而不是優雅的退出。究竟誰是 1 號程序則主要由 entrypoint, cmd, run 等指令的寫法決定,所以這些指令的使用是很有講究的。

在 docker 容器中捕獲訊號

訊號是一種程序間通訊的形式。乙個訊號就是核心傳送給程序的乙個訊息,告訴程序發生了某種事件。當乙個訊號被傳送給乙個程序後,程序會立即中斷當前的執行流並開始執行訊號的處理程式。如果沒有為這個訊號指定處理程式,就執行預設的處理程式。程序需要為自己感興趣的訊號註冊處理程式,比如為了能讓程式優雅的退出 接到退...

在 docker 容器中捕獲訊號

訊號是一種程序間通訊的形式。乙個訊號就是核心傳送給程序的乙個訊息,告訴程序發生了某種事件。當乙個訊號被傳送給乙個程序後,程序會立即中斷當前的執行流並開始執行訊號的處理程式 這麼說不太準確,訊號是在特定的時機被處理 如果沒有為這個訊號指定處理程式,就執行預設的處理程式。程序需要為自己感興趣的訊號註冊處...

詳解如何在 docker 容器中捕獲訊號

訊號 linux 訊號是一種程序間通訊的形式。乙個訊號就是核心傳送給程序的乙個訊息,告訴程序發生了某種事件。當乙個訊號被傳送給乙個程序後,程序會立即中斷當前的執行流並開始執行訊號的處理程式。如果沒有為這個訊號指定處理程式,就執行預設的處理程式。程序需要為自己感興趣的訊號註冊處理程式,比如為了能讓程式...