簡體   English   中英

通過發送 SIGTERM 停止正在運行的 Docker 容器

[英]Stop a running Docker container by sending SIGTERM

我有一個非常簡單的 Go 應用程序監聽端口 8080

http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(200)
    w.Header().Set("Content-Type", "text-plain")
    w.Write([]byte("Hello World!"))
})
log.Fatal(http.ListenAndServe(":8080", http.DefaultServeMux))

我將它安裝在 Docker 容器中並像這樣啟動它:

FROM golang:alpine
ADD . /go/src/github.com/myuser/myapp
RUN go install github.com/myuser/myapp
ENTRYPOINT ["/go/bin/myapp"]
EXPOSE 8080

然后我使用docker run運行容器:

docker run --publish 8080:8080 first-app

我希望像大多數程序一樣,我可以向運行docker run的進程發送一個 SIGTERM,這將導致容器停止運行。 我觀察到發送 SIGTERM 沒有效果,相反我需要使用像docker killdocker stop這樣的命令。

這是預期的行為嗎? 在論壇和 IRC 上問過,但沒有得到答復。

默認情況下, SIGTERMdocker run命令傳播到 Docker 守護程序,但除非在 Docker 正在運行的主進程中專門處理該信號,否則它不會生效。

您在容器中run的第一個進程將在該容器上下文中具有 PID 1。 這被 linux 內核視為一個特殊的進程。 除非進程為該信號安裝了處理程序,否則它不會被發送信號。 將信號轉發到其他子進程也是 PID 1 的工作。

docker run和其他命令是由 docker 守護進程托管的遠程 API 的API 客戶端。 docker 守護進程作為一個單獨的進程運行,並且是您在容器上下文中運行的命令的父進程。 這意味着在run和守護進程之間沒有以標准的 unix 方式直接發送信號。

--sig-proxy docker run--sig-proxy docker attach命令有一個--sig-proxy標志,默認信號代理為true 如果需要,您可以關閉此功能。

docker exec不代理信號

Dockerfile ,如果您不希望sh成為 PID 1 進程 ( Kevin Burke ),請在指定CMDENTRYPOINT默認值時小心使用“exec 形式”:

CMD ["executable", "param1", "param2"]

信號處理 Go 示例

在此處使用示例 Go 代碼: https : //gobyexample.com/signals

運行不處理信號的常規進程和捕獲信號並將它們置於后台的 Go 守護進程。 我正在使用sleep因為它很容易並且不處理“守護進程”信號。

$ docker run busybox sleep 6000 &
$ docker run gosignal &

使用具有“樹”視圖的ps工具,您可以看到兩個不同的進程樹。 一個用於sshd下的docker run進程。 另一個用於實際容器進程,在docker daemon

$ pstree -p
init(1)-+-VBoxService(1287)
        |-docker(1356)---docker-containe(1369)-+-docker-containe(1511)---gitlab-ci-multi(1520)
        |                                      |-docker-containe(4069)---sleep(4078)
        |                                      `-docker-containe(4638)---main(4649)
        `-sshd(1307)---sshd(1565)---sshd(1567)---sh(1568)-+-docker(4060)
                                                          |-docker(4632)
                                                          `-pstree(4671)

docker hosts 進程的詳細信息:

$ ps -ef | grep "docker r\|sleep\|main"
docker    4060  1568  0 02:57 pts/0    00:00:00 docker run busybox sleep 6000
root      4078  4069  0 02:58 ?        00:00:00 sleep 6000
docker    4632  1568  0 03:10 pts/0    00:00:00 docker run gosignal
root      4649  4638  0 03:10 ?        00:00:00 /main

殺戮

我無法殺死docker run busybox sleep命令:

$ kill 4060
$ ps -ef | grep 4060
docker    4060  1568  0 02:57 pts/0    00:00:00 docker run busybox sleep 6000

我可以docker run gosignal具有陷阱處理程序的docker run gosignal命令:

$ kill 4632
$ 
terminated
exiting

[2]+  Done                       docker run gosignal

通過docker exec信號

如果我在已經運行的sleep容器中docker exec一個新的sleep進程,我可以發送一個 ctrl-c 並中斷docker exec本身,但這不會轉發到實際進程:

$ docker exec 30b6652cfc04 sleep 600
^C
$ docker exec 30b6652cfc04 ps -ef
PID   USER     TIME   COMMAND
    1 root       0:00 sleep 6000   <- original
   97 root       0:00 sleep 600    <- execed still running
  102 root       0:00 ps -ef

TL; 博士

您使用 docker run任何進程都必須自己處理信號。

或使用--init標志將tini作為 PID 1 運行

所以這里有兩個因素在起作用:

1) 如果您為入口點指定一個字符串,如下所示:

ENTRYPOINT /go/bin/myapp

Docker 使用/bin/sh -c 'command'運行腳本。 此中間腳本獲取 SIGTERM,但不會將其發送到正在運行的服務器應用程序。

為避免中間層,請將您的入口點指定為字符串數組。

ENTRYPOINT ["/go/bin/myapp"]

2)我構建了我試圖使用以下字符串運行的應用程序:

docker build -t first-app .

這用名稱 first-app 標記了容器。 不幸的是,當我嘗試重建/重新運行我運行的容器時:

docker build .

哪個沒有覆蓋標簽,所以我的更改沒有被應用。

一旦我做了這兩件事,我就可以用 ctrl+c 終止進程,並關閉正在運行的容器。

可以在此處找到此問題的非常全面的描述和解決方案: https : //vsupalov.com/docker-compose-stop-slow

在我的情況下,我的應用程序希望收到 SIGTERM 信號以進行正常關閉,但沒有收到它,因為該進程由 bash 腳本啟動,該腳本從 dockerfile 以這種形式調用:ENTRYPOINT ["/path/to/script.sh"]

所以腳本沒有將 SIGTERM 傳播到應用程序。 解決方案是使用腳本中的 exec 運行啟動應用程序的命令:例如 exec java -jar ...

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM