服務器維護docker容器如何優雅的終止詳解
2020-05-27 17:34 作者:艾銻無限 瀏覽量:
如何做好服務器維護?北京艾銻無限科技與你談談IT人員必須知道的服務器維護信息
在
服務器維護過程中,我們日常的項目當中,這是我們經常需要面對和處理的問題:
服務器維護場景A:假如我們打包在容器中的程序,提供HTTP方式的服務,負責處理各種HTTP requests并返回結果,我們必然希望在容器被停掉的時候,能夠讓程序有時間把已經在處理中的請求繼續處理完畢,并返回結果給客戶端。
服務器維護場景B:又比如我們打包在容器中的程序,負責寫入數據到某個數據文件中,我們希望程序能夠在容器被停掉的時候,有時間把內存中緩存的數據持久化到存儲設備中,以防數據丟失。
服務器維護場景C:再比如現在流行的微服務架構中,一般會有服務發現的機制,也即每一個微服務在啟動之后,都會主動把自己的地址信息注冊到服務發現模塊當中,讓其他的服務可以知道自己的存在。而在容器被停掉的時候,微服務需要即時從服務發現模塊中注銷自己,以防止從API Gateway而來的請求被錯
誤的路由到了已經被停止掉的微服務。
如上的各種場景中,都要求打包在容器中的應用程序能夠被優雅的終止(也即gracefully shutdown),這種gracefully shutdown的方式,允許程序在容器被停止的時候,有一定時間做一些后續處理操作,這也是我們需要進一步探討的話題。
服務器維護提示:docker stop 與 docker kill 的區別
Docker本身提供了兩種終止容器運行的方式,即docker stop與docker kill。
服務器維護要點:docker stop
先來說說docker stop吧,當我們用docker stop命令來停掉容器的時候,docker默認會允許容器中的應用程序有10秒的時間用以終止運行。所以我們查看docker stop命令幫助的時候,會有如下的提示:
1
2
3
4
5
6 |
→ docker stop --help
Usage: docker stop [OPTIONS] CONTAINER [CONTAINER...]
Stop one or more running containers
Options:
--help Print usage
-t, --time int Seconds to wait for stop before killing it (default 10) |
在docker stop命令執行的時候,會先向容器中PID為1的進程發送系統信號SIGTERM,然后等待容器中的應用程序終止執行,如果等待時間達到設定的超時時間,或者默認的10秒,會繼續發送SIGKILL的系統信號強行kill掉進程。在容器中的應用程序,可以選擇忽略和不處理SIGTERM信號,不過一旦達到超時時間,程序就會被系統強行kill掉,因為SIGKILL信號是直接發往系統內核的,應用程序沒有機會去處理它。在使用docker stop命令的時候,我們唯一能控制的是超時時間,比如設置為20秒超時:
1 |
docker stop --time=20 container_name |
服務器維護要點:docker kill
接著我們來看看docker kill命令,默認情況下,docker kill命令不會給容器中的應用程序有任何gracefully shutdown的機會。它會直接發出SIGKILL的系統信號,以強行終止容器中程序的運行。通過查看docker kill命令的幫助,我們可以看到,除了默認發送SIGKILL信號外,還允許我們發送一些自定義的系統信號:
1
2
3
4
5
6 |
→ docker kill --help
Usage: docker kill [OPTIONS] CONTAINER [CONTAINER...]
Kill one or more running containers
Options:
--help Print usage
-s, --signal string Signal to send to the container (default "KILL") |
比如,如果我們想向docker中的程序發送SIGINT信號,我們可以這樣來實現:
1 |
docker kill --signal=SIGINT container_name |
與docker stop命令不一樣的地方在于,docker kill沒有任何的超時時間設置,它會直接發送SIGKILL信號,以及用戶通過signal參數指定的其他信號。
其實不難看出,docker stop命令,更類似于Linux系統中的kill命令,二者都是發送系統信號SIGTERM。而docker kill命令,更像是Linux系統中的kill -9或者是kill -SIGKILL命令,用來發送SIGKILL信號,強行終止進程。
服務器維護提示:在程序中接收并處理信號
了解了docker stop與docker kill的區別,我們能夠知道,docker kill適合用來強行終止程序并實現快速停止容器。而如果希望程序能夠gracefully shutdown的話,docker stop才是不二之選。這樣,我們可以讓程序在接收到SIGTERM信號后,有一定的時間處理、保存程序執行現場,優雅的退出程序。
接下來我們可以寫一個簡單的Go程序來實現信號的接收與處理,程序在啟動過后,會一直阻塞并監聽系統信號,直到監測到對應的系統信號后,輸出控制臺并退出執行。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 |
// main.go
package main
import (
"fmt"
"os"
"os/signal"
"syscall"
)
func main() {
fmt.Println("Program started...")
ch := make(chan os.Signal, 1)
signal.Notify(ch, syscall.SIGTERM)
s := <-ch
if s == syscall.SIGTERM {
fmt.Println("SIGTERM received!")
//Do something...
}
fmt.Println("Exiting...")
} |
接下來
服務器維護要點:使用交叉編譯的方式來編譯程序,讓程序可以在Linux下運行:
1 |
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o graceful
|
編譯好之后,我們還需要打包程序到容器中運行。于是,我們還得有個Dockerfile。在這里,我們選擇使用體積小又輕盈的alpine鏡像作為基礎鏡像,打包這個Go程序:
1
2
3
4 |
from alpine:latest
MAINTAINER Timothy
ADD graceful /graceful
CMD ["/graceful"]
|
服務器維護要點:這里需要避開的一個坑,是Dockerfile中CMD命令的用法。
服務器維護要點:CMD命令有兩種方式:
使用 CMD command param1 param2 這種方式,其實是以shell的方式運行程序。最終程序被執行時,類似于/bin/sh -c的方式運行了我們的程序,這樣會導致/bin/sh以PID為1的進程運行,而我們的程序只不過是它fork/execs出來的子進程而已。前面我們提到過docker stop的SIGTERM信號只是發送給容器中PID為1的進程,而這樣,我們的程序就沒法接收和處理到信號了。
使用 CMD [“executable”,”param1”,”param2”] 這種方式啟動程序,才是我們想要的,這種方式執行和啟動時,我們的程序會被直接啟動執行,而不是以shell的方式,這樣我們的程序就能以PID=1的方式開始執行了。
話題轉回來,我們開始執行容器構建操作,打包程序:
1 |
docker build -t registry.xiaozhou.net/graceful:latest .
|
打包過后的鏡像,才6MB左右:
1
2
3 |
λ Timothy [workspace/src/graceful] → docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
registry.xiaozhou.net/graceful latest b2210a85ca55 20 hours ago 6.484 MB
|
啟動并運行容器:
1 |
λ Timothy [workspace/src/graceful] → docker run -d --name graceful b2210a85 |
查看容器運行狀態:
1
2
3 |
λ Timothy [workspace/src/graceful] → docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
fd18eedafd16 b221 "/graceful" 3 seconds ago Up 2 seconds graceful |
查看容器輸出,能看到程序已經正常啟動:
1
2 |
λ Timothy [workspace/src/graceful] → docker logs graceful
Started... |
服務器維護要點:接著我們要使用docker stop大法,看程序能否響應SIGTERM信號:
1
2 |
λ Timothy [workspace/src/graceful] → docker stop graceful
graceful |
最后,查看容器的日志,檢驗輸出:
1
2
3
4 |
λ Timothy [workspace/src/graceful] → docker logs graceful
Started...
SIGTERM received!
Exiting... |
以上內容為
北京艾銻無限科技發展有限公司IT外包服務公司為大家提供的
服務器維護小知識,更多內容請關注:
www.bjitwx.com。