Skip to content

Quick Start.ru

sikkatu edited this page Oct 23, 2026 · 24 revisions

Быстрый старт — 4× RTX PRO 6000

English | Русский | 中文

От чистой машины 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

0. Проверить машину

  • 4× NVIDIA RTX PRO 6000 (96 GB GDDR7), nvidia-smi видит все четыре
  • ≥ 1 TB RAM (холодные банки экспертов в памяти хоста), ≥ 2 TB NVMe под модель + контейнер
  • Debian 12, root или sudo

1. Установить Docker + NVIDIA Container Toolkit

# 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

2. Скачать рецепты

# в репо есть вложенный каталог recipes/ -- клонируйте в отдельную папку, чтобы путь был однозначным
git clone https://github.com/sikkatu/recipes.git ~/sikkatu-recipes
cd ~/sikkatu-recipes/recipes/rtx-pro-6000/mimo-v2.6-pro-hotsplit

Все дальнейшие команды выполняются из этой папки — без многократных угадываний cd.

3. Закрепить образ контейнера (обязательно)

Лаунчер отклоняет плавающие теги. Используйте закреплённую 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 или своём логе) и относитесь к тому запуску как к эксперименту.

4. Скачать модель

Выполняйте эти команды на 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 при запуске — сервер получит ровно те файлы, которые вы проверили.

Вариант A — скачать весь репозиторий модели в локальный каталог (рекомендуется)

# 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 это ваша собственная запись; проверяйте её, когда нужна воспроизводимость.

Вариант B — лаунчер сам скачает модель в Hugging Face cache

Лаунчер также умеет загружать по 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).

5. API-ключ (создаётся автоматически)

Ничего делать не нужно. На 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 — но для любого другого адреса ключ обязателен всегда.

6. Первый запуск — безопасный baseline (исправленный загрузчик модели + UVA)

Начните с неизменённого 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

7. Как читать скрипт проверки

  • 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): короткие запросы не дают обновлений, это нормально.

NUMA-закрепление на двухсокетных хостах

На голом двухсокетном хосте задайте карту явно перед запуском:

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.

Повседневные команды (scripts/manage.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_DYNAMIC

manage.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 молча теряет пары fused qkv_proj weight+weight_scale_inv. Не ставьте PATCH_MODEL_FILES=0, кроме как чтобы воспроизвести этот баг.

Полные процедуры измерений: README рецепта и FAQ.

Clone this wiki locally