如何在Flask(uWSGI)服务中捕获SIGTERM/SIGINT信号?
解决uWSGI运行Flask服务时捕获K8S发送的SIGTERM/SIGINT信号问题
错误原因
你遇到的ValueError: Signal must be registered on the main thread,是因为Python的signal模块要求信号处理函数必须在主线程中注册,而你的类中信号注册逻辑大概率在子线程执行;同时uWSGI本身有独立的信号处理机制,直接混用Python原生signal容易引发冲突。
解决方案
1. 移至主线程注册Python原生信号处理函数
把信号注册逻辑放到程序启动的主线程代码中,不要放在Flask类的子线程方法或视图函数内。比如在Flask应用初始化的最外层执行:
import signal from flask import Flask app = Flask(__name__) def exit_gracefully(signum, frame): # 编写你的清理操作:关闭数据库连接、保存运行状态、通知依赖服务等 app.logger.info("Received SIGTERM/SIGINT, starting graceful shutdown") # 手动触发Flask服务关闭(如果适用) shutdown_func = getattr(request.environ.get('werkzeug.server.shutdown'), '__call__', None) if shutdown_func: shutdown_func() # uWSGI加载应用时,这段代码在主线程执行 if __name__ != '__main__': signal.signal(signal.SIGINT, exit_gracefully) signal.signal(signal.SIGTERM, exit_gracefully) @app.route('/') def index(): return "Hello World"
2. 使用uWSGI内置信号钩子(推荐)
uWSGI提供了专门的信号注册API,能更好地适配其运行时,避免线程问题。你可以直接在Python代码中绑定信号:
import uwsgi from flask import Flask app = Flask(__name__) def cleanup(): # 执行优雅终止逻辑 app.logger.info("Executing cleanup on SIGTERM/SIGINT") # 示例:关闭数据库连接 # db_session.close() # 在主线程注册uWSGI信号 # SIGTERM对应信号编号15,SIGINT对应编号2 uwsgi.register_signal(15, "", cleanup) uwsgi.register_signal(2, "", cleanup) # 启用信号监听 uwsgi.add_signal(15, "") uwsgi.add_signal(2, "") @app.route('/') def index(): return "Hello World"
也可以在uWSGI配置文件(如uwsgi.ini)中通过钩子绑定:
[uwsgi] module = app:app master = true processes = 1 threads = 4 # 绑定SIGTERM到清理脚本 hook-signal = SIGTERM:python /path/to/cleanup.py # 绑定SIGINT到清理脚本 hook-signal = SIGINT:python /path/to/cleanup.py
3. 配合K8S优雅终止配置
在K8S Pod配置中设置合理的优雅终止时间,确保清理逻辑有足够执行时间:
apiVersion: v1 kind: Pod metadata: name: flask-app spec: containers: - name: flask image: your-flask-image terminationGracePeriodSeconds: 60 # 根据清理逻辑耗时调整
注意事项
- 无论使用哪种方式,信号注册逻辑必须在主线程执行,子线程无法完成信号注册。
- SIGKILL信号无法被捕获,需保证清理逻辑能在K8S优雅终止时间内完成,避免被强制杀死。
- 若使用uWSGI master进程模式,确保信号能正确传递到工作进程,或直接在master进程中处理信号。
内容的提问来源于stack exchange,提问作者Vaibhav
相关产品推荐
相关产品推荐

