Repository navigation
Quick Start.ru
От чистой машины Debian 12 до работающего endpoint. Сначала прочитайте три обязательных параметра — лаунчер без них не стартует, и это сделано намеренно.
Целевая ОС: Debian 12 (bookworm). Команды также работают на Ubuntu 22.04/24.04 с
apt.
| Параметр | Зачем | Как получить |
|---|---|---|
IMAGE |
Обязательно с sha256 digest — плавающие теги отклоняются | шаг 3 |
MODEL_REVISION |
Обязателен при загрузке модели по id (не из локальной папки) | commit SHA модели |
API_KEY |
На loopback генерируется автоматически и сохраняется в live/api_key; для внешнего bind — обязателен явный ключ |
не требуется на loopback |
- 4× NVIDIA RTX PRO 6000 (96 GB GDDR7),
nvidia-smiвидит все четыре - ≥ 1 TB RAM (холодные банки экспертов в памяти хоста), ≥ 2 TB NVMe под модель + контейнер
- Debian 12, root или sudo
# Docker (Debian 12)
sudo apt-get update && sudo apt-get install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/debian $(. /etc/os-release && echo $VERSION_CODENAME) stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update && sudo apt-get install -y docker-ce docker-ce-cli containerd.io
# NVIDIA Container Toolkit
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | \
sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -fsSL https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list > /dev/null
sudo apt-get update && sudo apt-get install -y nvidia-container-toolkit
sudo nvidia-ctk runtime configure --runtime=docker && sudo systemctl restart docker
# проверка: внутри контейнера должна напечататься информация о GPU
docker run --rm --gpus all nvidia/cuda:12.4.1-base-ubuntu22.04 nvidia-smi# в репо есть вложенный каталог recipes/ -- клонируйте в отдельную папку, чтобы путь был однозначным
git clone https://github.com/sikkatu/recipes.git ~/sikkatu-recipes
cd ~/sikkatu-recipes/recipes/rtx-pro-6000/mimo-v2.6-pro-hotsplitВсе дальнейшие команды выполняются из этой папки — без многократных угадываний cd.
Лаунчер отклоняет плавающие теги. Используйте закреплённую nightly-сборку, под которую написаны патчи (в ней есть файлы модели MiMo и ядра mxfp4 oracle, которые патчи хукают):
# конкретный образ — тег несёт коммит vLLM, digest фиксирует точные байты
export IMAGE="vllm/vllm-openai:nightly-8e2beaa3bff595577a069d49d96bde9074294ec2@sha256:167ccd8696bc09f1b63301185134e04b883623f8adcbd8e3a0ec3140a193abcf"
docker pull "$IMAGE"
echo "$IMAGE" # должен заканчиваться на @sha256:<64-hex> — заканчивается, digest уже встроен вышеПочему этот образ: патчи hotsplit заменяют mimo_v2.py / mimo_v2_omni.py внутри vLLM
и хукают ядра fused_moe/oracle/mxfp4 — они есть только в свежих nightly-сборках.
Стабильный релиз вроде v0.32.0-cu129-ubuntu2404 может не содержать файлы модели MiMo;
проверка совместимости в лаунчере ищет их и отказывает в запуске, если их нет.
ВНИМАНИЕ: этот образ ПРОВЕРЕН на целевом 4-GPU хосте (2026-10-22): веса загружаются, API отвечает,
генерация осмысленна. Digest выше — именно тот, что проверен. Если позже переключитесь на другую
сборку — запишите её digest (например, в заметке issue или своём логе) и относитесь к тому запуску как к эксперименту.
Выполняйте эти команды на Linux-хосте (машине с четырьмя GPU), а НЕ внутри docker-контейнера. Каталог модели — это путь хоста, который лаунчер затем монтирует только на чтение.
Два имени — две разные вещи:
| Имя | Что это | Пример |
|---|---|---|
MODEL_ID |
адрес модели на Hugging Face | XiaomiMiMo/MiMo-V2.6-Pro-MOPD |
MODEL_DIR |
локальный каталог на хосте со скачанными файлами | ~/models/MiMo-V2.6-Pro-MOPD |
MODEL_REVISION |
точный commit SHA версии модели — используется и при скачивании, и при запуске |
f3a9... (40 hex-символов) |
MODEL_REVISION — это версия. Получение SHA не скачивает модель.
Обратите внимание: с MODEL_DIR лаунчер монтирует каталог и не проверяет его содержимое
по SHA — MODEL_REVISION лишь фиксирует, какую версию вы скачали. Для воспроизводимости
локального запуска храните строку ревизии рядом с файлами и проверяйте целостность отдельно.
При использовании MODEL_ID лаунчер проверяет ревизию при загрузке модели. Само скачивание выполняется на следующем шаге.
Используйте один и тот же SHA при запуске — сервер получит ровно те файлы, которые вы проверили.
# 1. установить CLI Hugging Face в отдельное Python-окружение (не системное)
python3 -m venv "$HOME/.venvs/hf-download"
source "$HOME/.venvs/hf-download/bin/activate"
python -m pip install --upgrade huggingface_hub
# 2. модель и каталог для хранения
export MODEL_ID="XiaomiMiMo/MiMo-V2.6-Pro-MOPD"
export MODEL_DIR="$HOME/models/MiMo-V2.6-Pro-MOPD" # в домашней папке; при наличии большого диска укажите другой путь
# 3. создать каталог и разрешить текущему пользователю запись
mkdir -p "$MODEL_DIR" # sudo не нужен — это в вашей домашней папке
# 4. получить точный SHA текущей версии модели
export MODEL_REVISION="$(
curl -fsSL "https://huggingface.co/api/models/$MODEL_ID" |
python3 -c 'import json,sys; print(json.load(sys.stdin)["sha"])'
)"
if ! [[ "$MODEL_REVISION" =~ ^[0-9a-f]{40}$ ]]; then
echo "ERROR: не удалось получить корректный SHA ревизии модели" >&2
exit 1
fi
echo "Модель: $MODEL_ID"
echo "Ревизия: $MODEL_REVISION"
echo "Каталог: $MODEL_DIR"
# 5. проверить место на диске ДО скачивания (модели — сотни ГБ)
df -h "$(dirname "$MODEL_DIR")"
# 6. скачать весь репозиторий модели по этой точной ревизии
hf download "$MODEL_ID" \
--revision "$MODEL_REVISION" \
--local-dir "$MODEL_DIR"
# 7. проверить загрузку (конфиг, индекс весов, все шарды, токенизатор, ревизия)
test -s "$MODEL_DIR/config.json" || { echo "ERROR: нет config.json" >&2; exit 1; }
find "$MODEL_DIR" -maxdepth 1 -type f -name '*.safetensors' -print -quit | grep -q . \
|| { echo "ERROR: не найдены файлы safetensors" >&2; exit 1; }
# каждый шард из индекса весов должен существовать и быть непустым
if ls "$MODEL_DIR"/*.index.json >/dev/null 2>&1; then
python3 -c '
import json, sys, pathlib
idx = next(pathlib.Path(sys.argv[1]).glob("*.index.json"))
shards = sorted(set(json.load(open(idx))["weight_map"].values()))
missing = [s for s in shards if not (pathlib.Path(sys.argv[1]) / s).is_file()
or (pathlib.Path(sys.argv[1]) / s).stat().st_size == 0]
if missing:
print("ERROR: отсутствуют или пустые шарды:", missing, file=sys.stderr); sys.exit(1)
print(f"шарды в порядке: {len(shards)} файлов из {idx.name}")
' "$MODEL_DIR" || exit 1
fi
# файлы токенизатора (хотя бы один из стандартных)
for f in tokenizer.json tokenizer_config.json; do
test -s "$MODEL_DIR/$f" || { echo "ERROR: нет файла токенизатора $f" >&2; exit 1; }
done
# сохранить ревизию рядом с файлами — при MODEL_DIR лаунчер НЕ сверяет её при загрузке
echo "$MODEL_REVISION" > "$MODEL_DIR/REVISION.txt"
echo "Загрузка завершена:"
ls -lah "$MODEL_DIR"hf download без перечисления файлов скачивает весь репозиторий (веса, config.json,
токенизатор, индекс) — это надёжнее, чем выбирать файлы вручную и забыть один. Если модели нужна
авторизация — сначала выполните hf auth login. Публичным моделям вход не нужен.
Эти проверки подтверждают config.json, каждый шард из индекса весов (наличие и ненулевой
размер) и файлы токенизатора. Но они не сверяют байты: для побайтовой целостности
запустите sha256sum против хэшей, опубликованных автором модели (если они есть).
С MODEL_DIR лаунчер НЕ сверяет каталог с MODEL_REVISION при загрузке — сохранённый
REVISION.txt это ваша собственная запись; проверяйте её, когда нужна воспроизводимость.
Лаунчер также умеет загружать по MODEL_ID в стандартный HF-кэш (~/.cache/huggingface), но
тогда MODEL_REVISION (40 hex-символов) обязателен — лаунчер отклоняет имя ветки. Получите
SHA тем же способом (шаг 4 выше), экспортируйте MODEL_ID и MODEL_REVISION вместо MODEL_DIR
и запустите сервер. Скачанные файлы окажутся в
~/.cache/huggingface/hub/models--<org>--<name>/snapshots/<SHA>/ — именно этот каталог сервер и
загрузит. Обратите внимание: кэш не удаляется при откате (см. scripts/manage.sh clean-cache).
Ничего делать не нужно. На loopback-адресе (127.0.0.1) лаунчер сам сгенерирует ключ при
первом запуске и сохранит его в live/api_key (права 600, файл в .gitignore). Ключ
переиспользуется при перезапусках — вручную его создавать или экспортировать не нужно.
check-tp4-api.sh и manage.sh читают его оттуда автоматически.
Если сервер слушает внешний адрес (не loopback) — ключ обязателен, задайте его явно:
export API_KEY="$(openssl rand -hex 24)" # запишите себе — пригодится для API-запросовДля loopback-отладки без авторизации можно запустить с ALLOW_NO_AUTH=1 BIND=127.0.0.1 —
но для любого другого адреса ключ обязателен всегда.
Начните с неизменённого baseline и маленьким контекстом. CPU_OFFLOAD_GB обязан покрывать
весь банк экспертов на rank (MiMo ≈ 123 GiB) — иначе часть экспертов остаётся в VRAM, и
конвертация весов в формат Marlin-ядра падает с CUDA OOM. Обратите внимание: pinned-память
UVA = 4 × CPU_OFFLOAD_GB (для MiMo это 512 GiB) — выделите виртуальной машине ≥ 800 ГБ
оперативной памяти (пин-память не выгружается в swap). Лаунчер проверит доступную RAM и
предупредит, если бюджета не хватает.
export IMAGE="vllm/vllm-openai:nightly-8e2beaa3bff595577a069d49d96bde9074294ec2@sha256:167ccd8696bc09f1b63301185134e04b883623f8adcbd8e3a0ec3140a193abcf" # из шага 3
export MAX_MODEL_LEN=16384
export CPU_OFFLOAD_GB=128 GPU_UTIL=0.85 # весь банк экспертов (~123 GiB/rank) уходит в UVA
bash scripts/run-tp4-uva.sh # API-ключ создастся сам и сохранится в live/api_key
bash scripts/check-tp4-api.sh # ждёт /v1/models, шлёт один chat-запрос (ключ берёт сам)Только когда baseline здоров, пробуйте hotsplit (проверен от начала до конца на целевом хосте 2026-10-23: все 69 слоёв разбиты на 4 rank, smoke-тест выдаёт осмысленный ответ; перед продом проведите более длинные проверки качества; полный Marlin-запуск падает с OOM при репаковке, см. «Известные ограничения»):
# сначала остановить UVA baseline — оба используют --network host и один порт
docker stop mimo-pro-tp4-uva && docker rm mimo-pro-tp4-uva
export MODEL_DIR="$HOME/models/MiMo-V2.6-Pro-MOPD"
export IMAGE="vllm/vllm-openai:nightly-8e2beaa3bff595577a069d49d96bde9074294ec2@sha256:167ccd8696bc09f1b63301185134e04b883623f8adcbd8e3a0ec3140a193abcf"
export MAX_MODEL_LEN=16384 CPU_OFFLOAD_GB=128 GPU_UTIL=0.85
export HOTSPLIT_DYNAMIC=0 # сначала static
export HOTSPLIT_HOT_GIB=30 # НА RANK. Обязательно: при полной UVA-выгрузке бюджет по умолчанию равен 0 (пустой hot-набор)
bash scripts/run-tp4-hotsplit.sh
bash scripts/check-hotsplit-tp4.sh mimo-pro-tp4-hotsplit staticЗатем dynamic-режим (проверен на целевом железе 2026-10-23, отдельный запуск):
docker stop mimo-pro-tp4-hotsplit && docker rm mimo-pro-tp4-hotsplit
export MODEL_DIR="$HOME/models/MiMo-V2.6-Pro-MOPD"
export IMAGE="vllm/vllm-openai:nightly-8e2beaa3bff595577a069d49d96bde9074294ec2@sha256:167ccd8696bc09f1b63301185134e04b883623f8adcbd8e3a0ec3140a193abcf"
export MAX_MODEL_LEN=16384 CPU_OFFLOAD_GB=128 GPU_UTIL=0.85
export HOTSPLIT_DYNAMIC=1 HOTSPLIT_HOT_GIB=30
bash scripts/run-tp4-hotsplit.sh
bash scripts/check-hotsplit-tp4.sh mimo-pro-tp4-hotsplit dynamic expect-progress-
check-hotsplit-tp4.sh <container> static— ожидаетсяdynamic_hook=INACTIVEна всех rank; отсутствиеhotsplit doneна ранге — FAIL. -
check-hotsplit-tp4.sh <container> dynamic— ожидаетсяdynamic_hook=ACTIVEна каждом rank. После реального трафика добавьтеexpect-progress(3-й аргумент илиEXPECT_PROGRESS=1): отсутствие прогресса станет FAIL с ненулевым кодом, а не молчаливым WARN. rank. После реального трафика счётчикhotsplit dynamic updateдолжен быть ненулевым; до генерации нулевой счётчик нормален — он ничего не доказывает. Окно планировщика — 2000 шагов движка (HOTSPLIT_DYN_STEPS): короткие запросы не дают обновлений, это нормально.
На голом двухсокетном хосте задайте карту явно перед запуском:
export HOTSPLIT_NUMA_MAP="0:0,1:0,2:1,3:1" # gpu:node пары — проверьте топологию numactl --hardwareВ VM (Proxmox и т.п.) ситуация другая: nvidia-smi topo -m обычно показывает все GPU через
PHB с GPU NUMA ID: N/A — VM не сообщает, к какому сокету физически подключена карта.
Не угадывайте карту: неверная карта закрепит worker не на том сокете и ухудшит работу.
Если не задавать её — это правильно и безопасно: numa_bind пропустит закрепление,
чтение холодных экспертов работает так же. Физическую раскладку смотрите через
numactl --hardware и lstopo на хосте (Proxmox), не внутри VM.
bash scripts/load-test.sh 5 # 5 раундов × 10 тем, ток/с по темам + динамика hit-rate
bash scripts/quality-compare.sh # 5 фиксированных запросов; по разу на каждый сервер и diff
bash scripts/check-prefix-cache.sh # доля попаданий в кэш + ток/с (счётчики /metrics или лог движка)-
1M токенов: модель поддерживает
max_position_embeddings=1048576. ЗадайтеMAX_MODEL_LEN=1048576,KV_CACHE_DTYPE=bf16(KV ~13.5 GiB/GPU — влезает в бюджет, полная точность; fp8 ~6.8 GiB — только если нужен запас) иMAX_NUM_BATCHED_TOKENS=8192(длинный prefill). -
Prefix caching включён по умолчанию — повторяющиеся префиксы (системный промпт,
история диалога) берутся из KV-кэша, как «дешёвые кэшированные токены» у облачных
провайдеров. Проверка:
bash scripts/check-prefix-cache.sh.
После первого успешного запуска не нужно помнить docker-флаги и переменные окружения:
scripts/manage.sh status # контейнеры, GPU, доступность API — одним экраном
scripts/manage.sh logs # следить за логом сервера
scripts/manage.sh stop # остановить и удалить контейнер
scripts/manage.sh start # запустить снова (проверенные умолчания: HOTSPLIT_DYNAMIC=0, HOTSPLIT_HOT_GIB=30; переопределяется через env или tp4.env)
scripts/manage.sh restart # stop + start, подхватывает изменённый tp4.env
scripts/manage.sh update # git pull + перезапуск
scripts/manage.sh rollback # откат на уровне контейнера (удаляет только контейнер)
scripts/manage.sh clean-cache # показать размер HF-кэша, удалить только после явного подтверждения
scripts/manage.sh check # проверка hotsplit, режим следует HOTSPLIT_DYNAMICmanage.sh — тонкая обёртка над лаунчерами рецепта: подхватывает ./tp4.env (скопируйте
tp4.env.example), а уже экспортированные переменные окружения имеют приоритет над файлом.
-
Hot-бюджет (
HOTSPLIT_HOT_GIB) — на rank. Пустое значение = «использовать объём GPU-резидентных байт экспертов, который использовала стоковая расстановка», не «весь банк» — весь банк на одну карту не помещается. Арифметика — в Модели и геометрия. -
HOTSPLIT_VRAM_RESERVE_GIB=2— это пол, а не обещание. Длинные контексты и конкурентность требуют больше — измеряйте процедурами из раздела «Проверка» README рецепта. - Модельный патч (
PATCH_MODEL_FILES=1) проверяется поpatches/MANIFEST.sha256до запуска — не правьте файлы патчей на месте. -
PATCH_MODEL_FILES=1— значение по умолчанию, и оно критично. A/B на целевом железе (2026-10-22, одинаковые веса и флаги): с собственнымmimo_v2.pyиз образа (PATCH_MODEL_FILES=0) модель выдаёт фрагменты обучающего корпуса; с пропатченным загрузчиком — осмысленный текст. Стоковыйload_weightsмолча теряет пары fusedqkv_projweight+weight_scale_inv. Не ставьтеPATCH_MODEL_FILES=0, кроме как чтобы воспроизвести этот баг.
Полные процедуры измерений: README рецепта и FAQ.
English
Русский
中文