How to run a background timer in Flask How to run a background timer in Flask kubernetes kubernetes

How to run a background timer in Flask


At a guess you are using the builtin Flask development server. Such builtin development servers in Flask and Django, or anything based on the web server or WSGI server in the standard library are not intended for production systems and don't normally handle shutdown on signals properly.

As such, you should really use a proper production grade WSGI server such as Apache/mod_wsgi (mod_wsgi-express), gunicorn or uWSGI. These all handle signals properly and you don't have the issue with the development servers where they ignore the signal causing the container shutdown to be delayed, with it eventually being killed by Kubernetes when the shutdown timeout occurs.


This works with the internal server. The caveat is there is a /_shutdown URL that shuts the server down, and this is open to malicious shutdowns. If this is not what you want, then remove requests.post() and uncomment os._exit(). And of course remove @app.route("/_shutdown") and the function as well.

from flask import Flask, abort, requestimport signalimport threadingimport timeimport osimport requestsapp = Flask(__name__)shuttingDown = Falsedef exit_call():    time.sleep(20)    requests.post("http://localhost:5420/_shutdown")    # os._exit(0)def exit_gracefully(self, signum):    app.logger.error('Received shutdown signal. Exiting gracefully')    global shuttingDown    shuttingDown = True    # TODO: wait for some time here to ensure we are not receiving any more    # traffic    _et = threading.Thread(target=exit_call)    _et.daemon = True    _et.start()signal.signal(signal.SIGTERM, exit_gracefully)@app.route("/")def hello():        return "Hello World!"@app.route("/_status/liveness")def liveness():        return "I am alive"@app.route("/_shutdown", methods=["POST"])def shutdown():    func = request.environ.get('werkzeug.server.shutdown')    if func is None:        return "Not a werkzeug server"    func()    return "shutdown"@app.route("/_status/readiness")def readiness():        if not shuttingDown:            return "I am ready"        else:            abort(500, 'not ready anymore')app.run(port=5420)