Flask+uWSGI优雅关闭方案求助:无法捕获SIGTERM/SIGKILL信号
可行的优雅关闭方案
一、保留Flask+uWSGI的方案
1. 用uWSGI信号API捕获终止信号
uWSGI会拦截原生Python信号,直接用uWSGI提供的API注册信号处理更可靠,适配你1进程4线程的场景:
from flask import Flask import uwsgi import signal app = Flask(__name__) def cleanup(signum, frame): # 写入你的关闭逻辑:关闭数据库连接、停止后台线程、清理临时资源等 print("Executing graceful shutdown...") # 示例:等待后台线程结束 # if background_thread.is_alive(): # background_thread.join(timeout=10) # 示例:关闭数据库连接池 # db_pool.close() uwsgi.stop() # 通知uWSGI停止当前worker # 仅在uWSGI环境下注册信号 if 'uwsgi' in locals(): # 捕获TERM(优雅关闭)和INT(强制中断)信号 signal.signal(signal.SIGTERM, cleanup) signal.signal(signal.SIGINT, cleanup)
对应的uWSGI配置文件(uwsgi.ini):
[uwsgi] module = your_app:app processes = 1 threads = 4 enable-threads = true # 确保线程被正确管理 die-on-term = true # 收到TERM信号后立即启动退出流程
启动命令:uwsgi --ini uwsgi.ini
关闭时发送TERM信号:kill -TERM $(ps aux | grep uwsgi | grep -v grep | awk '{print $2}')
2. 使用uWSGI Master FIFO(适合未来扩展多进程)
如果之后要扩展到多进程,Master FIFO是官方推荐的全局控制方式,以下是可运行示例:
第一步:修改uWSGI配置
[uwsgi] module = your_app:app master = true # 启用master进程管理worker processes = 1 threads = 4 enable-threads = true master-fifo = /tmp/uwsgi_control_fifo # 定义FIFO文件路径 die-on-term = true
第二步:在Flask应用中添加FIFO处理逻辑
from flask import Flask import uwsgi import os import signal app = Flask(__name__) # Worker进程的清理逻辑 def worker_cleanup(signum, frame): print("Worker process cleaning up...") # 线程级资源清理 uwsgi.stop() # Master进程的FIFO处理逻辑 def handle_fifo_commands(signum, frame): if not os.path.exists('/tmp/uwsgi_control_fifo'): return with open('/tmp/uwsgi_control_fifo', 'rb') as f: cmd = f.read().strip() if cmd == b'stop': print("Master received stop command, triggering global shutdown...") # 通知所有worker执行清理 uwsgi.send_to_all_workers(signal.SIGTERM) # 等待worker退出后停止master uwsgi.stop() # 根据进程角色注册逻辑 if 'uwsgi' in locals(): if uwsgi.masterpid() == 0: # 当前是worker进程,注册终止信号 signal.signal(signal.SIGTERM, worker_cleanup) else: # 当前是master进程,注册FIFO检查信号 uwsgi.register_signal(20, "master", handle_fifo_commands) # 每隔1秒检查一次FIFO(可调整间隔) uwsgi.add_timer(20, 1)
第三步:触发优雅关闭
执行命令向FIFO写入停止指令:
echo "stop" > /tmp/uwsgi_control_fifo
二、替换为Gunicorn+Flask(更简单的优雅关闭)
Gunicorn对信号处理的支持更成熟,文档也更完善,适合快速解决问题:
1. 安装依赖
pip install gunicorn
2. 在Flask应用中添加清理逻辑
from flask import Flask import signal import sys app = Flask(__name__) def graceful_shutdown(signum, frame): print("Executing graceful shutdown...") # 执行你的清理逻辑 sys.exit(0) # 注册终止信号 signal.signal(signal.SIGTERM, graceful_shutdown) signal.signal(signal.SIGINT, graceful_shutdown)
3. 启动Gunicorn并启用优雅关闭
gunicorn --workers=1 --threads=4 --timeout=30 --graceful-timeout=15 your_app:app --pid=gunicorn.pid
--graceful-timeout=15:给15秒时间完成清理,超时后强制退出- 关闭时发送TERM信号:
kill -TERM $(cat gunicorn.pid)
三、替换为FastAPI+Uvicorn(ASGI原生优雅关闭)
FastAPI基于ASGI标准,Uvicorn原生支持生命周期管理,不需要手动处理信号:
1. 安装依赖
pip install fastapi uvicorn
2. 利用生命周期事件实现优雅关闭
from fastapi import FastAPI from contextlib import asynccontextmanager @asynccontextmanager async def lifespan(app: FastAPI): # 服务启动时的初始化逻辑(可选) print("Service starting...") yield # 服务运行中 # 服务关闭时的清理逻辑 print("Executing graceful shutdown...") # 清理异步资源:关闭数据库连接、停止异步任务等 app = FastAPI(lifespan=lifespan) @app.get("/") async def root(): return {"message": "Hello World"}
3. 启动Uvicorn
uvicorn --workers=1 --threads=4 --timeout-graceful-shutdown=15 your_app:app --pid=uvicorn.pid
--timeout-graceful-shutdown=15:设置优雅关闭超时时间- 关闭时发送TERM信号即可:
kill -TERM $(cat uvicorn.pid)
关于Kubernetes preStop钩子的修复
你之前的preStop没生效,可能是因为钩子执行时机或命令问题,试试这个配置:
spec: containers: - name: your-app image: your-image lifecycle: preStop: exec: command: ["/bin/sh", "-c", "sleep 3 && curl -X POST http://localhost:8000/internal/shutdown"]
然后在你的应用中添加一个仅限本地访问的关闭接口:
# Flask示例 from flask import request @app.route('/internal/shutdown', methods=['POST']) def shutdown(): if request.remote_addr != '127.0.0.1': return "Forbidden", 403 graceful_shutdown(signal.SIGTERM, None) return "Shutting down..."
这样preStop钩子会先调用接口触发清理,再等待容器终止。
内容的提问来源于stack exchange,提问作者Vaibhav
相关产品推荐
相关产品推荐

