From 9c3321b3a7f8d46649221a9a220a1513f06c4bcb Mon Sep 17 00:00:00 2001 From: second_constantine Date: Tue, 21 Jul 2026 00:10:02 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B5=D0=B2=D0=BE=D0=B4=20tr?= =?UTF-8?q?acking=20=D0=BD=D0=B0=20SQLite=20+=20HTTP-=D0=BA=D0=BB=D0=B8?= =?UTF-8?q?=D0=B5=D0=BD=D1=82=D1=8B=20(file-store=20=D0=B7=D0=B0=D0=BF?= =?UTF-8?q?=D1=80=D0=B5=D1=89=D1=91=D0=BD=20=D0=B2=20MLflow=203.x)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - start_ui.sh: SQLite backend (sqlite:///mlflow.db) вместо file-store - все скрипты src/: mlflow.set_tracking_uri(http://localhost:5555) с env-override - README: сервер должен быть запущен до запуска скриптов - AGENTS.md: обновлена архитектура хранения и gotcha --- AGENTS.md | 8 ++++---- README.md | 8 +++++++- src/autolog_demo.py | 1 + src/compare_runs.py | 3 +++ src/grid_search_cv.py | 1 + src/hyperparam_sweep.py | 3 +++ src/load_and_predict.py | 3 +++ src/register_model.py | 3 +++ src/serve_model.py | 3 +++ src/train_gpu.py | 1 + src/train_simple.py | 1 + start_ui.sh | 43 +++++++++++++++++------------------------ 12 files changed, 48 insertions(+), 30 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index bb68944..945b1fe 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -28,7 +28,7 @@ mlflow-practice/ ├── python_env.yaml # окружение для MLproject (python 3.10) ├── requirements.txt # pip-зависимости ├── setup_server.sh # установка окружения (РАЗВИЛКА macOS/Linux) -├── start_ui.sh # запуск MLflow Tracking Server (UI на :5555, file-store) +├── start_ui.sh # запуск MLflow Tracking Server (UI на :5555, SQLite backend) ├── .gitignore ├── scripts/ │ └── setup_git.sh # настройка Git (HTTPS + токен) @@ -44,7 +44,7 @@ mlflow-practice/ │ └── grid_search_cv.py # Урок 9: GridSearchCV + autolog ├── data/ # датасеты (MNIST скачается автоматически) ├── artifacts/ # графики, отчёты (логируются в MLflow) -└── mlruns/ # локальный file store MLflow (в .gitignore) +└── mlruns/ # старые запуски file-store (в .gitignore, не используется) ``` --- @@ -143,10 +143,10 @@ mlflow run . -P epochs=10 -P batch_size=256 -P lr=0.001 --entry-point gpu - `autolog_demo.py` намеренно отключает сигнатуры (`log_model_signatures=False`) как демо опций autolog — там warning ожидаем. - **INFO про переменные окружения** (`OPENAI_API_KEY` и т.п.) при логировании подавлено через `MLFLOW_RECORD_ENV_VARS_IN_MODEL_LOGGING=false` в скриптах, логирующих модели. - **`python -m venv` падает на Linux (ensurepip)**: на Debian/Ubuntu без пакета `python3.13-venv` (или `python3-venv`) создание venv падает на шаге `ensurepip` (`non-zero exit status 1`). `setup_server.sh` ловит это и пробует fallback: `venv --without-pip` + bootstrap pip через `get-pip.py`. Альтернатива руками — `sudo apt-get install -y python3.13-venv python3.13-dev`. -- **File-store как СЕРВЕРНЫЙ бэкенд в maintenance mode (MLflow 3.x)**: `mlflow server --backend-store-uri file://...` по умолчанию падает с `MlflowException ... filesystem tracking backend is in maintenance mode`. Официальный opt-out — `export MLFLOW_ALLOW_FILE_STORE=true` (уже выставлено в `start_ui.sh` перед file-блоком). Это касается ТОЛЬКО сервера; клиентский file store (как пишут скрипты в `./mlruns`) работает без opt-out. Альтернатива без opt-out — SQLite-бэкенд (закомментированный блок в `start_ui.sh`), но тогда скрипты должны логировать на сервер через `mlflow.set_tracking_uri('http://localhost:5555')`. +- **File-store как tracking backend запрещён в MLflow 3.x** (maintenance mode): падает и `mlflow server --backend-store-uri file://...`, и клиент-скрипт с дефолтным `./mlruns` — оба с `MlflowException ... in maintenance mode`. Поэтому проект использует **SQLite** (`sqlite:///mlflow.db`) как tracking backend, а скрипты логируют на сервер по HTTP. Опциональный opt-out `MLFLOW_ALLOW_FILE_STORE=true` НЕ используем (он может быть убран в будущем). Артефакты при этом лежат на локальном диске (`./artifacts`, file artifact repository — он НЕ в maintenance mode). - **`input_example` для `mlflow.pytorch.log_model` — только numpy**, не `torch.Tensor`: MLflow 3.x валидирует пример входа и падает с `MlflowException ... but got ''`. В `train_gpu.py` передаётся `np.random.rand(1,1,28,28).astype(np.float32)`. Аналогично для других flavor'ов — пример должен быть DataFrame/ndarray/dict/list/скаляр. - **urllib3 `NotOpenSSLWarning`** на macOS (системный `ssl` собран с LibreSSL 2.8.3, urllib3 v2 просит OpenSSL 1.1.1+) — стороннее шумовое предупреждение, к проекту отношения не имеет; не подавляем (чтобы не прятать реальные предупреждения). -- **«Эксперимент не виден в UI» (главная причина)**: скрипты по умолчанию пишут в локальный file store `./mlruns`, а `start_ui.sh` раньше поднимал сервер на **SQLite** (`mlflow.db`) — два разных хранилища, UI ничего не показывал. Теперь `start_ui.sh` использует `--backend-store-uri file://${PWD}/mlruns` — **то же хранилище, что и скрипты**, поэтому всё видно без настройки tracking URI. Альтернатива (SQLite-бэкенд) оставлена закомментированной в `start_ui.sh`; при её включении в скриптах нужно задать `mlflow.set_tracking_uri('http://localhost:5555')` (или `MLFLOW_TRACKING_URI`). +- **Архитектура хранения (SQLite + HTTP-клиенты)**: `start_ui.sh` поднимает сервер с `--backend-store-uri sqlite:///${PWD}/mlflow.db`. Все скрипты в `src/` содержат `mlflow.set_tracking_uri(os.environ.get("MLFLOW_TRACKING_URI", "http://localhost:5555"))` — пишут на сервер. UI и скрипты работают с **одной** sqlite-базой → эксперименты сразу видны. **Сервер должен быть запущен до запуска скриптов** (иначе `ConnectionError` к localhost:5555). Переопределить адрес: `export MLFLOW_TRACKING_URI=http://:5555` (например, при логировании на удалённый сервер). - `mlruns/`, `artifacts/*`, `data/MNIST/`, `mlflow.db` — в `.gitignore`, в репозиторий не попадают. diff --git a/README.md b/README.md index 961cc48..b88992c 100644 --- a/README.md +++ b/README.md @@ -48,13 +48,19 @@ pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121 pip install flask # для serve_model.py ``` -### 2. Запуск MLflow UI +### 2. Запуск MLflow UI (Tracking Server) ```bash ./start_ui.sh # → http://10.0.0.7:5555 ``` +> Сервер использует SQLite-бэкенд (`mlflow.db`). Скрипты логируют на него +> по HTTP (`http://localhost:5555`), поэтому **сервер должен быть запущен +> и работать в отдельном терминале всё время, пока вы запускаете уроки**. +> Иначе скрипты упадут с `ConnectionError` к `localhost:5555`. +> Адрес можно переопределить: `export MLFLOW_TRACKING_URI=http://:5555`. + ### 3. Уроки по порядку ```bash diff --git a/src/autolog_demo.py b/src/autolog_demo.py index f22093a..049d0e5 100644 --- a/src/autolog_demo.py +++ b/src/autolog_demo.py @@ -21,6 +21,7 @@ import mlflow.sklearn # Подавляем INFO MLflow о переменных окружения (напр. OPENAI_API_KEY) при логировании модели import os os.environ.setdefault("MLFLOW_RECORD_ENV_VARS_IN_MODEL_LOGGING", "false") +mlflow.set_tracking_uri(os.environ.get("MLFLOW_TRACKING_URI", "http://localhost:5555")) def main(): diff --git a/src/compare_runs.py b/src/compare_runs.py index c1f398b..a495144 100644 --- a/src/compare_runs.py +++ b/src/compare_runs.py @@ -11,6 +11,9 @@ import argparse import mlflow from mlflow.tracking import MlflowClient +import os +mlflow.set_tracking_uri(os.environ.get("MLFLOW_TRACKING_URI", "http://localhost:5555")) + def main(): parser = argparse.ArgumentParser() diff --git a/src/grid_search_cv.py b/src/grid_search_cv.py index 546b77b..4dd63a9 100644 --- a/src/grid_search_cv.py +++ b/src/grid_search_cv.py @@ -18,6 +18,7 @@ import mlflow.sklearn # Подавляем INFO MLflow о переменных окружения (напр. OPENAI_API_KEY) при логировании модели import os os.environ.setdefault("MLFLOW_RECORD_ENV_VARS_IN_MODEL_LOGGING", "false") +mlflow.set_tracking_uri(os.environ.get("MLFLOW_TRACKING_URI", "http://localhost:5555")) def main(): diff --git a/src/hyperparam_sweep.py b/src/hyperparam_sweep.py index a50a188..619b5cc 100644 --- a/src/hyperparam_sweep.py +++ b/src/hyperparam_sweep.py @@ -21,6 +21,9 @@ from sklearn.model_selection import train_test_split import mlflow +import os +mlflow.set_tracking_uri(os.environ.get("MLFLOW_TRACKING_URI", "http://localhost:5555")) + def main(): parser = argparse.ArgumentParser(description="MLflow hyperparameter sweep") diff --git a/src/load_and_predict.py b/src/load_and_predict.py index b4b2dcb..022f6eb 100644 --- a/src/load_and_predict.py +++ b/src/load_and_predict.py @@ -12,6 +12,9 @@ from sklearn.datasets import load_digits import mlflow +import os +mlflow.set_tracking_uri(os.environ.get("MLFLOW_TRACKING_URI", "http://localhost:5555")) + def main(): # Загружаем модель из реестра по имени и стадии diff --git a/src/register_model.py b/src/register_model.py index 5550758..505eec0 100644 --- a/src/register_model.py +++ b/src/register_model.py @@ -15,6 +15,9 @@ import argparse import mlflow from mlflow.tracking import MlflowClient +import os +mlflow.set_tracking_uri(os.environ.get("MLFLOW_TRACKING_URI", "http://localhost:5555")) + def main(): parser = argparse.ArgumentParser(description="Register best model in MLflow") diff --git a/src/serve_model.py b/src/serve_model.py index 0c24c1a..39eb5b3 100644 --- a/src/serve_model.py +++ b/src/serve_model.py @@ -24,6 +24,9 @@ import numpy as np from sklearn.datasets import load_digits import mlflow + +import os +mlflow.set_tracking_uri(os.environ.get("MLFLOW_TRACKING_URI", "http://localhost:5555")) from flask import Flask, request, jsonify diff --git a/src/train_gpu.py b/src/train_gpu.py index b288c42..eab3c14 100644 --- a/src/train_gpu.py +++ b/src/train_gpu.py @@ -35,6 +35,7 @@ import mlflow.pytorch # Подавляем INFO MLflow о переменных окружения (напр. OPENAI_API_KEY) при логировании модели import os os.environ.setdefault("MLFLOW_RECORD_ENV_VARS_IN_MODEL_LOGGING", "false") +mlflow.set_tracking_uri(os.environ.get("MLFLOW_TRACKING_URI", "http://localhost:5555")) # ─── Модель: простая CNN ─── diff --git a/src/train_simple.py b/src/train_simple.py index cd516bc..4554d00 100644 --- a/src/train_simple.py +++ b/src/train_simple.py @@ -24,6 +24,7 @@ import mlflow.sklearn # Подавляем INFO MLflow о переменных окружения (напр. OPENAI_API_KEY) при логировании модели import os os.environ.setdefault("MLFLOW_RECORD_ENV_VARS_IN_MODEL_LOGGING", "false") +mlflow.set_tracking_uri(os.environ.get("MLFLOW_TRACKING_URI", "http://localhost:5555")) from mlflow.models.signature import infer_signature diff --git a/start_ui.sh b/start_ui.sh index 654afca..daec880 100755 --- a/start_ui.sh +++ b/start_ui.sh @@ -3,45 +3,38 @@ # Запуск MLflow Tracking Server # Откройте в браузере: http://localhost:5555 # -# Backend store = file://$(pwd)/mlruns (тот же каталог, куда скрипты -# пишут по умолчанию). Поэтому UI видит все эксперименты/запуски -# без доп. настройки tracking URI в скриптах. +# Backend store = SQLite (mlflow.db) — recommended MLflow backend. +# Скрипты в src/ логируют на этот сервер (http://localhost:5555) через +# mlflow.set_tracking_uri(...) — поэтому UI и скрипты работают с одной +# базой, и эксперименты сразу видны в UI. # -# ВАЖНО: file-store как СЕРВЕРНЫЙ бэкенд в новых MLflow (3.x) переведён в -# maintenance mode и по умолчанию запрещён. Официальный opt-out — -# MLFLOW_ALLOW_FILE_STORE=true (его и выставляем ниже). Это касается только -# `mlflow server`; клиентский file store (как пишут скрипты) работает и без него. +# ⚠️ file-store как tracking backend в новых MLflow (3.x) переведён в +# maintenance mode и запрещён (ошибка и на сервере, и у клиента-скрипта). +# Поэтому используем sqlite. Артефакты при этом лежат на локальном диске +# в ./artifacts (file-based artifact repository — он НЕ в maintenance mode). # -# Альтернатива — SQLite-бэкенд (recommended MLflow, без opt-out). Тогда -# раскомментируйте блок SQLITE ниже и закомментируйте FILE, а в скриптах -# задайте mlflow.set_tracking_uri("http://localhost:5555") -# (или export MLFLOW_TRACKING_URI=http://localhost:5555). +# Запустите сервер ДО запуска скриптов обучения: +# ./start_ui.sh # в одном терминале +# python src/train_simple.py ... # в другом (нужен работающий сервер) # ============================================================ set -e cd "$(dirname "$0")" -PORT=5555 +PORT="${MLFLOW_PORT:-5555}" echo "🚀 Запускаем MLflow Tracking Server..." -echo " UI: http://localhost:${PORT}" -echo " Store: file://${PWD}/mlruns" +echo " UI: http://localhost:${PORT}" +echo " Backend: sqlite:///${PWD}/mlflow.db" echo " Artifacts: file://${PWD}/artifacts" echo "" +echo " Скрипты логируют сюда через MLFLOW_TRACKING_URI=http://localhost:${PORT}" +echo " (можно переопределить: export MLFLOW_TRACKING_URI=http://:${PORT})" +echo "" echo " Нажмите Ctrl+C для остановки" echo "" -# ─── FILE backend (по умолчанию — единое хранилище со скриптами) ─── -# MLFLOW_ALLOW_FILE_STORE=true — opt-out из maintenance mode file-store бэкенда. -export MLFLOW_ALLOW_FILE_STORE=true mlflow server \ - --backend-store-uri "file://${PWD}/mlruns" \ + --backend-store-uri "sqlite:///${PWD}/mlflow.db" \ --default-artifact-root "file://${PWD}/artifacts" \ --host 0.0.0.0 \ --port "${PORT}" - -# ─── SQLITE backend (альтернатива — см. комментарий в шапке) ────── -# mlflow server \ -# --backend-store-uri sqlite:///mlflow.db \ -# --default-artifact-root ./artifacts \ -# --host 0.0.0.0 \ -# --port "${PORT}"