From 4db0551d338759f4f3ff92ef695ff153bb18148b Mon Sep 17 00:00:00 2001 From: gahusb Date: Tue, 19 May 2026 08:42:34 +0900 Subject: [PATCH] feat(video-render): main.py + services/docker-compose entry (SP-7) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FastAPI lifespan에서 worker_loop 스폰. /health endpoint. docker-compose: port 18712, NAS_BASE_URL default=18801 (video-lab), 4 provider env (OPENAI_API_KEY, GOOGLE_*, PIAPI_API_KEY, SEEDANCE_API_KEY), GCP service account JSON read-only mount. Plan-B-Video Phase 2 완료 — 박재오 머신에서 .env + GCP JSON 작성 + 빌드 대기. Co-Authored-By: Claude Opus 4.7 (1M context) --- services/docker-compose.yml | 30 +++++++++++++++++++++++++++++ services/video-render/main.py | 36 +++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 services/video-render/main.py diff --git a/services/docker-compose.yml b/services/docker-compose.yml index 77dce0d..d81f9cf 100644 --- a/services/docker-compose.yml +++ b/services/docker-compose.yml @@ -49,3 +49,33 @@ services: interval: 60s timeout: 5s retries: 3 + + video-render: + build: + context: ./video-render + container_name: video-render + restart: unless-stopped + ports: + - "18712:8000" + environment: + - TZ=Asia/Seoul + - REDIS_URL=${REDIS_URL:-redis://192.168.45.54:6379} + - NAS_BASE_URL=${NAS_BASE_URL:-http://192.168.45.54:18801} + - INTERNAL_API_KEY=${INTERNAL_API_KEY:-} + - OPENAI_API_KEY=${OPENAI_API_KEY:-} + - GOOGLE_PROJECT_ID=${GOOGLE_PROJECT_ID:-} + - GOOGLE_LOCATION=${GOOGLE_LOCATION:-us-central1} + - GOOGLE_GCS_BUCKET=${GOOGLE_GCS_BUCKET:-} + - GOOGLE_APPLICATION_CREDENTIALS=/app/keys/gcp-sa.json + - PIAPI_API_KEY=${PIAPI_API_KEY:-} + - SEEDANCE_API_KEY=${SEEDANCE_API_KEY:-} + - VIDEO_MEDIA_ROOT=${VIDEO_MEDIA_ROOT:-/mnt/nas/webpage/data/video} + - VIDEO_MEDIA_URL_PREFIX=${VIDEO_MEDIA_URL_PREFIX:-/media/video} + volumes: + - /mnt/nas/webpage/data/video:/mnt/nas/webpage/data/video + - ${GCP_SA_JSON_HOST_PATH:-/etc/webai/gcp-sa.json}:/app/keys/gcp-sa.json:ro + healthcheck: + test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"] + interval: 60s + timeout: 5s + retries: 3 diff --git a/services/video-render/main.py b/services/video-render/main.py new file mode 100644 index 0000000..4641de3 --- /dev/null +++ b/services/video-render/main.py @@ -0,0 +1,36 @@ +"""video-render FastAPI entry — health + lifespan (worker loop spawn).""" +from __future__ import annotations + +import asyncio +import logging +from contextlib import asynccontextmanager + +from fastapi import FastAPI + +import worker + +logging.basicConfig(level=logging.INFO, format="%(asctime)s %(name)s %(levelname)s %(message)s") +logger = logging.getLogger(__name__) + + +@asynccontextmanager +async def lifespan(app: FastAPI): + worker_task = asyncio.create_task(worker.worker_loop()) + logger.info("video-render lifespan 시작") + try: + yield + finally: + worker_task.cancel() + try: + await worker_task + except asyncio.CancelledError: + pass + logger.info("video-render lifespan 종료") + + +app = FastAPI(lifespan=lifespan) + + +@app.get("/health") +def health(): + return {"ok": True, "service": "video-render"}