Spaces:
Sleeping
Sleeping
File size: 4,547 Bytes
78738de | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | # PoolCoach — Hugging Face Spaces (Docker SDK) / VPS.
# HF tự build trên server của họ khi push — máy local KHÔNG cần Docker.
# Chi tiết: Documents/PoolCoach_Deploy_HFSpaces.md (repo poolcoach-docs).
#
# 14/08/2026 (BRIEF việc B): image này giờ chạy **Analyzer** (video → danh
# sách cú), không chỉ app gợi ý như bản 23/07. Ba thay đổi lớn, mỗi cái có
# lý do đứng riêng:
#
# 1. torch CPU + ultralytics + cv2 QUAY LẠI image (bản 23/07 đã gỡ vì engine
# ZonePlanner V2 không nạp net). Analyzer thì cần: detector bàn/bi, ShotNet
# c4b, decode video. Bản CPU lấy từ index pytorch riêng — bản PyPI là CUDA
# ~2.5GB cho một Space không có GPU.
# 2. MỘT tiến trình, không Redis, không worker rời: `POOLCOACH_LOCAL_CV=1`
# bật mode "local" (app/jobqueue.py) — transport fakeredis trong tiến
# trình + một LUỒNG gọi đúng hàm của scripts/cv_worker.py (app/localcv.py).
# Space free là một container; nuôi Redis + 2 worker ở đó là ba tiến
# trình và hai lần JIT pooltool trên 2 vCPU.
# 3. Ghi ở đâu: HF chạy container bằng uid 1000, /code là read-only với nó.
# Mọi thứ CẦN GHI phải ra /tmp — cache numba/mpl/ultralytics VÀ thư mục
# kết quả Analyzer (POOLCOACH_ANALYZER_DIR). Thiếu là app chết lúc
# runtime chứ không phải lúc build.
FROM python:3.12-slim
WORKDIR /code
# uid 1000 phải CÓ TRONG /etc/passwd, không chỉ "được ghi ở đâu đó".
# Đo được 14/08 khi chạy thử image bằng `docker run --user 1000:1000`:
# luồng CV chết ngay lúc nạp model với `KeyError: getpwuid(): uid not
# found: 1000` — thư viện (ultralytics/torch) hỏi HOME qua pwd, mà uid 1000
# không có entry thì ném KeyError chứ không fallback. App vẫn sống (luồng
# nhúng tự bắt lỗi) nhưng tab Analyzer chết câm trên Space. `-m` để
# /home/user tồn tại và thuộc uid 1000.
RUN useradd -m -u 1000 user
# ffmpeg : cắt clip per cú (cv_worker.cut_clip) + ffprobe đo thời lượng
# lúc upload (app/main.py). Thiếu ffmpeg thì mọi cú thành
# "ffmpeg không cắt được clip" — hỏng đúng tính năng chính.
# libgl1 + libglib2.0-0 : opencv-python (bản không headless) cần libGL.so.1
RUN apt-get update && apt-get install -y --no-install-recommends \
ffmpeg libgl1 libglib2.0-0 \
&& rm -rf /var/lib/apt/lists/*
RUN pip install --no-cache-dir uv
# torch CPU ở bước RIÊNG với --index-url CHỈ trỏ index CPU của pytorch —
# không được trộn với PyPI ở bước này, nếu không resolver có quyền chọn bản
# CUDA. Bước sau pin lại đúng hai phiên bản này (requirements-cv-cpu.txt) để
# ultralytics không kéo torch khác đè lên.
RUN uv pip install --system --index-url https://download.pytorch.org/whl/cpu \
torch==2.12.1 torchvision==0.27.1
# deps trước để tận dụng layer cache (requirements.txt đã chứa
# --extra-index-url archive.panda3d.org cho panda3d 1.11 dev)
COPY requirements.txt requirements-app.txt requirements-cv-cpu.txt ./
RUN uv pip install --system --index-strategy unsafe-best-match \
-r requirements.txt -r requirements-app.txt -r requirements-cv-cpu.txt
COPY src ./src
COPY app ./app
# CHỈ file worker, không cả scripts/ (train/eval/diag không thuộc image).
# Mode "local" nạp nó bằng đường dẫn — xem app/localcv.py.
COPY scripts/cv_worker.py ./scripts/cv_worker.py
# Đúng HAI model, đích danh: detector bàn/bi + ShotNet c4b đang chạy app.
# Cả models/ còn lại (mấy chục run RL/CV cũ) KHÔNG vào image.
COPY models/cv_full_20260805/best.pt ./models/cv_full_20260805/best.pt
COPY models/shotnet_20260812_c4b/best.pt ./models/shotnet_20260812_c4b/best.pt
# HF Spaces chạy container bằng uid 1000 (không ghi được /code):
# trỏ mọi cache/config VÀ thư mục kết quả Analyzer về /tmp.
ENV HOME=/tmp \
NUMBA_CACHE_DIR=/tmp/numba \
MPLCONFIGDIR=/tmp/mpl \
YOLO_CONFIG_DIR=/tmp \
PYTHONUNBUFFERED=1 \
POOLCOACH_LOCAL_CV=1 \
POOLCOACH_CV_DEVICE=cpu \
POOLCOACH_ANALYZER_DIR=/tmp/analyzer
EXPOSE 7860
# Chạy bằng chính uid HF sẽ dùng — để `docker run` trên máy giống Space,
# không phải phát hiện lỗi quyền sau khi đã push (bài học ngay ở trên).
USER user
CMD ["python", "-m", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "7860"]
|