I have a Python Flask app:
from flask import Flask, Response
import signal
app = Flask("myapp")
def exit_gracefully(*args):
print("Doing something here")
signal.signal(signal.SIGINT, exit_gracefully)
signal.signal(signal.SIGTERM, exit_gracefully)
@app.route("/myendpoint")
def myendpoint():
print("Doing something here")
return Response("Everything is fine", status=200, mimetype="application/json")
This app is ran by gunicorn, using a classic wsgi.py
file and a systemd service:
[Unit]
Description=myservice
[Service]
User=root
Group=root
WorkingDirectory=/opt/myservice
ExecStart=/opt/myservice/run.sh
[Install]
WantedBy=multi-user.target
#!/bin/bash
source env/bin/activate
env/bin/gunicorn --bind unix:gunicorn.sock --workers 1 --timeout 10 wsgi:app
I need to intercept SIGTERM signals sent to the service, because I need to perform some cleanup and interruptions (especially setting threading.Event()
flags)
However, the issue is that, when I type systemctl stop myservice
, the service hangs for 30 seconds before shutting down (which is probably gunicorn's graceful_timeout
setting), instead of shutting down immediately, as I intend to.
When looking in the source code of gunicorn, I notice that the Worker class uses a signal handler to shutdown itself gracefully:
# gunicorn/workers/base.py:173
signal.signal(signal.SIGTERM, self.handle_exit)
My guess is that, since it is possible to use only one handler for each signal, I am overriding this handler by using my own handler. But I still need both handlers, mine for my cleanup operations to be done and the default one for the gunicorn worker to stop properly.
Something maybe worth to mention is that, when I run run.sh
in my shell and send SIGINT to it (by pressing Ctrl+C), my app stopped immediately, despite gunicorn also implements an handler for it.
Any ideas on how I should proceed? I thought about calling the function handle_exit
in my own handler, but since my code is "gunicorn-agnostic", I have no idea about how I should do that. Should I change the design of my app?