无法在Docker容器中捕获SIGINT信号。

huangapple go评论76阅读模式
英文:

Can't trap SIGINT in Docker container

问题

我有以下的代码:

FROM golang:1.8.3
WORKDIR /go/src/x/x/program
RUN mkdir /logs
VOLUME ["/go/src/x/x/program", "/logs"]
CMD ["sh", "-c", "go install && program"]

我的 Go 服务器以以下方式监听 SIGINT 信号:

// ... 其他代码

c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)

go func() {
	<-c
	signal.Stop(c)

	// 服务器优雅关闭
	err := s.Shutdown(context.Background())
	if err != nil {
		fileLogger.Printf("无法关闭服务器:%v", err)
	} else {
		fileLogger.Print("服务器成功关闭")
	}

// ... 启动服务器

但是我无法捕获和处理 SIGINT 信号。我尝试了以下方法:

  • docker kill -s SIGINT <my_container>
  • (使用 compose) docker-compose down/kill
  • docker exec -ti <my_container> /bin/bash
    • (接着执行) kill -SIGINT <go program PID>

没有任何日志记录,所以我认为我的程序根本没有处理 SIGINT。


在测试时,我通过以下方式成功处理了 SIGINT(但不适用于生产环境):

  • docker run -ti -v <local_path_to_log>:/logs <my_image> /bin/bash
    • go run *.go
    • 按下 CTRL + C 中断进程

我可以在文件中看到日志。

我还发现我的镜像设置方式导致有两个进程在运行:

docker exec -ti <my_container> /bin/bash
root@xxx:/go/src/x/x/x# ps aux | grep program
root         1  0.0  0.0   4332   716 ?        Ss   03:47   0:00 sh -c go install && program
root        32  0.0  0.3 335132  6624 ?        Sl   03:47   0:00 program
root@xxx:/go/src/x/x/x# kill -SIGINT 32

如上所示,杀死第二个进程(而不是 PID 1)会向程序发送 SIGINT 信号,然后程序可以捕获和处理它。


我知道我离在容器外部发送 SIGINT 的解决方案很近了,但我还无法理解。

在这种情况下,正确的方法是什么,以便容器可以接收到正确的进程的 SIGINT 信号?

英文:

I have the following image

FROM golang:1.8.3
WORKDIR /go/src/x/x/program
RUN mkdir /logs
VOLUME [&quot;/go/src/x/x/program&quot;, &quot;/logs&quot;]
CMD [&quot;sh&quot;, &quot;-c&quot;, &quot;go install &amp;&amp; program&quot;]

My Go server listens to SIGINT in the following way

// ... Other stuff

c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)

go func() {
	&lt;-c
	signal.Stop(c)

	// Server graceful shutdown
	err := s.Shutdown(context.Background())
	if err != nil {
		fileLogger.Printf(&quot;could not shutdown server: %v&quot;, err)
	} else {
		fileLogger.Print(&quot;server successfully shutdown&quot;)
	}

// ... Start server

But I'm failing to trap and handle SIGINT. I tried the following:

  • docker kill -s SIGINT &lt;my_container&gt;
  • (with compose) docker-compose down/kill
  • docker exec -ti &lt;my_container&gt; /bin/bash
    • (followed by) kill -SIGINT &lt;go program PID&gt;

Nothing gets logged, so I assume SIGINT wasn't handled by my program at all.


When testing, I managed to do it by doing the following (which isn't fit for production)

  • docker run -ti -v &lt;local_path_to_log&gt;:/logs &lt;my_image&gt; /bin/bash
    • go run *.go
    • CTRL + C to interrupt process

I see the logs in my file.

I also just figured out that the way my image is set up, it ends up having two processes running:

docker exec -ti &lt;my_container&gt; /bin/bash
root@xxx:/go/src/x/x/x# ps aux | grep program
root         1  0.0  0.0   4332   716 ?        Ss   03:47   0:00 sh -c go install &amp;&amp; program
root        32  0.0  0.3 335132  6624 ?        Sl   03:47   0:00 program
root@xxx:/go/src/x/x/x# kill -SIGINT 32

So as shown above, killing the second process (and not PID 1) sends SIGINT to the program, which can than trap and handle it.


I know I'm close to a solution to sending SIGINT from outside of the container. But I can't grasp it yet.

What is the correct approach here to have a container that can receive a SIGINT to the correct process?

答案1

得分: 0

Docker将信号传递给PID 1进程。在您的情况下,由于您正在生成一个子进程,所以您没有收到信号。
如果CMD中只有一个进程,您可以这样做:

CMD ["/bin/ping","localhost"]

如果您正在执行多个操作,就像您上面所写的那样,您可以在CMD中运行一个脚本,并在脚本中进行信号处理,并将其传递给后台进程。另一个选项是在CMD中只有一个命令处理。您可以在上一步中执行"go install",然后在CMD中只运行可执行文件。

英文:

Docker passes signals to PID 1 process. In your case, since you are spawning a child process, you are not getting the signal.
If there is only 1 processing in CMD, you can do something like:

CMD [&quot;/bin/ping&quot;,&quot;localhost&quot;]

If you are doing multiple operations like what you have put above, you can run a script in CMD and have signal processing in the script and pass it to your background process. The other option is to have only 1 command processing in CMD. You can do "go install" in previous step and just run executable in CMD.

huangapple
  • 本文由 发表于 2017年7月31日 12:03:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/45407283.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定