You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Docker容器内FastAPI lifespan的shutdown事件未执行的问题

问题

以下FastAPI代码在Docker容器和本地终端直接运行时,收到Ctrl+C后的表现不一致:

from contextlib import asynccontextmanager
from fastapi import FastAPI
from uvicorn import run

@asynccontextmanager
async def lifespan(app: FastAPI):
    # startup
    logger.info("[IN SYSTEM STARTUP]")     
    logger.info("[STARTUP DONE]")
    
    yield
    # shutdown
    logger.info("[IN SHUTDOWN]")
    logger.info("[IN SHUTDOWN] Done")

app = FastAPI(lifespan=lifespan)

容器内运行日志(Ctrl+C未执行shutdown逻辑)

event_deliver | INFO:     Waiting for application startup.
event_deliver | 2023-09-05 02:50:22,092 - logger INFO - [IN SYSTEM STARTUP]
event_deliver | 2023-09-05 02:50:22,092 - logger INFO - [STARTUP DONE]
event_deliver | INFO:     Application startup complete.
event_deliver | INFO:     Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
^CGracefully stopping... (press Ctrl+C again to force)
Stopping event_deliver   ... done
Stopping redis           ... done

本地终端运行日志(Ctrl+C正常执行shutdown逻辑)

2023-09-05 02:57:48,307 - logger INFO - [IN SYSTEM STARTUP]
2023-09-05 02:57:48,307 - logger INFO - [STARTUP DONE]
INFO:     Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
^CINFO:     Shutting down
INFO:     Waiting for application shutdown.
2023-09-05 02:57:52,523 - logger INFO - [IN SHUTDOWN]
2023-09-05 02:57:52,523 - logger INFO - [IN SHUTDOWN] Done
INFO:     Application shutdown complete.
INFO:     Finished server process [70464]

请问造成这种差异的原因是什么?

原因分析
  • PID 1进程的信号处理特性:Docker容器默认将启动命令作为PID 1进程,而Linux系统中PID 1进程不会自动转发SIGINT(Ctrl+C发送的信号)给子进程。如果容器内的启动逻辑没有正确处理信号,Uvicorn进程收不到终止信号,就无法触发shutdown代码。而本地终端中Uvicorn是非PID 1进程,能正常捕获SIGINT。
  • Docker Compose的停止机制:从日志看使用了Docker Compose,按下Ctrl+C时,Compose会先向容器发送SIGTERM信号,若进程未在指定时间内退出,就会发送SIGKILL强制终止。如果Uvicorn未配置处理SIGTERM,或者容器内的PID 1进程没有把SIGTERM转发给Uvicorn,进程会被直接杀死,来不及执行shutdown逻辑。
  • 启动方式的信号传递差异:本地直接用uvicorn命令运行时,Uvicorn是前台进程,能直接接收终端发送的信号。但容器中如果用shell脚本启动、或者启动命令未用exec形式执行,会导致Uvicorn成为shell的子进程,而作为PID 1的shell不会转发信号,最终Uvicorn收不到终止信号,无法执行shutdown逻辑。

内容的提问来源于stack exchange,提问作者coder_boy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 09:42:27