Здесь показаны различия между двумя версиями данной страницы.
| — |
truenas:application:llamacpp_moe_ssd [2026/11/12 18:15] (текущий) werwolf llama.cpp: MoE-модели с NVMe + RAM на TrueNAS (Custom App) |
||
|---|---|---|---|
| Строка 1: | Строка 1: | ||
| + | ====== llama.cpp: большие MoE-модели с NVMe + RAM на TrueNAS (Docker, Custom App) ====== | ||
| + | |||
| + | ===== Введение ===== | ||
| + | |||
| + | Инструкция описывает, как на сервере **TrueNAS** запустить большую MoE-модель (**Qwen3.8-Flash-Next**, 80–190 GB), которая **не помещается целиком в RAM+VRAM**: веса лежат на NVMe-накопителе, горячая часть держится в оперативной памяти и в кэше на видеокарте. Движок — **llama.cpp** (''llama-server'', OpenAI-совместимый API), запуск — Docker-приложение через веб-интерфейс TrueNAS. | ||
| + | |||
| + | Главное отличие от [[:truenas:application:strata_qwen_server|Strata на TrueNAS]]: llama.cpp **работает на текущем драйвере NVIDIA 550** (проверено на сервере), обновлять TrueNAS не нужно, и модель не «закрепляет» RAM — память, занятая под веса, отдаётся другим приложениям по требованию. | ||
| + | |||
| + | **Статус инструкции.** На сервере проверено: образ llama.cpp запускается и видит GPU на драйвере 550.142, поддерживает архитектуру модели (''qwen4exp'') и нужные ключи. Сама модель на сервере ещё не запускалась, скорость не замерялась — такие места помечены **(проверить)**. | ||
| + | |||
| + | ===== Почему это работает ===== | ||
| + | |||
| + | Qwen3.8-Flash-Next — модель Mixture-of-Experts (данные из заголовка GGUF): | ||
| + | |||
| + | ^ Параметр ^ Значение ^ | ||
| + | | Архитектура | ''qwen4exp'' | | ||
| + | | Слоёв | 48 | | ||
| + | | Экспертов в слое | **512**, из них на каждый токен работают только **10** | | ||
| + | | Контекст | до 262 144 токенов | | ||
| + | |||
| + | На каждый токен читается лишь малая часть весов (~2% экспертов). Поэтому: | ||
| + | * **общие веса** (внимание, эмбеддинги) и KV-кэш — на GPU; | ||
| + | * **эксперты** — в файле на NVMe, отображённом в память (''mmap''): Linux сам держит в RAM часто нужные страницы и дочитывает остальное с диска; | ||
| + | * **самые горячие эксперты** — дополнительно в GPU-кэше (''--moe-cache-mib''). | ||
| + | |||
| + | Чем больше модель превышает свободную RAM, тем чаще чтения с NVMe и тем медленнее генерация — но запускается и работает. | ||
| + | |||
| + | ===== Какие движки умеют NVMe + RAM и что подходит этому серверу ===== | ||
| + | |||
| + | ^ Движок ^ Как использует SSD ^ На этом сервере ^ | ||
| + | | **llama.cpp** (''llama-server'') | ''mmap'' весов + эксперты MoE в RAM (''--cpu-moe'') + GPU-кэш экспертов (''--moe-cache-mib''); режим Direct I/O (''-lm dio'') | **работает сейчас** (драйвер 550, проверено) — **эта инструкция** | | ||
| + | | **Strata** | свой движок, часть экспертов в RAM, остальное с диска; на ПК — самый быстрый вариант | только после обновления до TrueNAS 25.10.7+ (нужен драйвер 580), закрепляет RAM — [[:truenas:application:strata_qwen_server|отдельная инструкция]] | | ||
| + | | **Ollama** (уже стоит на сервере) | внутри llama.cpp, ''mmap'' | проще, но меньше контроля над выгрузкой экспертов; поддержка ''qwen4exp'' **(проверить)** | | ||
| + | | **SGLang «SSD Expert Pack»** | эксперты на NVMe, Direct I/O, GPU-кэш; для гигантских моделей (DeepSeek-V4-Flash, Kimi-K3) | **(проверить)** — вероятно, нужен более новый драйвер; см. блог LMSYS, 2026-08 | | ||
| + | |||
| + | ===== Сервер ===== | ||
| + | |||
| + | ^ Параметр ^ Значение ^ | ||
| + | | Система | TrueNAS SCALE 25.04.2.6, драйвер NVIDIA **550.142** | | ||
| + | | CPU | Intel Core i5-14500 (6 P-ядер + 8 E-ядер, 20 потоков) | | ||
| + | | RAM | 62 GB | | ||
| + | | GPU | RTX 2000 Ada, 16 GB | | ||
| + | | NVMe | пул ''appData'' — 2 × Samsung 990 PRO 4 TB, ''/mnt/appData'' | | ||
| + | |||
| + | Проверка образа на сервере: | ||
| + | |||
| + | <code> | ||
| + | $ docker run --rm --gpus all ghcr.io/ggml-org/llama.cpp:server-cuda --version | ||
| + | version: 0.9.0-dev (build 12316, commit 5d4c8eeb2) | ||
| + | $ docker run --rm --gpus all ghcr.io/ggml-org/llama.cpp:server-cuda --list-devices | ||
| + | Available devices: | ||
| + | CUDA0: NVIDIA RTX 2000 Ada Generation (16075 MiB, 4178 MiB free) | ||
| + | </code> | ||
| + | |||
| + | ''4178 MiB free'' — остальную видеопамять в этот момент держали другие приложения (см. «Шаг 1»). Образ (4.4 GB) уже скачан на сервер. | ||
| + | |||
| + | ===== Шаг 1: Освободить видеокарту ===== | ||
| + | |||
| + | На GPU сервера уже работают Ollama, Whisper, LibreTranslate, XTTS/VoxCPM — вместе ~12 из 16 GB. Для модели нужно хотя бы 8–10 GB свободных (общие веса + KV-кэш + кэш экспертов). На время работы остановите GPU-приложения: **Apps → Installed → (приложение) → Stop**. В первую очередь ''ollama'' — она держит загруженные модели в VRAM. | ||
| + | |||
| + | <code bash> | ||
| + | nvidia-smi --query-gpu=memory.used,memory.total --format=csv | ||
| + | </code> | ||
| + | |||
| + | Остальные приложения (Nextcloud, Jellyfin и т.д.) **можно не останавливать**: память под веса модели — это кэш страниц Linux, он вытесняется, когда памяти не хватает. Чем больше свободной RAM, тем быстрее генерация. | ||
| + | |||
| + | ===== Шаг 2: Датасет для моделей на NVMe ===== | ||
| + | |||
| + | **Datasets** → ''appData'' → **Add Dataset**: | ||
| + | * Name: ''llm-models'' | ||
| + | * Dataset Preset: **Generic** | ||
| + | * **Advanced Options**: | ||
| + | * Record Size: **1M** (большие файлы читаются крупными блоками) | ||
| + | * Compression: **OFF** (GGUF почти не сжимается) | ||
| + | * ATime: **Off** | ||
| + | * Save. | ||
| + | |||
| + | Путь: ''/mnt/appData/llm-models''. Места — с запасом под модели (на пуле ~3.3 TB свободно). | ||
| + | |||
| + | **Кэш ZFS (ARC) и mmap.** Файл модели, читаемый через ''mmap'', на ZFS кэшируется дважды: в ARC и в кэше страниц Linux — RAM тратится впустую. Рекомендуется для этого датасета отключить кэширование данных в ARC (останутся только метаданные) — в **System → Shell** под root (''sudo -i''): | ||
| + | |||
| + | <code bash> | ||
| + | zfs set primarycache=metadata appData/llm-models | ||
| + | zfs get primarycache,recordsize,compression appData/llm-models | ||
| + | </code> | ||
| + | |||
| + | **(проверить)** влияние на скорость: сравните ''tokens/s'' до и после (см. «Шаг 7»). Вернуть: ''zfs set primarycache=all appData/llm-models''. | ||
| + | |||
| + | ===== Шаг 3: Выбор модели ===== | ||
| + | |||
| + | Все варианты — Qwen3.8-Flash-Next в GGUF. «Свободно RAM» на сервере: ~30–38 GB при работающих приложениях, ~55 GB при остановленных. Что не поместилось в RAM — читается с NVMe. | ||
| + | |||
| + | ^ Вариант ^ Размер ^ Где взять ^ Ожидание на этом сервере **(проверить)** ^ | ||
| + | | ''UD-Q2_K_XL'' | 80 GB | ''unsloth/Qwen3.8-Flash-Next-GGUF'' | быстрее всех, качество ниже | | ||
| + | | **RVN ''IQ3_S''** | 83 GB | уже скачан на ПК: ''F:\projects\inst\qwen-iq3s'' (8 файлов) | **рекомендуется для начала** — ничего не качать, только скопировать | | ||
| + | | ''UD-IQ3_XXS'' / ''UD-Q3_K_XL'' | 84 / 90 GB | Unsloth | близко к IQ3_S | | ||
| + | | ''UD-IQ4_XS'' | 94 GB | Unsloth | 4 бита, чуть больше чтений с диска | | ||
| + | | ''UD-Q4_K_XL'' | 111 GB | Unsloth | **лучше качество**; заметная часть экспертов — с NVMe, медленнее | | ||
| + | | ''UD-Q5_K_XL'' … ''Q8_0'' | 158–188 GB | Unsloth | запустится, но в основном с диска — медленно; имеет смысл после добавления RAM | | ||
| + | |||
| + | **Примечание про RVN IQ3_S.** Этот же набор файлов не принял движок Strata (ему нужны служебные тензоры в BF16), а llama.cpp такие типы тензоров (IQ3_S, IQ4_NL) читает. Архитектура в файле — ''qwen4exp'', её поддерживает текущий llama.cpp. **(проверить запуск)** | ||
| + | |||
| + | ===== Шаг 4: Загрузить модель на сервер ===== | ||
| + | |||
| + | ==== Вариант А: скопировать RVN IQ3_S с ПК ==== | ||
| + | |||
| + | - **Shares → Windows (SMB) Shares → Add**: Path ''/mnt/appData/llm-models'', Name ''llm-models'', Save (TrueNAS предложит включить службу SMB — согласиться). Доступ — вашему пользователю. | ||
| + | - На ПК (PowerShell):<code powershell> | ||
| + | New-Item -ItemType Directory -Force \\TRUENAS\llm-models\qwen-rvn-iq3_s | Out-Null | ||
| + | robocopy F:\projects\inst\qwen-iq3s \\TRUENAS\llm-models\qwen-rvn-iq3_s *.gguf /J /NP | ||
| + | </code>83 GB по гигабитной сети — ~15 минут. | ||
| + | |||
| + | ==== Вариант Б: скачать с Hugging Face прямо на сервер ==== | ||
| + | |||
| + | В **System → Shell** (или SSH) — одноразовый контейнер, который скачивает выбранный квант и удаляется: | ||
| + | |||
| + | <code bash> | ||
| + | docker run --rm -v /mnt/appData/llm-models:/models python:3.12-slim sh -c \ | ||
| + | "pip install -q -U huggingface_hub hf_xet && \ | ||
| + | hf download unsloth/Qwen3.8-Flash-Next-GGUF --include '*UD-Q4_K_XL*' --local-dir /models/unsloth-ud-q4_k_xl" | ||
| + | ls -la /mnt/appData/llm-models/unsloth-ud-q4_k_xl/* | ||
| + | </code> | ||
| + | |||
| + | Для другого кванта поменяйте ''UD-Q4_K_XL'' в обоих местах. Загрузка возобновляется при повторном запуске. **(проверить точные имена папок в репозитории Unsloth)** | ||
| + | |||
| + | В YAML («Шаг 6») указывается **первый** файл набора (''...-00001-of-0000N.gguf''), остальные части llama.cpp подхватит сам. | ||
| + | |||
| + | ===== Шаг 5: API-ключ ===== | ||
| + | |||
| + | <code bash> | ||
| + | openssl rand -hex 24 | ||
| + | </code> | ||
| + | |||
| + | Ниже — ''<КЛЮЧ>''. Без ключа сервер будет открыт всей сети. | ||
| + | |||
| + | ===== Шаг 6: Custom App через веб-интерфейс ===== | ||
| + | |||
| + | **Apps → Discover Apps → ⋮ → Install via YAML**: | ||
| + | * Name: ''llama-qwen'' | ||
| + | * Custom Config: | ||
| + | |||
| + | <code yaml> | ||
| + | services: | ||
| + | llama-qwen: | ||
| + | image: ghcr.io/ggml-org/llama.cpp:server-cuda-b12316 | ||
| + | container_name: llama-qwen | ||
| + | restart: unless-stopped | ||
| + | ports: | ||
| + | - "30081:8080" | ||
| + | volumes: | ||
| + | - /mnt/appData/llm-models:/models:ro | ||
| + | command: | ||
| + | - -m | ||
| + | - /models/qwen-rvn-iq3_s/RVN-Qwen3.8-Flash-Next-IQ3_S-00001-of-00008.gguf | ||
| + | - --alias | ||
| + | - qwen3.8-flash-next | ||
| + | - --host | ||
| + | - 0.0.0.0 | ||
| + | - --port | ||
| + | - "8080" | ||
| + | - --api-key | ||
| + | - "<КЛЮЧ>" | ||
| + | - -c | ||
| + | - "65536" | ||
| + | - -ngl | ||
| + | - "99" | ||
| + | - --cpu-moe | ||
| + | - --moe-cache-mib | ||
| + | - "6144" | ||
| + | - -fa | ||
| + | - "on" | ||
| + | - -ctk | ||
| + | - q8_0 | ||
| + | - -ctv | ||
| + | - q8_0 | ||
| + | - -t | ||
| + | - "6" | ||
| + | - --jinja | ||
| + | deploy: | ||
| + | resources: | ||
| + | reservations: | ||
| + | devices: | ||
| + | - driver: nvidia | ||
| + | count: 1 | ||
| + | capabilities: [gpu] | ||
| + | </code> | ||
| + | |||
| + | Что делает каждая строка: | ||
| + | |||
| + | ^ Параметр ^ Смысл ^ | ||
| + | | ''server-cuda-b12316'' | закреплённая версия llama.cpp (сборка 12316, CUDA 12 — работает на драйвере 550). Обновление — сменить номер сборки | | ||
| + | | ''30081:8080'' | снаружи порт **30081** (8080/8081 заняты, 30080 зарезервирован под Strata) | | ||
| + | | ''/models:ro'' | датасет с моделями, только чтение | | ||
| + | | ''-m'' | первый файл набора GGUF | | ||
| + | | ''--alias'' | имя модели в API (''/v1/models'') — его указывают клиенты | | ||
| + | | ''-c 65536'' | контекст 64K (больше — больше VRAM под KV-кэш) | | ||
| + | | ''-ngl 99'' | все слои на GPU… | | ||
| + | | ''--cpu-moe'' | …**кроме экспертов**: они остаются в RAM/mmap (а значит, на NVMe) | | ||
| + | | ''--moe-cache-mib 6144'' | 6 GB видеопамяти — кэш самых используемых экспертов. Если VRAM мало — уменьшить; если свободно больше — увеличить | | ||
| + | | ''-fa on'', ''-ctk/-ctv q8_0'' | Flash Attention и KV-кэш в 8 бит — меньше VRAM под контекст | | ||
| + | | ''-t 6'' | потоки CPU: по числу P-ядер i5-14500. **(проверить)** ''-t 12'' и ''-t 14'' | | ||
| + | | ''--jinja'' | шаблон чата из модели (нужен для вызова инструментов агентами) | | ||
| + | | ''deploy … nvidia'' | доступ к GPU (как у ваших ''whisper'' и ''libretranslate'') | | ||
| + | |||
| + | Лимит памяти и ''memlock'' **не нужны**: в режиме ''mmap'' (по умолчанию) память под веса отдаётся системе по требованию. | ||
| + | |||
| + | ===== Шаг 7: Запуск и проверка ===== | ||
| + | |||
| + | **Apps → Installed → llama-qwen → Logs**. Нормальная загрузка выглядит так (суть строк): модель найдена, ''CUDA0 model buffer'' — часть на GPU, ''CPU_Mapped model buffer'' — эксперты отображены из файла, затем ''server is listening on http://0.0.0.0:8080''. Загрузка — от десятков секунд до нескольких минут. | ||
| + | |||
| + | На сервере: | ||
| + | |||
| + | <code bash> | ||
| + | curl -s http://127.0.0.1:30081/health | ||
| + | curl -s http://127.0.0.1:30081/v1/models -H "Authorization: Bearer <КЛЮЧ>" | ||
| + | </code> | ||
| + | |||
| + | С ПК (PowerShell), с замером скорости — в ответе поле ''timings.predicted_per_second'' (токенов/с генерации): | ||
| + | |||
| + | <code powershell> | ||
| + | '{"model":"qwen3.8-flash-next","messages":[{"role":"user","content":"Объясни кратко, чем list отличается от tuple в Python"}],"max_tokens":300}' | Out-File -Encoding utf8 req.json | ||
| + | curl.exe -s http://192.168.1.135:30081/v1/chat/completions -H "Authorization: Bearer <КЛЮЧ>" -H "Content-Type: application/json" --data-binary "@req.json" | ||
| + | </code> | ||
| + | |||
| + | **Первые запросы будут медленными** — эксперты ещё не в RAM и читаются с NVMe. После нескольких запросов «горячие» эксперты оседают в кэше, скорость растёт. Замеряйте 3–5 запросов подряд. | ||
| + | |||
| + | ===== Шаг 8: Подстройка скорости ===== | ||
| + | |||
| + | Меняйте по одному параметру (**Apps → llama-qwen → Edit**) и сравнивайте ''predicted_per_second'': | ||
| + | |||
| + | * **''--moe-cache-mib''** — больше кэша экспертов на GPU, если VRAM свободна (''nvidia-smi''). Обычно самый заметный эффект. | ||
| + | * **''--n-cpu-moe N''** вместо ''--cpu-moe'' — эксперты первых N слоёв на CPU, остальных — **целиком на GPU**. Например ''--n-cpu-moe 44'' — эксперты 4 последних слоёв в VRAM. Подбирать, пока хватает VRAM. | ||
| + | * **''-t''** — 6 / 12 / 14 потоков; E-ядра иногда замедляют. | ||
| + | * **''-c''** — меньше контекст → больше VRAM под эксперты. | ||
| + | * **Свободная RAM** — остановка тяжёлых приложений даёт больше места под кэш страниц → меньше чтений с NVMe. | ||
| + | * **''-lm dio''** (Direct I/O) — читать веса в обход кэша страниц. Обычно медленнее, имеет смысл только для экспериментов. | ||
| + | |||
| + | Активность диска: **Reports → Disk** (или ''zpool iostat -v appData 2'' под root) — при «прогретом» кэше чтений с NVMe во время генерации должно быть мало. | ||
| + | |||
| + | ===== Шаг 9: Подключение клиентов ===== | ||
| + | |||
| + | ^ Параметр ^ Значение ^ | ||
| + | | Тип API | OpenAI Compatible | | ||
| + | | Base URL | ''http://192.168.1.135:30081/v1'' | | ||
| + | | Chat Completions | ''http://192.168.1.135:30081/v1/chat/completions'' | | ||
| + | | API Key | ''<КЛЮЧ>'' | | ||
| + | | Модель | ''qwen3.8-flash-next'' (значение ''--alias'') | | ||
| + | |||
| + | ==== Goose Desktop ==== | ||
| + | |||
| + | Settings → Models → **Add Custom Provider**: Provider Type **OpenAI Compatible**, Display Name ''llama.cpp Server'', API URL ''http://192.168.1.135:30081/v1/chat/completions'', API Key ''<КЛЮЧ>'', Available Models ''qwen3.8-flash-next'', Streaming — вкл. Или файлом ''%APPDATA%\Block\goose\config\custom_providers\custom_llama_server.json'': | ||
| + | |||
| + | <code json> | ||
| + | { | ||
| + | "name": "custom_llama_server", | ||
| + | "engine": "openai", | ||
| + | "display_name": "llama.cpp Server", | ||
| + | "description": "llama.cpp on TrueNAS 192.168.1.135:30081", | ||
| + | "api_key_env": "CUSTOM_LLAMA_SERVER_API_KEY", | ||
| + | "base_url": "http://192.168.1.135:30081/v1/chat/completions", | ||
| + | "models": [ | ||
| + | { "name": "qwen3.8-flash-next", "context_limit": 65536 } | ||
| + | ], | ||
| + | "headers": null, | ||
| + | "timeout_seconds": 600, | ||
| + | "supports_streaming": true, | ||
| + | "requires_auth": true | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | Ключ — в Settings → Models у провайдера. Русский язык и MCP — как в [[:truenas:application:strata_qwen_goose|инструкции для ПК]]. | ||
| + | |||
| + | ==== Open WebUI (уже стоит на сервере) ==== | ||
| + | |||
| + | Admin Panel → Settings → Connections → OpenAI API → URL ''http://192.168.1.135:30081/v1'', Key ''<КЛЮЧ>''. | ||
| + | |||
| + | Остальные клиенты (OpenCode, Claude Code, Python) — как на странице [[:truenas:application:strata_qwen_server|Strata на TrueNAS]], с адресом ''http://192.168.1.135:30081/v1'' и моделью ''qwen3.8-flash-next''. | ||
| + | |||
| + | ===== Несколько моделей / смена модели ===== | ||
| + | |||
| + | * **Сменить модель** — **Edit** → поменять путь в ''-m'' (и при желании ''--alias'') → Save. | ||
| + | * **Не запускайте одновременно** ''llama-qwen'' и ''strata'' (после обновления TrueNAS) — они делят одну видеокарту. | ||
| + | * Удалить ненужную модель — удалить её папку в ''/mnt/appData/llm-models''. | ||
| + | |||
| + | ===== Безопасность ===== | ||
| + | |||
| + | * Порт 30081 открыт всей домашней сети — ключ обязателен. Не пробрасывайте порт на роутере в интернет; доступ извне — через VPN или ''nginx-proxy-manager'' с HTTPS. | ||
| + | |||
| + | ===== Проблемы и решения ===== | ||
| + | |||
| + | ==== unknown model architecture: 'qwen4exp' ==== | ||
| + | |||
| + | Слишком старая сборка llama.cpp. Используйте свежий тег ''server-cuda-bNNNNN'' (поддержка добавлена в 2026-08, PR #27742). | ||
| + | |||
| + | ==== Нет GPU в логах / no CUDA devices ==== | ||
| + | |||
| + | В YAML нет блока ''deploy … nvidia'' или видеокарту не видит Docker: на сервере ''docker run --rm --gpus all ghcr.io/ggml-org/llama.cpp:server-cuda-b12316 --list-devices''. | ||
| + | |||
| + | ==== CUDA out of memory при загрузке ==== | ||
| + | |||
| + | Видеокарту держат другие приложения («Шаг 1») — либо уменьшите ''--moe-cache-mib'' и ''-c''. | ||
| + | |||
| + | ==== Очень медленно (меньше 1–2 токенов/с) ==== | ||
| + | |||
| + | * Кэш ещё не прогрет — сделайте несколько запросов. | ||
| + | * Мало свободной RAM — остановите тяжёлые приложения; проверьте ''free -g'' (столбец ''available''). | ||
| + | * Модель слишком большая для RAM этого сервера — возьмите квант меньше. | ||
| + | * Попробуйте ''primarycache=metadata'' («Шаг 2»). | ||
| + | |||
| + | ==== Модель не найдена ==== | ||
| + | |||
| + | Путь в ''-m'' — **внутри контейнера** (''/models/...''), а не ''/mnt/appData/...''. Проверка: ''ls /mnt/appData/llm-models/<папка>''. | ||
| + | |||
| + | ===== Шпаргалка ===== | ||
| + | |||
| + | <code bash> | ||
| + | docker logs -f llama-qwen | ||
| + | nvidia-smi | ||
| + | free -g | ||
| + | curl -s http://192.168.1.135:30081/v1/models -H "Authorization: Bearer <КЛЮЧ>" | ||
| + | </code> | ||
| + | |||
| + | ^ Что ^ Значение ^ | ||
| + | | Адрес API | ''http://192.168.1.135:30081/v1'' | | ||
| + | | Модель (API) | ''qwen3.8-flash-next'' | | ||
| + | | Модели на диске | ''/mnt/appData/llm-models'' (NVMe) | | ||
| + | | Образ | ''ghcr.io/ggml-org/llama.cpp:server-cuda-b12316'' | | ||