feat: run FastAPI under gunicorn with uvicorn ASGI workers
Production entrypoint switch:
- pyproject.toml: add gunicorn>=23.0 dep
- gunicorn.conf.py: env-var-driven config (bind, workers, threads,
timeout, graceful_timeout, keepalive, log level, access-log format)
- Dockerfile: CMD gunicorn main:app (auto-loads gunicorn.conf.py from
WORKDIR /app)
- docker-compose.yml: forward GUNICORN_WORKERS / GUNICORN_TIMEOUT /
GUNICORN_BIND
- .env.example: document the new tunables
Why gunicorn over standalone uvicorn for production:
- Process supervision: master restarts crashed workers, restarts on
memory leaks
- Graceful shutdown: SIGTERM drains workers in-flight
- Multi-worker: concurrent requests actually run in parallel
- Standard ops: k8s readiness probes, log aggregators, etc. all know
gunicorn
Why uvicorn workers (not sync workers): gunicorn can't natively serve
ASGI; uvicorn.workers.UvicornWorker is the canonical way to run an
ASGI app under gunicorn.
Defaults:
- 2 workers (small MCP service; raise for high concurrency)
- 1 thread per worker (no blocking I/O)
- 120s timeout (yarn logs can be slow; uvicorn's 30s default is too
tight)
Verified: gunicorn boots, lifespan runs (14 tools logged), MCP
initialize + tools/list + tools/call all work, /openapi.json = 200,
multiple gunicorn worker processes visible in ps.
uv sync picked up gunicorn 26.0.0. Tests still 116/116.
This commit is contained in:
+11
-2
@@ -55,11 +55,20 @@ RUN uv sync --index-url=https://pypi.tuna.tsinghua.edu.cn/simple/ --frozen --no-
|
||||
COPY main.py ./
|
||||
COPY spark_executor ./spark_executor
|
||||
COPY common ./common
|
||||
COPY gunicorn.conf.py ./
|
||||
RUN uv sync --index-url=https://pypi.tuna.tsinghua.edu.cn/simple/ --frozen --no-dev
|
||||
|
||||
# Put the venv on PATH so `python` / `uvicorn` resolve to the project env.
|
||||
# Put the venv on PATH so `python` / `gunicorn` / `uvicorn` resolve to the project env.
|
||||
ENV PATH=/app/.venv/bin:$PATH
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
|
||||
# gunicorn is the prod entrypoint — multiple ASGI workers, graceful
|
||||
# shutdown, stdout/stderr logs. Config knobs are env-var driven (see
|
||||
# gunicorn.conf.py).
|
||||
#
|
||||
# Common overrides via -e flags at `docker run`:
|
||||
# -e GUNICORN_WORKERS=4
|
||||
# -e GUNICORN_TIMEOUT=180
|
||||
# -e GUNICORN_BIND=0.0.0.0:9000
|
||||
EXPOSE 8000
|
||||
CMD ["python", "main.py"]
|
||||
CMD ["gunicorn", "main:app"]
|
||||
|
||||
Reference in New Issue
Block a user