无法优雅地关闭 Docker 进程

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

Unable to gracefully shutdown a docker process

问题

我想要能够优雅地关闭一个 Docker 进程。我按照这篇博文的思路创建了以下文件:https://husobee.github.io/golang/ecs/2016/05/19/ecs-graceful-go-shutdown.html

以下是我的文件:

1)Dockerfile

  1. FROM debian:jessie
  2. ADD app /app
  3. RUN apt-get update --fix-missing
  4. RUN apt-get install -y golang
  5. CMD ["go", "run", "/app/main.go"]

2)app/main.go

  1. package main
  2. import "os"
  3. import "syscall"
  4. import "fmt"
  5. import "time"
  6. import "os/signal"
  7. func main() {
  8. // 创建一个 "returnCode" 通道,用于应用程序的返回代码
  9. var returnCode = make(chan int)
  10. // finishUP 通道用于通知应用程序完成
  11. var finishUP = make(chan struct{})
  12. // done 通道用于通知信号处理程序应用程序已完成
  13. var done = make(chan struct{})
  14. // gracefulStop 是一个 os.Signal 的通道,我们将监听 -SIGTERM 信号
  15. var gracefulStop = make(chan os.Signal)
  16. // 监听来自操作系统的 SIGTERM 和 SIGINT 信号,并在 gracefulStop 通道上通知应用程序
  17. signal.Notify(gracefulStop, syscall.SIGTERM)
  18. signal.Notify(gracefulStop, syscall.SIGINT)
  19. // 启动一个工作协程,其工作是始终监听 gracefulStop 信号
  20. go func() {
  21. // 等待操作系统信号停止应用程序
  22. // 在 gracefulStop 通道上
  23. // 这个协程将阻塞,直到我们收到一个操作系统信号
  24. sig := <-gracefulStop
  25. fmt.Printf("caught sig: %+v", sig)
  26. // 在 "finish up" 通道上发送消息,告诉应用程序优雅地关闭
  27. finishUP <- struct{}{}
  28. // 等待是否完成的消息
  29. select {
  30. case <-time.After(30*time.Second):
  31. // 等待应用程序完成超过 30 秒,超时,
  32. // 我们的应用程序应该 Exit(1)
  33. returnCode <- 1
  34. case <-done:
  35. // 如果我们收到了 done 的消息,表示我们已经完成,所以结束应用程序
  36. // 我们的应用程序应该 Exit(0)
  37. returnCode <- 0
  38. }
  39. }()
  40. // ... 在协程中执行业务逻辑
  41. fmt.Println("waiting for finish")
  42. // 等待 finishUP 通道写入以关闭应用程序
  43. <-finishUP
  44. fmt.Println("stopping things, might take 2 seconds")
  45. // ... 模拟关闭的业务逻辑,通过 Sleep 2 秒
  46. time.Sleep(2*time.Second)
  47. // 向 done 通道写入消息,表示我们已完成。
  48. done <- struct{}{}
  49. os.Exit(<-returnCode)
  50. }

我通过以下命令构建镜像:

  1. docker build -f Dockerfile -t docker-shutdown .

我通过以下命令启动容器:

  1. docker run docker-shutdown

然后我尝试通过以下命令关闭它:

  1. docker stop <container id>

在运行 'docker run' 的控制台中,我可以看到:

  1. waiting for finish

然后它就终止了,没有进一步的输出。

我期望至少能看到一些输出,例如:

  1. 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)

    1. // finishUP channel signals the application to finish up
    2. var finishUP = make(chan struct{})
    3. // done channel signals the signal handler that the application has completed
    4. var done = make(chan struct{})
    5. // gracefulStop is a channel of os.Signals that we will watch for -SIGTERM
    6. var gracefulStop = make(chan os.Signal)
    7. // watch for SIGTERM and SIGINT from the operating system, and notify the app on
    8. // the gracefulStop channel
    9. signal.Notify(gracefulStop, syscall.SIGTERM)
    10. signal.Notify(gracefulStop, syscall.SIGINT)
    11. // launch a worker whose job it is to always watch for gracefulStop signals
    12. go func() {
    13. // wait for our os signal to stop the app
    14. // on the graceful stop channel
    15. // this goroutine will block until we get an OS signal
    16. sig := &lt;-gracefulStop
    17. fmt.Printf(&quot;caught sig: %+v&quot;, sig)
    18. // send message on &quot;finish up&quot; channel to tell the app to
    19. // gracefully shutdown
    20. finishUP&lt;-struct{}{}
    21. // wait for word back if we finished or not
    22. select {
    23. case &lt;-time.After(30*time.Second):
    24. // timeout after 30 seconds waiting for app to finish,
    25. // our application should Exit(1)
    26. returnCode&lt;-1
    27. case &lt;-done:
    28. // if we got a message on done, we finished, so end app
    29. // our application should Exit(0)
    30. returnCode&lt;-0
    31. }
    32. }()
    33. // ... Do business Logic in goroutines
    34. fmt.Println(&quot;waiting for finish&quot;)
    35. // wait for finishUP channel write to close the app down
    36. &lt;-finishUP
    37. fmt.Println(&quot;stopping things, might take 2 seconds&quot;)
    38. // ... Do business Logic for shutdown simulated by Sleep 2 seconds
    39. time.Sleep(2*time.Second)
    40. // write to the done channel to signal we are done.
    41. done &lt;-struct{}{}
    42. os.Exit(&lt;-returnCode)

    }

I build the image by running

  1. docker build -f Dockerfile -t docker-shutdown .

I start a container by

  1. docker run docker-shutdown

Then I try to shut it down by

  1. docker stop &lt;container id&gt;

In the console that runs 'docker run', I can see:

  1. waiting for finish

and it just terminated with no further output.

I would expect to see at least some output like

  1. 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:

确定