# DXA Quality Assessment — контейнер для инференса (CPU) # # Сборка: docker build -t rell.ru:5000/rell-bone-2026:latest . # Запуск: docker run -v /data/bone_2026/models:/app/models:ro -p 8000:8000 dxa-quality # # Веса в образ НЕ копируются: swarm монтирует каталог с чекпоинтом в # /app/models только для чтения, а models/ не попадает в git (см. .gitignore), # поэтому COPY models ломал бы сборку в CI. Путь к чекпоинту берётся из # DXA_MODEL_PATH и по умолчанию указывает на смонтированный каталог. # # Требования методики: зафиксированные версии зависимостей и работа без # внешних сервисов. Inference не скачивает веса из сети — они внутри # чекпоинта, поэтому при запуске сеть не нужна. FROM python:3.11-slim # libgl1/libglib2.0-0 нужны opencv (импортируется через зависимости проекта), # libgomp1 — для параллельных циклов torch. RUN apt-get update && apt-get install -y --no-install-recommends \ libgl1 \ libglib2.0-0 \ libgomp1 \ && rm -rf /var/lib/apt/lists/* WORKDIR /app # --- Слой зависимостей: отдельно от кода, чтобы кэшировался --- # Все версии зафиксированы, torch ставится с CPU-индексом: образ для инференса # не требует GPU, а версия из PyPI на Linux подтянула бы CUDA-колёса. COPY requirements.txt ./ RUN pip install --no-cache-dir --upgrade pip \ && pip install --no-cache-dir \ --extra-index-url https://download.pytorch.org/whl/cpu \ -r requirements.txt # --- Код --- COPY src ./src # Скрипт пакетной обработки (оценка берёт его из корня образа). COPY run.sh ./run.sh RUN chmod +x ./run.sh # Smoke-проверка импорта: падает на сборке, если расходятся зависимости или # модуль не найден, вместо тихой 500-й на каждом запросе в рантайме. Модель # здесь не загружается — чекпоинта в контексте сборки нет. RUN python -c "import src.main; print('app import ok')" # --- Проверка статики для офлайн-работы --- # Веб-интерфейс не должен зависеть от CDN: Tailwind и FontAwesome лежат в # src/api/static/vendor и src/api/static/webfonts (см. index.html). RUN python -c "import os; \ files=['src/api/static/vendor/tailwind.js','src/api/static/vendor/fontawesome.css', \ 'src/api/static/webfonts/fa-solid-900.woff2']; \ missing=[f for f in files if not os.path.exists(f)]; \ assert not missing, f'missing frontend assets: {missing}'; \ print('frontend assets ok')" # Каталоги монтирований swarm: создаём заранее, чтобы образ вёл себя одинаково # и при обычном `docker run` без volume. RUN mkdir -p /app/data /app/logs # --- Переменные окружения --- # Читается кодом только DXA_MODEL_PATH (src/main.py). MODEL_PATH, DATA_PATH, # ANNOTATION_DIR, APP_* swarm передаёт по инерции от прежней версии — код их # не использует, и они ни на что не влияют. ENV PYTHONUNBUFFERED=1 \ PYTHONPATH=/app \ DXA_MODEL_PATH=/app/models/dxa_model.pth \ MODEL_PATH=/app/models \ DATA_PATH=/app/data \ ANNOTATION_DIR=/app/data/annotations EXPOSE 8000 # HEALTHCHECK опирается на /api/v1/health, который отдаёт model_loaded. HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ CMD python -c "import urllib.request,sys; \ r=urllib.request.urlopen('http://127.0.0.1:8000/api/v1/health', timeout=5); \ sys.exit(0 if r.status==200 else 1)" CMD ["python", "-m", "uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8000"]