From 77e21b54e6cfaa85b8cf9ed49f6509252485724b Mon Sep 17 00:00:00 2001 From: gahusb Date: Fri, 22 May 2026 01:44:48 +0900 Subject: [PATCH] feat(task-watcher): main.py + Dockerfile + requirements + env (SP-10) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FastAPI lifespan에서 watcher_loop 스폰. /health. tzdata(zoneinfo Asia/Seoul). .env: REDIS_URL, STOCK_BASE_URL, TRADING_START/END. Plan-B-Infra Phase 2. Co-Authored-By: Claude Opus 4.7 (1M context) --- services/task-watcher/.env.example | 11 ++++++++ services/task-watcher/Dockerfile | 16 ++++++++++++ services/task-watcher/main.py | 36 ++++++++++++++++++++++++++ services/task-watcher/requirements.txt | 5 ++++ 4 files changed, 68 insertions(+) create mode 100644 services/task-watcher/.env.example create mode 100644 services/task-watcher/Dockerfile create mode 100644 services/task-watcher/main.py create mode 100644 services/task-watcher/requirements.txt diff --git a/services/task-watcher/.env.example b/services/task-watcher/.env.example new file mode 100644 index 0000000..77a9777 --- /dev/null +++ b/services/task-watcher/.env.example @@ -0,0 +1,11 @@ +# Plan-B-Infra — task-watcher + +# NAS Redis +REDIS_URL=redis://192.168.45.54:6379 + +# NAS stock holidays endpoint +STOCK_BASE_URL=http://192.168.45.54:18500 + +# 트레이딩 윈도우 (KST, HH:MM) — 이 시간대에만 queue:paused +TRADING_START=07:00 +TRADING_END=16:30 diff --git a/services/task-watcher/Dockerfile b/services/task-watcher/Dockerfile new file mode 100644 index 0000000..959ca84 --- /dev/null +++ b/services/task-watcher/Dockerfile @@ -0,0 +1,16 @@ +FROM python:3.12-slim-bookworm +ENV PYTHONUNBUFFERED=1 + +WORKDIR /app + +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates tzdata \ + && rm -rf /var/lib/apt/lists/* + +COPY requirements.txt . +RUN pip install --no-cache-dir --timeout 600 --retries 5 -r requirements.txt + +COPY . . + +EXPOSE 8000 +CMD ["python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "1"] diff --git a/services/task-watcher/main.py b/services/task-watcher/main.py new file mode 100644 index 0000000..00b6876 --- /dev/null +++ b/services/task-watcher/main.py @@ -0,0 +1,36 @@ +"""task-watcher FastAPI entry — health + lifespan (watcher loop spawn).""" +from __future__ import annotations + +import asyncio +import logging +from contextlib import asynccontextmanager + +from fastapi import FastAPI + +import watcher + +logging.basicConfig(level=logging.INFO, format="%(asctime)s %(name)s %(levelname)s %(message)s") +logger = logging.getLogger(__name__) + + +@asynccontextmanager +async def lifespan(app: FastAPI): + watcher_task = asyncio.create_task(watcher.watcher_loop()) + logger.info("task-watcher lifespan 시작") + try: + yield + finally: + watcher_task.cancel() + try: + await watcher_task + except asyncio.CancelledError: + pass + logger.info("task-watcher lifespan 종료") + + +app = FastAPI(lifespan=lifespan) + + +@app.get("/health") +def health(): + return {"ok": True, "service": "task-watcher"} diff --git a/services/task-watcher/requirements.txt b/services/task-watcher/requirements.txt new file mode 100644 index 0000000..1b3639e --- /dev/null +++ b/services/task-watcher/requirements.txt @@ -0,0 +1,5 @@ +fastapi==0.115.6 +uvicorn[standard]==0.34.0 +redis>=5.0 +httpx>=0.27 +pytest>=8.0