Перейти к содержанию
Публикация AiManual

Self-Hosting LLM в 2026: выбор модели, GPU и безопасный запуск vLLM

Как выбрать модель и GPU для своей LLM, проверить локальный запуск vLLM, защитить API и оценить полную стоимость без обещаний окупаемости.

Коротко

Что будет в материале

  1. 01

    Когда имеет смысл свой инференс-сервер

  2. 02

    Модель: не гонитесь за размером, гонитесь за эффективностью

  3. 03

    Железо: облако против своего сервера, или как не прогореть

  4. 04

    Квантование: экономия памяти с проверкой качества

Когда имеет смысл свой инференс-сервер

Self-hosting LLM означает, что вы сами разворачиваете модель и отвечаете за её работу: вычисления, обновления, доступ и сохранность данных. Это полезно, когда нужны определённые модели, собственные настройки или управление тем, где обрабатываются запросы. Сам по себе большой счёт за API ещё не доказывает, что свой сервер окажется дешевле.

Ниже — план выбора модели и оборудования, ограниченный локальный smoke-test vLLM и список проверок перед продакшеном. Smoke-test проверяет запуск и один ответ API. Он не подтверждает качество модели, производительность или отказоустойчивость; результатов запуска на конкретном GPU здесь нет.

Облачная аренда тоже предполагает участие третьей стороны. Сначала определите допустимые данные, сетевые границы, доступ операторов и правила хранения. Начинайте с искусственных запросов без персональных данных и секретов.

Модель: не гонитесь за размером, гонитесь за эффективностью

Сравнивайте модели по качеству на своей задаче, задержке ответа и расходу памяти. Размер модели и место в публичном рейтинге не заменяют проверку на ваших типичных запросах. Также проверьте лицензию, официальный model ID и поддержку архитектуры выбранным сервером.

1 Определите задачу точнее «нужна умная модель»

Четко сформулируйте, что должна делать модель. Генерировать код? Понимать длинные документы? Вести диалог? От этого зависит выбор архитектуры.

  • Код и рассуждение: подготовьте примеры из нужного языка и домена с проверяемым результатом. Оценивайте корректность, а не только сходство ответа с образцом; сгенерированный код запускайте в изолированной среде.
  • Диалог: проверяйте язык, следование инструкциям, формат ответа и работу с историей. Для проверки самой инфраструктуры подойдёт небольшая реальная модель Qwen/Qwen2.5-0.5B-Instruct, используемая ниже. Это не рекомендация её качества для вашего продукта.
  • Документы и RAG: оценивайте поиск релевантных фрагментов, точность ссылок и ответы при отсутствии нужных сведений. Максимальный контекст в model card не означает, что серверу хватит памяти при вашей параллельной нагрузке.
💡
Не берите самую большую модель «на вырост». Сначала проверьте качество небольшой модели, затем память при нужной длине контекста и числе одновременных запросов. Квантование может уменьшить память весов, но скорость и изменение качества нужно измерять.

Если сомневаетесь, запустите быстрый A/B тест на Ollama. Сравните варианты на одном наборе разрешённых к обработке примеров. Результат локального теста не переносится автоматически на другую квантованную сборку, сервер или GPU.

Железо: облако против своего сервера, или как не прогореть

Самый болезненный вопрос. Арендовать GPU в облаке или купить железо? Ответ – смотрите на горизонт планирования и график нагрузки.

СценарийЧто сравниватьПроверка до выбора
ЭкспериментыДоступная локальная машина и краткосрочная арендаСовместимость модели и runtime, свободная VRAM/RAM, место под веса и зависимости.
Сервис с меняющейся нагрузкойАренда одного или нескольких ускорителейЗадержка и очередь на целевом профиле запросов, время загрузки, масштабирование и резерв.
Предсказуемая длительная нагрузкаДолгосрочная аренда и собственный серверПолная стоимость владения: оборудование, простой, электричество, охлаждение, ремонт и сопровождение.

При сравнении предложений, в том числе Vultr (партнёрская ссылка), проверяйте точную модель ускорителя, объём памяти, правила остановки инстанса, хранение данных и итоговую смету. Наличие нужной конфигурации и условия аренды уточняйте перед заказом.

