英文:
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 ["/go/src/x/x/program", "/logs"]
CMD ["sh", "-c", "go install && program"]
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() {
<-c
signal.Stop(c)
// Server graceful shutdown
err := s.Shutdown(context.Background())
if err != nil {
fileLogger.Printf("could not shutdown server: %v", err)
} else {
fileLogger.Print("server successfully shutdown")
}
// ... Start server
But I'm failing to trap and handle SIGINT. I tried the following:
docker kill -s SIGINT <my_container>
- (with compose)
docker-compose down/kill
docker exec -ti <my_container> /bin/bash
- (followed by)
kill -SIGINT <go program PID>
- (followed by)
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 <local_path_to_log>:/logs <my_image> /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 <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
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 ["/bin/ping","localhost"]
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论