docker容器如何優雅的終止詳解_docker

來源:互聯網
上載者:User

前言

在Docker大行其道的今天,我們能夠非常方便的使用容器打包我們的應用程式,並且將它在我們的伺服器上部署並運行起來。但是,談論到如何停掉運行中的docker容器並正確的終止其中的程式,這就成為一個非常值得討論的話題了。

事實上,在我們日常的項目當中,這是我們經常需要面對和處理的問題:

     情境A:假如我們打包在容器中的程式,提供HTTP方式的服務,負責處理各種HTTP requests並返回結果,我們必然希望在容器被停掉的時候,能夠讓程式有時間把已經在處理中的請求繼續處理完畢,並返回結果給用戶端。

     情境B:又比如我們打包在容器中的程式,負責寫入資料到某個資料檔案中,我們希望程式能夠在容器被停掉的時候,有時間把記憶體中緩衝的資料持久化到存放裝置中,以防資料丟失。

     情境C:再比如現在流行的微服務架構中,一般會有服務發現的機制,也即每一個微服務在啟動之後,都會主動把自己的地址資訊註冊到服務發現模組當中,讓其他的服務可以知道自己的存在。而在容器被停掉的時候,微服務需要即時從服務發現模組中登出自己,以防止從API Gateway而來的請求被錯誤的路由到了已經被停止掉的微服務。

如上的各種情境中,都要求打包在容器中的應用程式能夠被優雅的終止(也即gracefully shutdown),這種gracefully shutdown的方式,允許程式在容器被停止的時候,有一定時間做一些後續處理操作,這也是我們需要進一步探討的話題。

docker stop 與 docker kill 的區別

Docker本身提供了兩種終止容器啟動並執行方式,即docker stopdocker kill

docker stop

先來說說docker stop吧,當我們用docker stop命令來停掉容器的時候,docker預設會允許容器中的應用程式有10秒的時間用以終止運行。所以我們查看docker stop命令協助的時候,會有如下的提示:

→ 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命令執行的時候,會先向容器中PID為1的進程發送系統訊號SIGTERM,然後等待容器中的應用程式終止執行,如果等待時間達到設定的逾時時間,或者預設的10秒,會繼續發送SIGKILL的系統訊號強行kill掉進程。在容器中的應用程式,可以選擇忽略和不處理SIGTERM訊號,不過一旦達到逾時時間,程式就會被系統強行kill掉,因為SIGKILL訊號是直接發往系統核心的,應用程式沒有機會去處理它。在使用docker stop命令的時候,我們唯一能控制的是逾時時間,比如設定為20秒逾時:

docker stop --time=20 container_name

docker kill

接著我們來看看docker kill命令,預設情況下,docker kill命令不會給容器中的應用程式有任何gracefully shutdown的機會。它會直接發出SIGKILL的系統訊號,以強行終止容器中程式的運行。通過查看docker kill命令的協助,我們可以看到,除了預設發送SIGKILL訊號外,還允許我們發送一些自訂的系統訊號:

→ 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中的程式發送SIGINT訊號,我們可以這樣來實現:

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 stopdocker kill的區別,我們能夠知道,docker kill適合用來強行終止程式並實現快速停止容器。而如果希望程式能夠gracefully shutdown的話,docker stop才是不二之選。這樣,我們可以讓程式在接收到SIGTERM訊號後,有一定的時間處理、儲存程式執行現場,優雅的退出程式。

接下來我們可以寫一個簡單的Go程式來實現訊號的接收與處理,程式在啟動過後,會一直阻塞並監聽系統訊號,直到監測到對應的系統訊號後,輸出控制台並退出執行。

// main.gopackage mainimport ( "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下運行:

CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o graceful

編譯好之後,我們還需要打包程式到容器中運行。於是,我們還得有個Dockerfile。在這裡,我們選擇使用體積小又輕盈的alpine鏡像作為基礎鏡像,打包這個Go程式:

from alpine:latestMAINTAINER TimothyADD graceful /gracefulCMD ["/graceful"]

這裡需要避開的一個坑,是Dockerfile中CMD命令的用法。

CMD命令有兩種方式:

CMD /graceful

使用 CMD command param1 param2 這種方式,其實是以shell的方式運行程式。最終程式被執行時,類似於/bin/sh -c的方式運行了我們的程式,這樣會導致/bin/sh以PID為1的進程運行,而我們的程式只不過是它fork/execs出來的子進程而已。前面我們提到過docker stop的SIGTERM訊號只是發送給容器中PID為1的進程,而這樣,我們的程式就沒法接收和處理到訊號了。

CMD [“/graceful”]

使用 CMD [“executable”,”param1”,”param2”] 這種方式啟動程式,才是我們想要的,這種方式執行和啟動時,我們的程式會被直接啟動執行,而不是以shell的方式,這樣我們的程式就能以PID=1的方式開始執行了。

話題轉回來,我們開始執行容器構建操作,打包程式:

docker build -t registry.xiaozhou.net/graceful:latest .

打包過後的鏡像,才6MB左右:

λ Timothy [workspace/src/graceful] → docker imagesREPOSITORY            TAG     IMAGE ID   CREATED    SIZEregistry.xiaozhou.net/graceful       latest    b2210a85ca55  20 hours ago  6.484 MB

啟動並運行容器:

λ Timothy [workspace/src/graceful] → docker run -d --name graceful b2210a85

查看容器運行狀態:

λ Timothy [workspace/src/graceful] → docker ps -aCONTAINER ID  IMAGE    COMMAND    CREATED    STATUS    PORTS    NAMESfd18eedafd16  b221    "/graceful"   3 seconds ago  Up 2 seconds       graceful

查看容器輸出,能看到程式已經正常啟動:

λ Timothy [workspace/src/graceful] → docker logs gracefulStarted...

接著我們要使用docker stop大法,看程式能否響應SIGTERM訊號:

λ Timothy [workspace/src/graceful] → docker stop gracefulgraceful

最後,查看容器的日誌,檢驗輸出:

λ Timothy [workspace/src/graceful] → docker logs gracefulStarted...SIGTERM received!Exiting...

總結

以上就是這篇文章的全部內容了,用docker kill命令,可以簡單粗暴的終止docker容器中啟動並執行程式,但是想要優雅的終止掉的話,我們需要使用docker stop命令,並且在程式中多花一些功夫來處理系統訊號,這樣能保證程式不被粗暴的終止掉,從而實現gracefully shutdown。希望本文的內容對大家的學習或者工作能有所協助,如果有疑問大家可以留言交流。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.