В этот же список для сравнения можно включить Timeweb Cloud (партнёрская ссылка): у провайдера есть раздел серверов с GPU. Сверяйте конкретный ускоритель, доступную VRAM, совместимость окружения и условия обработки данных с требованиями выбранной модели. Этот пример не подтверждает наличие подходящего сервера или его производительность.

Начинайте с конфигурации, которая совместима с runtime и проходит минимальные требования модели. До закупки или долгой аренды измерьте память, задержку и скорость на целевой нагрузке. Самый дешёвый час аренды может дать высокую стоимость полезного результата.

Квантование: экономия памяти с проверкой качества

Квантование уменьшает точность представления части чисел в модели и может снизить расход памяти. Оно не гарантирует ускорение или незаметное изменение качества: результат зависит от модели, формата, вычислительных ядер и оборудования. Поддержку конкретной реализации сверяйте с таблицей совместимости vLLM.

2 Как выбрать готовую сборку

  • AWQ/GPTQ и другие форматы: проверяйте поддержку именно вашего GPU и версии runtime. Название формата само по себе не обещает максимальную скорость.
  • GGUF: распространён в экосистеме llama.cpp, которая поддерживает CPU, GPU и смешанное выполнение. Выбор backend и распределения слоёв влияет на результат.
  • Источник: проверяйте автора, исходную модель, лицензию, revision и описание преобразования. Совпадающее имя не делает стороннюю сборку официальной.

Для первого smoke-test ниже квантование не нужно. Загрузчик получает модель из существующего репозитория вместе с конфигурацией и токенизатором. Двух произвольных файлов весов и config.json недостаточно для общего рецепта; при отдельной загрузке используйте документацию Hugging Face о снимках репозитория.

Подробнее про инструменты читайте в нашем сравнении LM Studio и llama.cpp.

Развёртывание: сначала один локальный ответ

Для серверного инференса рассмотрим vLLM. Его OpenAI-совместимый интерфейс позволяет использовать привычные HTTP-клиенты, но поддержку нужных endpoints, параметров, streaming и tool calling проверяют отдельно. Смена URL не гарантирует полной совместимости приложения; см. справочник API.

3 Ограниченный smoke-test vLLM

Условия примера: Linux, выделенный для теста совместимый NVIDIA GPU, рабочий драйвер, Python 3.12 и установленный uv. До установки сверьте требования текущего vLLM: для NVIDIA в текущей документации указана compute capability не ниже 7.5; совместимость драйвера и доступной сборки проверяется отдельно. Нужны доступ к Hugging Face, место под зависимости и веса, свободная RAM/VRAM. Для CPU, AMD или macOS используйте отдельную инструкцию установки.

В новой папке создайте отдельное окружение. Это начальная установка текущего выпуска, а не заранее протестированный lockfile. После успешного теста зафиксируйте версии зависимостей, драйвер, модель и её commit revision.

uv venv --python 3.12 --seed
source .venv/bin/activate
uv pip install vllm --torch-backend=auto
uv pip freeze > requirements-smoke.txt
vllm --version

Сохраните следующий код как smoke_vllm.py и выполните python smoke_vllm.py в этом окружении. Он скачивает модель при первом запуске, открывает HTTP только на 127.0.0.1:8000, ждёт до 600 секунд, проверяет отказ без ключа и отправляет один искусственный запрос с пределом 32 выходных токена. После проверки или ошибки тест завершает запущенный сервер.

import json
import os
import secrets
import signal
import socket
import subprocess
import tempfile
import time
from importlib.metadata import version
from urllib.error import HTTPError, URLError
from urllib.request import Request, ProxyHandler, build_opener

MODEL = "Qwen/Qwen2.5-0.5B-Instruct"
HOST, PORT = "127.0.0.1", 8000
BASE = f"http://{HOST}:{PORT}"
# Не подключаем тест к уже работающему сервису.
with socket.socket() as probe:
    probe.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    probe.bind((HOST, PORT))

key = secrets.token_urlsafe(32)
env = os.environ.copy()
env.update(VLLM_API_KEY=key, VLLM_NO_USAGE_STATS="1",
           VLLM_LOGGING_LEVEL="INFO")
