无法优雅地关闭 Docker 进程

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

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 进程无法捕获 TERMKILL 信号?

英文:

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

  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() {
    // 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 := &lt;-gracefulStop
     	fmt.Printf(&quot;caught sig: %+v&quot;, sig)
    
     	// send message on &quot;finish up&quot; channel to tell the app to
     	// gracefully shutdown
     	finishUP&lt;-struct{}{}
    
     	// wait for word back if we finished or not
     	select {
     	case &lt;-time.After(30*time.Second):
     		// timeout after 30 seconds waiting for app to finish,
     		// our application should Exit(1)
     		returnCode&lt;-1
     	case &lt;-done:
     		// if we got a message on done, we finished, so end app
     		// our application should Exit(0)
     		returnCode&lt;-0
     	}
     }()
    
    
     // ... Do business Logic in goroutines
    
     fmt.Println(&quot;waiting for finish&quot;)
     // wait for finishUP channel write to close the app down
     &lt;-finishUP
     fmt.Println(&quot;stopping things, might take 2 seconds&quot;)
    
     // ... 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 &lt;-struct{}{}
     os.Exit(&lt;-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 &lt;container id&gt;

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 &lt;SIGNAL_VALUE&gt;

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.

huangapple
  • 本文由 发表于 2017年8月9日 14:58:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/45583641.html
匿名

发表评论

匿名网友

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

确定