importsignalimportsysfromthreadingimportEventshutdown_event=Event()defhandle_sigterm(signum,frame):print("SIGTERM received, shutting down gracefully...")shutdown_event.set()signal.signal(signal.SIGTERM,handle_sigterm)# In your main loop or serverwhilenotshutdown_event.is_set():process_next_item()# Cleanupfinish_pending_work()close_connections()sys.exit(0)
funcmain(){srv:=&http.Server{Addr:":8080"}gofunc(){iferr:=srv.ListenAndServe();err!=http.ErrServerClosed{log.Fatal(err)}}()// Wait for SIGTERMquit:=make(chanos.Signal,1)signal.Notify(quit,syscall.SIGTERM,syscall.SIGINT)<-quit// Graceful shutdown with timeoutctx,cancel:=context.WithTimeout(context.Background(),30*time.Second)defercancel()iferr:=srv.Shutdown(ctx);err!=nil{log.Printf("Server forced to shutdown: %v",err)}}
shutdown_in_progress=False@app.get("/health/ready")defreadiness():ifshutdown_in_progress:returnResponse(status_code=503)return{"status":"ok"}defhandle_sigterm():globalshutdown_in_progressshutdown_in_progress=True# Continue serving existing requests# But readiness probe fails, so no new traffic
Kubernetes stops sending traffic when readiness fails.
asyncdefshutdown():# 1. Stop accepting new workshutdown_flag.set()# 2. Wait for in-flight queriesawaitasyncio.sleep(5)# 3. Close connection poolawaitdb_pool.close()
defworker():whileTrue:ifshutdown_event.is_set():# Finish current job, don't take new onesbreakjob=queue.get(timeout=1)ifjob:process(job)queue.task_done()# Acknowledge we're donequeue.join()
# Start your app./myapp &APP_PID=$!# Start a slow requestcurl http://localhost:8080/slow &# Send SIGTERMkill -TERM $APP_PID# Did the slow request complete?wait
If the request completes with a valid response, you’re handling shutdown correctly.