通过发送SIGTERM来停止正在运行的Docker容器 [英] Stop a running Docker container by sending SIGTERM
问题描述
我有一个非常简单的Go应用,正在8080端口上监听
I have a very very simple Go app listening on port 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容器中并像这样启动它:
I install it in a Docker container and start it like so:
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
我希望像大多数程序一样,我可以将SIGTERM发送到运行 docker run
的进程,这将导致容器停止运行。我观察到发送SIGTERM无效,而是需要使用 docker kill
或 docker stop
之类的命令。
I expect that, like most programs, I can send a SIGTERM to the process running docker run
and this will cause the container to stop running. I observe that sending SIGTERM has no effect, and instead I need to use a command like docker kill
or docker stop
.
Is this intended behavior? I've asked in the forums and on IRC and gotten no answer.
推荐答案
A SIGTERM
默认情况下,它是通过 docker run
命令传播到Docker守护进程的,但是除非该信号在Docker运行的主进程中专门处理,否则它不会生效。
A SIGTERM
is propagated by the docker run
command to the Docker daemon by default but it will not take effect unless the signal is specifically handled in main process being run by Docker.
您在容器中运行的第一个进程
在该容器上下文中将具有PID 1。 linux内核将其视为特殊过程。除非进程为该信号安装了处理程序,否则不会发送信号。 PID 1的任务是将信号转发到其他子进程。
The first process you run
in a container will have PID 1 in that containers context. This is treated as a special process by the linux kernel. It will not be sent a signal unless the process has a handler installed for that signal. It is also PID 1's job to forward signals onto other child processes.
docker run
和其他命令是远程API 。 docker守护程序作为单独的进程运行,并且是您在容器上下文中运行的命令的父代。这意味着在 run
与守护程序之间没有以标准的unix方式直接发送信号。
docker run
and other commands are API clients for the Remote API hosted by the docker daemon. The docker daemon runs as a seperate process and is the parent for the commands you run inside a container context. This means that there is no direct sending of signals between run
and the daemon, in the standard unix manner.
docker run
和 docker attach
命令有一个-sig-proxy
标志,默认标志为 true
。您可以根据需要关闭此功能。
The docker run
and docker attach
command have a --sig-proxy
flag that defaults signal proxying to true
. You can turn this off if you want.
docker exec
不代理信号。
在 Dockerfile
中,在指定 CMD
和 ENTRYPOINT
默认为 sh
成为PID 1进程( Kevin Burke ):
In a Dockerfile
, be careful to use the "exec form" when specifying CMD
and ENTRYPOINT
defaults if you don't want sh
to become the PID 1 process (Kevin Burke):
CMD ["executable", "param1", "param2"]
信号处理Go示例
在此处使用示例Go代码: https://gobyexample.com/signals
同时运行一个不处理信号,并捕获信号并将其置于后台的Go守护程序。我正在使用 sleep
,因为它很容易并且不处理守护程序信号。
Run both a regular process that doesn't handle signals and the Go daemon that traps signals and put them in the background. I'm using sleep
as it's easy and doesn't handle "daemon" signals.
$ docker run busybox sleep 6000 &
$ docker run gosignal &
使用 ps
工具具有树视图,您可以看到两个不同的过程树。一个用于 sshd
下的 docker run
进程。另一个用于实际容器进程,位于 docker daemon
下。
With a ps
tool that has a "tree" view, you can see the two distinct process trees. One for the docker run
process under sshd
. The other for the actual container processes, under 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主机进程的详细信息:
The details of docker hosts processes:
$ 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
命令:
I can kill the docker run gosignal
command that has the trap handler:
$ kill 4632
$
terminated
exiting
[2]+ Done docker run gosignal
通过 docker exec的信号
如果我 docker exec
在已经运行的 sleep $ c中有一个新的
sleep
进程$ c>容器,我可以发送ctrl-c并中断 docker exec
本身,但这不会转发到实际过程:
Signals via docker exec
If I docker exec
a new sleep
process in the already running sleep
container, I can send an ctrl-c and interrupt the docker exec
itself, but that doesn't forward to the actual process:
$ 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; DR
任何使用docker 运行
的进程都必须处理信号
TL;DR
Any process you run
with docker must handle signals itself.
或使用-init
标志运行 tini
作为PID 1
or use the --init
flag to run tini
as PID 1
这篇关于通过发送SIGTERM来停止正在运行的Docker容器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!