client = build_opener(ProxyHandler({}))  # Loopback без HTTP-прокси.

def request(path, payload=None, authenticated=True, timeout=5):
    headers = {"Content-Type": "application/json"}
    if authenticated:
        headers["Authorization"] = "Bearer " + key
    body = None if payload is None else json.dumps(payload).encode()
    with client.open(Request(BASE + path, data=body, headers=headers),
                     timeout=timeout) as response:
        return json.load(response)

cmd = ["vllm", "serve", MODEL, "--host", HOST, "--port", str(PORT),
       "--dtype", "half", "--max-model-len", "2048",
       "--max-num-seqs", "1", "--gpu-memory-utilization", "0.7",
       "--enforce-eager", "--generation-config", "vllm",
       "--no-enable-log-requests", "--no-enable-log-outputs"]
# Приватный временный файл для диагностики старта, без вывода ключа.
with tempfile.NamedTemporaryFile(mode="w", prefix="vllm-smoke-",
                                 suffix=".log", delete=False) as log:
    print("vLLM:", version("vllm"), "Log:", log.name)
    proc = subprocess.Popen(cmd, env=env, stdout=log,
                            stderr=subprocess.STDOUT, start_new_session=True)
    try:
        deadline = time.monotonic() + 600
        while True:
            if proc.poll() is not None:
                raise RuntimeError("vLLM завершился; проверьте log")
            try:
                models = request("/v1/models", timeout=2)
                if MODEL in [m["id"] for m in models["data"]]:
                    break
            except (URLError, TimeoutError):
                pass
            if time.monotonic() >= deadline:
                raise TimeoutError("Сервер не готов за 600 секунд")
            time.sleep(2)

        try:
            request("/v1/models", authenticated=False)
        except HTTPError as exc:
            if exc.code != 401:
                raise
        else:
            raise RuntimeError("API не отклонил запрос без ключа")

        result = request("/v1/chat/completions", {
            "model": MODEL,
            "messages": [{"role": "user", "content": "Say hello."}],
            "max_tokens": 32, "temperature": 0, "n": 1
        }, timeout=60)
        content = result["choices"][0]["message"]["content"]
        if not isinstance(content, str) or not content.strip():
            raise RuntimeError("Получен пустой ответ")
        print("OK: модель доступна, ключ проверяется, генерация отвечает")
    finally:
        # Завершаем только созданную этим тестом группу процессов.
        try:
            os.killpg(proc.pid, signal.SIGTERM)
        except ProcessLookupError:
            pass
        try:
            proc.wait(timeout=30)
        except subprocess.TimeoutExpired:
            os.killpg(proc.pid, signal.SIGKILL)
            proc.wait(timeout=5)

Успех означает только три проверки, перечисленные в строке OK. Пустой ответ, ошибка CUDA, неизвестный параметр или таймаут требуют разбирательства по журналу и документации установленной версии. Медленное скачивание может не уложиться в ожидание. Ни память, ни время запуска этим примером не гарантированы.

--max-model-len 2048 ограничивает суммарный контекст запроса и ответа; --max-num-seqs 1 ограничивает одновременно обрабатываемые последовательности. --enforce-eager отключает CUDA graphs для простого теста, поэтому его скорость не представляет оптимизированный продакшен. Доля памяти 0.7 — настройка теста, а не универсальная рекомендация; см. управление памятью.

💡
Явный --host 127.0.0.1 устраняет предположение, что сервер без настройки слушает только localhost. Это ограничение HTTP-порта не заменяет firewall для внутренних портов и зависимостей. Не открывайте порт 8000 в интернет для этого теста.

4 Настройка для реального мира

