如何优雅关闭运行在Docker容器中的Go Web服务器?
Go Web服务在Docker中优雅关闭的实现验证
你的代码核心思路是可行的,但需要调整几个细节,才能完美适配Docker的容器停止流程,下面具体说明:
关键前提:Docker stop发送的信号
默认情况下,docker stop会先给容器内的主进程发送SIGTERM信号,等待10秒后如果进程未退出,会发送SIGKILL强制终止。所以你捕获SIGTERM是正确的,但建议同时监听SIGINT(比如容器内按Ctrl+C的场景),覆盖更多终止触发场景。
代码调整要点
信号监听补充
修改信号捕获逻辑,同时监听SIGTERM和SIGINT:ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGTERM, syscall.SIGINT)ListenAndServe的错误处理
原代码里的go srv.ListenAndServe()没有处理错误,除了http.ErrServerClosed(正常关闭的预期错误),其他错误需要捕获告警:go func() { if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatalf("服务启动失败: %v", err) } }()Shutdown的上下文优化
调用srv.Shutdown时,不要用已经触发Done的原ctx,应该创建新的带超时的上下文(超时时间建议比Docker的10秒短,预留缓冲):shutdownCtx, cancel := context.WithTimeout(context.Background(), 8*time.Second) defer cancel() err = srv.Shutdown(shutdownCtx)清理逻辑的位置
注释// do something的位置适合放置自定义清理操作,比如关闭数据库连接、停止后台异步任务等,要在Shutdown前完成必要的资源释放。
调整后的完整代码示例
package main import ( "context" "log" "net/http" "syscall" "time" ) func main() { port := ":8080" mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("Hello World")) }) // 监听Docker stop的SIGTERM和手动中断的SIGINT ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGTERM, syscall.SIGINT) defer stop() srv := &http.Server{ Addr: port, Handler: mux, } // 启动服务并处理启动错误 go func() { if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatalf("服务启动失败: %v", err) } }() // 等待终止信号 <-ctx.Done() log.Println("收到停止信号,开始优雅关闭...") // 执行自定义清理操作(示例:关闭数据库连接) // db.Close() // 创建带超时的上下文,确保Shutdown有足够时间处理请求 shutdownCtx, cancel := context.WithTimeout(context.Background(), 8*time.Second) defer cancel() // 执行优雅关闭 if err := srv.Shutdown(shutdownCtx); err != nil { log.Fatalf("服务关闭失败: %v", err) } log.Println("服务已优雅关闭") }
总结
你的核心实现思路是正确的:通过捕获信号触发Server.Shutdown()来完成优雅关闭,只要调整上述细节,就能完美适配Docker容器的停止流程。
内容的提问来源于stack exchange,提问作者kkkkgyg
相关产品推荐
相关产品推荐

