FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app ./app
EXPOSE 8000
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
requirements.txt를 app/ 코드보다 먼저 COPY하고 그 사이에 pip install을 끼운 순서가 핵심이다 — 의존성이 안 바낀 채 코드만 수정했을 때는 pip install 레이어가 캐시에서 그대로 재사용되어 빌드가 훨씬 빨라진다.
python:3.12-slim으로 버전을 명시적으로 고정한 이유는 로컬 검증 과정에서 실제로 격은 문제와 직결된다 — requirements.txt의 pydantic==2.10.4가 Python 3.14용 사전 빌드 wheel을 아직 제공하지 않아서, 베이스 이미지가 최신 Python을 따라갔다면 컸테이너 빌드 자체가 깨졌을 것이다.
services:
qdrant:
image: qdrant/qdrant:v1.12.4
ports: ["6333:6333", "6334:6334"]
volumes: ["qdrant_storage:/qdrant/storage"]
app:
build: .
ports: ["8000:8000"]
env_file: [".env"]
environment:
QDRANT_HOST: qdrant
depends_on: [qdrant]
target-tracking-service의 docker-compose.yml과 다른 점: 그쪽은 Postgres/Redis/Kafka/Zookeeper까지 전부 자체적으로 띄우는 "올인원" 구성인 반면, 이 서비스는 Qdrant만 자체적으로 띄우고 Kafka는 .env의 KAFKA_BOOTSTRAP_SERVERS로 기존에 떠 있는 공유 브로커를 가리키게 했다. Kafka 브로커를 이 레포에서 또 하나 띄우면 완전히 별개의 두 Kafka 클러스터가 생겨서 두 서비스가 같은 토픽을 공유한다는 전제 자체가 깨진다.
QDRANT_HOST: qdrant를 environment에 별도로 얹은 이유 — .env의 기본값은 로컬(venv로 직접 실행할 때)을 위해 localhost로 두고 싶었는데, docker-compose 네트워크 안에서는 서비스 이름(qdrant)이 공 호스트명이다. env_file보다 environment가 우선순위가 높다는 compose 규칙을 이용해서, .env 파일은 안 건드리고 compose 안에서만 오버라이드했다.
error: the configured Python interpreter version (3.14) is newer than PyO3's maximum supported version (3.13)
ERROR: Failed building wheel for pydantic-core
원인: 이 맥북의 시스템 기본 python3가 3.14였는데, pydantic==2.10.4가 의존하는 pydantic-core(Rust, PyO3 바인딩)는 아직 3.14용 사전 빌드 wheel을 안 올려둔 상태라 소스에서 직접 빌드를 시도했고, 그 빌드에 쓰이는 PyO3 버전이 3.14를 지원하지 않아 실패했다.
/opt/homebrew/bin/python3.12 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt # 성공
Homebrew로 이미 설치된 python3.12를 명시적으로 지정해서 해결했다. 이 레포의 의존성 스택(FastAPI/pydantic/langgraph 등)은 아직 Python 3.14를 완전히 지원하지 않으므로, 시스템 기본 python3를 그냥 믿고 쓰면 같은 문제에 부딞힌다.