英文:
Unable to gracefully shutdown a docker process
问题
我想要能够优雅地关闭一个 Docker 进程。我按照这篇博文的思路创建了以下文件:https://husobee.github.io/golang/ecs/2016/05/19/ecs-graceful-go-shutdown.html
以下是我的文件:
1)Dockerfile
FROM debian:jessie
ADD app /app
RUN apt-get update --fix-missing
RUN apt-get install -y golang
CMD ["go", "run", "/app/main.go"]
2)app/main.go
package main
import "os"
import "syscall"
import "fmt"
import "time"
import "os/signal"
func main() {
// 创建一个 "returnCode" 通道,用于应用程序的返回代码
var returnCode = make(chan int)
// finishUP 通道用于通知应用程序完成
var finishUP = make(chan struct{})
// done 通道用于通知信号处理程序应用程序已完成
var done = make(chan struct{})
// gracefulStop 是一个 os.Signal 的通道,我们将监听 -SIGTERM 信号
var gracefulStop = make(chan os.Signal)
// 监听来自操作系统的 SIGTERM 和 SIGINT 信号,并在 gracefulStop 通道上通知应用程序
signal.Notify(gracefulStop, syscall.SIGTERM)
signal.Notify(gracefulStop, syscall.SIGINT)
// 启动一个工作协程,其工作是始终监听 gracefulStop 信号
go func() {
// 等待操作系统信号停止应用程序
// 在 gracefulStop 通道上
// 这个协程将阻塞,直到我们收到一个操作系统信号
sig := <-gracefulStop
fmt.Printf("caught sig: %+v", sig)
// 在 "finish up" 通道上发送消息,告诉应用程序优雅地关闭
finishUP <- struct{}{}
// 等待是否完成的消息
select {
case <-time.After(30*time.Second):
// 等待应用程序完成超过 30 秒,超时,
// 我们的应用程序应该 Exit(1)
returnCode <- 1
case <-done:
// 如果我们收到了 done 的消息,表示我们已经完成,所以结束应用程序
// 我们的应用程序应该 Exit(0)
returnCode <- 0
}
}()
// ... 在协程中执行业务逻辑
fmt.Println("waiting for finish")
// 等待 finishUP 通道写入以关闭应用程序
<-finishUP
fmt.Println("stopping things, might take 2 seconds")
// ... 模拟关闭的业务逻辑,通过 Sleep 2 秒
time.Sleep(2*time.Second)
// 向 done 通道写入消息,表示我们已完成。
done <- struct{}{}
os.Exit(<-returnCode)
}
我通过以下命令构建镜像:
docker build -f Dockerfile -t docker-shutdown .
我通过以下命令启动容器:
docker run docker-shutdown
然后我尝试通过以下命令关闭它:
docker stop <container id>
在运行 'docker run' 的控制台中,我可以看到:
waiting for finish
然后它就终止了,没有进一步的输出。
我期望至少能看到一些输出,例如:
caught sig <SIGNAL_VALUE>
那么为什么我的预期行为没有发生?我对 go
也不熟悉,所以我不确定这是否与 go
代码或 Dockerfile
中的 CMD
定义有关。
我的问题是:
为什么 go
进程无法捕获 TERM 和 KILL 信号?
英文:
I want to be able to shutdown a docker process gracefully. I derived the these files by following the idea in this blog post: https://husobee.github.io/golang/ecs/2016/05/19/ecs-graceful-go-shutdown.html
Here are my files
-
Dockerfile
FROM debian:jessie
ADD app /app
RUN apt-get update --fix-missing
RUN apt-get install -y golang
CMD ["go", "run", "/app/main.go"] -
app/main.go
package main
import "os"
import "syscall"
import "fmt"
import "time"
import "os/signal"func main() {
// create a "returnCode" channel which will be the return code of the application
var returnCode = make(chan int)// finishUP channel signals the application to finish up var finishUP = make(chan struct{}) // done channel signals the signal handler that the application has completed var done = make(chan struct{}) // gracefulStop is a channel of os.Signals that we will watch for -SIGTERM var gracefulStop = make(chan os.Signal) // watch for SIGTERM and SIGINT from the operating system, and notify the app on // the gracefulStop channel signal.Notify(gracefulStop, syscall.SIGTERM) signal.Notify(gracefulStop, syscall.SIGINT) // launch a worker whose job it is to always watch for gracefulStop signals go func() { // wait for our os signal to stop the app // on the graceful stop channel // this goroutine will block until we get an OS signal sig := <-gracefulStop fmt.Printf("caught sig: %+v", sig) // send message on "finish up" channel to tell the app to // gracefully shutdown finishUP<-struct{}{} // wait for word back if we finished or not select { case <-time.After(30*time.Second): // timeout after 30 seconds waiting for app to finish, // our application should Exit(1) returnCode<-1 case <-done: // if we got a message on done, we finished, so end app // our application should Exit(0) returnCode<-0 } }() // ... Do business Logic in goroutines fmt.Println("waiting for finish") // wait for finishUP channel write to close the app down <-finishUP fmt.Println("stopping things, might take 2 seconds") // ... Do business Logic for shutdown simulated by Sleep 2 seconds time.Sleep(2*time.Second) // write to the done channel to signal we are done. done <-struct{}{} os.Exit(<-returnCode)
}
I build the image by running
docker build -f Dockerfile -t docker-shutdown .
I start a container by
docker run docker-shutdown
Then I try to shut it down by
docker stop <container id>
In the console that runs 'docker run', I can see:
waiting for finish
and it just terminated with no further output.
I would expect to see at least some output like
caught sig <SIGNAL_VALUE>
So why my expected behavior did not happen? I am also new to go
so I am not sure if it may have something to do with the go
code or the CMD
definition in the Dockerfile
.
My question:
Why the go
process cannot catch the either TERM and KILL signals?
答案1
得分: 4
go run
接收到信号的是正在运行的程序,而不是被运行的程序。
预先编译程序并直接在 Docker 中运行二进制文件,它将按预期工作。
英文:
go run
is getting the signal, not the program that is being run.
Compile the program ahead of time and run the binary directly in docker, and it will work as expected.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论