Голый сервер – это полдела. Нужно добавить обвязку:

  1. Доступ и ограничения: держите backend в закрытой сети. На reverse proxy разрешайте только необходимые маршруты и методы, добавляйте HTTPS, аутентификацию пользователей, квоты, лимит тела запроса, длины генерации и очереди. Документация безопасности vLLM предупреждает: встроенный API-ключ защищает не все endpoints. Он не заменяет сетевую изоляцию и авторизацию.
  2. Логирование и мониторинг: измеряйте задержку до первого токена, полное время ответа, очередь, ошибки, использование памяти и производительность на одном профиле нагрузки. Метрики vLLM можно собирать отдельно от содержимого запросов. Не записывайте по умолчанию промпты, ответы, заголовки Authorization и персональные данные; задайте доступ и срок хранения журналов по рекомендациям OWASP. Проверяйте также proxy, трассировку и библиотеки.
  3. Границы доверия: не включайте исполнение стороннего model code и динамическую загрузку дополнений без проверки. Если приложению нужен поиск в интернете, используйте ограниченный внешний компонент, как обсуждается в гайде про доступ в интернет для LLM. Ответ модели не должен автоматически давать доступ к файлам, внутренней сети или секретам.
  4. Эксплуатация: зафиксируйте версии окружения и commit модели, настройте проверки готовности, восстановление и откат. Контейнер помогает воспроизводить зависимости, но не обеспечивает безопасность или высокую доступность сам по себе.

В примере отключён сбор usage statistics средствами VLLM_NO_USAGE_STATS=1; описание механизма есть в документации vLLM. Это не режим полного офлайна и не контроль всех исходящих соединений. Загрузка весов, зависимости, интеграции и инфраструктурный мониторинг требуют отдельной проверки.

Типичные грабли, на которые наступают все

  • Считать только веса: к ним добавляются KV-cache для контекста, рабочие буферы и ресурсы runtime. Универсального запаса «ещё 2–4 GB» или «30%» нет. Проверяйте реальные лимиты контекста и параллельности.
  • Не учитывать питание и охлаждение: для собственного сервера проверьте требования производителя и возможности площадки, включая длительную нагрузку и резерв.
  • Выбирать RAM по названию поколения: важны доступный объём, загрузка модели, CPU и путь данных. Требование «только DDR5» без привязки к конфигурации необоснованно.
  • Считать smoke-test нагрузочным тестом: после одного ответа испытайте типичную и пиковую нагрузку, длины входа/выхода, streaming, отмену запросов и поведение при переполнении очереди. Сравнивайте качество и задержку одновременно.

Как сравнить стоимость с внешним API

Сначала задайте одинаковый объём полезной работы и требуемое качество. Для внешнего API отдельно считайте входные и выходные токены, повторные запросы и дополнительные функции по действующим условиям выбранного сервиса. Для своего сервера учитывайте вычисления, часы простоя, диски, трафик, мониторинг, сопровождение и резервирование.

Рабочая формула: стоимость принятого результата = все затраты за период / число результатов, прошедших проверку качества. Для потокового чата дополнительно сравнивайте задержку и доступность: дешёвая генерация не помогает, если пользователь не дожидается ответа.

Пример бюджета заполняйте своими измерениями и предложениями провайдеров. Нельзя вывести окупаемость из числа запросов без их длины, режима нагрузки и стоимости работы команды. Переход на собственную модель даёт больше настроек, но также добавляет ответственность за эксплуатацию; фиксированного срока окупаемости нет.

FAQ: короткие ответы на больные вопросы

ВопросОтвет
Какая модель лучше для старта?Для проверки установки в этом примере используется Qwen/Qwen2.5-0.5B-Instruct. Модель для продукта выбирайте по качеству на своих задачах, лицензии и измеренному расходу ресурсов; универсально лучшей нет.
Что проще развернуть: vLLM или Ollama?Ollama можно рассмотреть для локальных проб, vLLM — для серверного инференса. Выбор зависит от платформы, поддержки модели и нужного API; сам инструмент не делает сервис готовым к продакшену.
Можно ли использовать CPU вместо GPU?Да, например через llama.cpp с поддерживаемой GGUF-моделью. Пригодность зависит от модели, RAM, длины контекста и допустимой задержки. Универсального множителя замедления относительно GPU нет.
Как обновлять модель на проде?Проверьте новую revision на тех же задачах и нагрузке, предусмотрите откат. Blue-green или canary возможны при наличии ресурсов и маршрутизации; без такого резерва планируйте согласованное окно обслуживания.

Начните с совместимого тестового окружения и одного локального ответа. Затем проверьте качество, память и задержку на своей нагрузке, составьте смету и план эксплуатации. Решение о продакшене принимайте по этим результатам.

Подписаться на канал