Commit Graph
6 Commits
Author SHA1 Message Date
Claude 6bcf3b9ac9 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.
2026-06-25 10:44:25 +08:00
Claude 565661371b fix(Dockerfile): openjdk 17 install error 2026-06-24 17:53:38 +08:00
Claude 093e51a9c2 feat: update Dockerfile mirror 2026-06-24 17:49:12 +08:00
Claude 33294af477 fix(Dockerfile): install openjdk-17-jre-headless for spark-submit
Spark 4.1.2 requires Java 17 at runtime (it boots a JVM via the
bin/spark-submit shell script). The previous Dockerfile only installed
curl, ca-certificates, and tar — leaving spark-submit unable to find
the 'java' binary.

A JRE is sufficient (not the full JDK) because spark-submit does not
compile Java at runtime — it loads pre-built JARs from $SPARK_HOME/jars/.
JRE-only keeps the image ~160 MB smaller than JDK-headless.

Sets JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 and prepends it to
PATH so spark-submit (which checks JAVA_HOME) finds the right JVM.

Combined with the prior yarn-REST rewrite, the runtime image now needs
exactly three things in addition to python:3.12-slim:
  - uv (from ghcr.io/astral-sh/uv)
  - openjdk-17-jre-headless
  - Spark 4.1.2 from Tsinghua mirror
2026-06-24 17:37:44 +08:00
Claude bfdf4fd1ff feat: update Dockerfile 2026-06-24 17:36:14 +08:00
Claude 0d7b1d1ec3 feat: add Dockerfile 2026-06-24 17:07:23 +08:00