build: local stack, container images and the end-to-end smoke script

docker-compose brings up PostgreSQL 16 with pgvector and Redis 7 on
non-standard host ports so it never collides with an existing local database.
scripts/smoke.sh is the proof: compose, migrate, boot the API, issue a token,
call the real DeepSeek API and print the resulting ob_ai_call and bl_ledger
rows. services/vision and services/matting are placeholders noting that the
local RTX 5090 makes BiRefNet and RTMPose developable without cloud GPU.
This commit is contained in:
xsl
2026-08-14 00:26:39 +08:00
parent 93b88db2f5
commit 8a4720472b
14 changed files with 481 additions and 0 deletions
+55
View File
@@ -0,0 +1,55 @@
# syntax=docker/dockerfile:1
# API image (架构文档 §14.1 CVM-app / api). Multi-stage so the runtime layer
# carries no build toolchain and no dev dependencies.
FROM python:3.12-slim AS builder
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
ENV UV_COMPILE_BYTECODE=1 \
UV_LINK_MODE=copy \
UV_PYTHON_DOWNLOADS=never
WORKDIR /app
COPY pyproject.toml uv.lock* README.md ./
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --frozen --no-install-project --no-dev || uv sync --no-install-project --no-dev
COPY apps ./apps
COPY packages ./packages
COPY services ./services
COPY prompts ./prompts
COPY migrations ./migrations
COPY alembic.ini ./
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --no-dev --frozen || uv sync --no-dev
FROM python:3.12-slim AS runtime
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PATH="/app/.venv/bin:$PATH" \
TZ=Asia/Shanghai
RUN groupadd --system meida && useradd --system --gid meida --home /app meida
WORKDIR /app
COPY --from=builder --chown=meida:meida /app /app
USER meida
EXPOSE 8000
HEALTHCHECK --interval=15s --timeout=5s --start-period=20s --retries=3 \
CMD python -c "import urllib.request;urllib.request.urlopen('http://localhost:8000/healthz')"
# Gunicorn supervises uvicorn workers (架构文档 §5: FastAPI + Uvicorn/Gunicorn).
CMD ["gunicorn", "apps.api.main:app", \
"--worker-class", "uvicorn.workers.UvicornWorker", \
"--workers", "4", \
"--bind", "0.0.0.0:8000", \
"--timeout", "120", \
"--graceful-timeout", "30", \
"--access-logfile", "-"]