feat(video-render): main.py + services/docker-compose entry (SP-7)
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) <noreply@anthropic.com>
This commit is contained in:
@@ -49,3 +49,33 @@ services:
|
|||||||
interval: 60s
|
interval: 60s
|
||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 3
|
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
|
||||||
|
|||||||
36
services/video-render/main.py
Normal file
36
services/video-render/main.py
Normal file
@@ -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"}
|
||||||
Reference in New Issue
Block a user