容器關閉時(docker stop)處理自定義操作
前言
現如今在開發、測試、生產運維等各個軟件開發的環節中都少不了docker的部署,本文不再贅述docker相關介紹。
在項目生產環境中,特別是用k8s結合微服務框架(如tars)來做服務治理、伸縮時,當容器被關閉的時候,需要告知主節點(Master)優雅的關閉該容器上的服務並下線該服務,而不能粗暴的終止程序/進程,這會導致生產環境上出現不可控的風險。
本文主要目的就是提供一種在執行docker stop命令時,可以在容器內部監聽到該命令並執行自定義操作的方法。
工作原理
1. 發送信號
docker stop命令實際上是向容器內部發出了終止進程的信號SIGTERM。
Linux信號這里簡單介紹下,有助於理解整個過程。
Linux系統利用信號與系統中的進程進行通信。Linux的常見信號有:
信號
|
值
|
描述
|
1
|
SIGHP
|
掛起進程
|
2
|
SIGINT
|
終止進程
|
3
|
SIGQUIT
|
停止進程
|
9
|
SIGKILL
|
無條件終止進程
|
15
|
SIGTERM
|
盡可能終止進程
|
17
|
SIGSTOP
|
無條件停止進程,但不是終止進程
|
18
|
SIGTSTP
|
停止或暫停進程,但不終止進程
|
19
|
SIGCONT
|
繼續運行停止的進程
|
我們在linux系統上最常用的命令Ctrl+C,實際就是產生SIGINT信號,強制終止進程。
2. 捕捉信號
docker stop命令實際是向容器中PID=1的進程發送了SIGTERM信號,告知容器它即將被關閉,我們要做的就是捕捉SIGTERM信號,並執行需要的相關停止服務、下線服務或其他操作。
詳細流程
1. 創建PID=1的進程
制作鏡像的dockerfile中,有一個關鍵詞ENTRYPOINT,是當容器啟動時第一個執行的程序,並且該進程ID會被設定為1,所以以它執行的shell腳本才可以捕捉到上文提到的SIGTERM信號。
下面是dockerfile實例:
FROM tarscloud/tars-env-full COPY entrypoint.sh /sbin/ RUN chmod 755 /sbin/entrypoint.sh ENTRYPOINT [ "/sbin/entrypoint.sh" ]
可以看到,我們將entrypoint.sh腳本作為容器啟動時PID=1的程序。
下面我們將編寫entrypoint.sh,在腳本中捕捉SIGTERM信號,
entrypoint.sh 實例
#!/bin/bash # 在容器關閉前,優雅的關閉服務並下線服務 function stop_server() { if [ -f "/usr/local/tars-auto/stop_tars_server.go" ]; then echo "/usr/local/tars-auto/stop_tars_server.go exist, will run." go run /usr/local/tars-auto/stop_tars_server.go exit else echo "stop file not exist" exit fi } # 捕捉docker stop時發送的SIGTERM信號 trap 'stop_server' SIGTERM
trap 'stop_server' SIGTERM就是捕捉SIGTERM的代碼,trap是shell腳本中專門用來捕捉信號的方法。
第二個參數“stop_server”是指捕捉到信號后要執行的命令,
可以看到stop_server函數中執行了一個stop_tars_server.go的腳本,當然可以根據其他需要進行替換。
我們通過上面的dockerfile,啟動了一個容器,
執行
docker exec -it tars-node bash
進入容器內部,再執行top 1,查看當前所有進程,如下圖:

可以看到進程ID=1的程序是enterypoint的執行程序。
2. 停止容器
接來下我們執行docker stop
docker stop tars-node
執行后,查看容器日志:
docker logs -f tars-node
可以看到,enterypoint.sh腳本中stop_server函數的echo打印正確輸出了:

並且還打印輸出了go腳本中的執行日志,也就是文章最開頭提到的關閉服務、下線服務的內容。
至此,我們成功做到了在執行docker stop后,讓需要的自定義操作在容器關閉前執行。