Заметки

Qdrant: разворачиваем векторную базу на своём сервере

M
Markabus
·27 августа 2026 г.
Открытый серверный блок на тёмном рабочем столе: материнская плата, планки памяти и NVMe-накопитель крупным планом, за ним монитор с тёмной панелью управления и синим облаком точек — визуализацией векторов.

Когда вы собираете поиск по своим документам или RAG-систему, рано или поздно упираетесь в вопрос: где хранить векторы. Держать их в памяти Python-процесса можно ровно до первого перезапуска, а отдавать корпоративную базу знаний в облако хочется не всегда. Qdrant — векторная база с открытым кодом (Apache 2.0), которая ставится одной командой Docker и спокойно живёт на обычном VPS. Разберём весь путь: от выбора машины до боевой конфигурации с ключом, фильтрами, квантизацией и бэкапами.

Что такое Qdrant и когда он нужен

Qdrant написан на Rust и решает одну задачу: хранить векторы (числовые представления текста, картинок, аудио) с произвольными метаданными и быстро находить среди них ближайшие к запросу. «Ближайшие» — значит похожие по смыслу: на этом строятся семантический поиск, рекомендации и подстановка контекста в промпт.

Критерий простой. Пара тысяч фрагментов и один процесс, который их читает, — хватит перебора в numpy. Десятки тысяч фрагментов, несколько сервисов, фильтры по метаданным (только документы этого клиента, только за последний год) и требование пережить деплой — пора ставить отдельную базу.

Qdrant в этом ряду выделяется тем, что его удобно держать у себя: один бинарник без внешних зависимостей — ни отдельного кластера, ни JVM, ни управляющей базы. Плюс встроенная веб-панель, где видно коллекции и можно руками выполнить запрос.

Сколько ресурсов заложить

Основной расход — память под векторы: число векторов × размерность × 4 байта (тип float32 по умолчанию). Сверху индекс HNSW, который связывает векторы в граф: примерно m × 2 × 4 байта × 1,2 на вектор, где m — число связей, по умолчанию 16, то есть около 154 байт.

Пример: миллион фрагментов, эмбеддинги размерностью 1536 (типичный размер у моделей OpenAI и Gemini) — это ≈ 6,1 ГБ на векторы плюс ≈ 0,15 ГБ на индекс. Документация советует закладывать ещё около 20% на кеш файловой системы, журнал записи и временные сегменты. Итого машина с 8 ГБ памяти — и это без запаса на всё остальное.

Пугает это только на первый взгляд: миллион фрагментов — очень много, база знаний среднего бизнеса редко переваливает за сотню тысяч. К тому же есть два рычага: квантизация (о ней ниже) и «холодный» режим, когда векторы лежат на диске в memory-mapped файлах. Для старта хватает VPS с 2 vCPU и 4 ГБ памяти; диск — обязательно SSD или NVMe.

Запуск в Docker

Самый короткий путь — официальный образ:

docker pull qdrant/qdrant

docker run -p 6333:6333 -p 6334:6334 \
    -v "$(pwd)/qdrant_storage:/qdrant/storage:z" \
    qdrant/qdrant

Порт 6333 — REST API и веб-панель, 6334 — gRPC (быстрее на больших пакетах, клиентские библиотеки умеют оба). Ключевая строка — том: без -v данные исчезнут вместе с контейнером, каталог qdrant_storage и есть ваша база. После запуска откройте http://localhost:6333/dashboard — там список коллекций и консоль запросов.

Docker Compose и автозапуск

На сервере одноразовая команда не годится — нужен файл, который переживёт перезагрузку:

services:
  qdrant:
    image: qdrant/qdrant:v1.18.2
    restart: unless-stopped
    ports:
      - "127.0.0.1:6333:6333"
      - "127.0.0.1:6334:6334"
    volumes:
      - ./qdrant_storage:/qdrant/storage
    environment:
      QDRANT__SERVICE__API_KEY: ${QDRANT_API_KEY}

Два принципиальных отличия от команды выше. Версия зафиксирована явно (актуальна 1.18.2, июнь 2026) — тег latest на проде однажды подложит мажорное обновление в неподходящий момент. И порты привязаны к 127.0.0.1: база доступна только с самого сервера. Если приложение живёт в той же docker-сети, публиковать порты наружу не нужно вовсе.

Закрываем доступ: API-ключ и TLS

Этот раздел чаще всего пропускают, а потом находят свою базу в чужих руках. По умолчанию Qdrant не защищён ничем: кто дотянулся до порта, тот читает и удаляет коллекции. Открытых инстансов с торчащим наружу 6333 в интернете находится неприятно много.

Минимум — ключ через переменную окружения QDRANT__SERVICE__API_KEY (как в compose-файле выше) или в конфиге:

service:
  api_key: ваш_секретный_ключ
  read_only_api_key: ключ_только_на_чтение
  enable_tls: true
tls:
  cert: ./tls/cert.pem
  key: ./tls/key.pem

Отдельный ключ на чтение полезен: сервису, который только ищет, право удалять коллекции не нужно. И не игнорируйте TLS — ключ, летящий по открытому HTTP, видно на любом промежуточном узле. Практичная схема для одного сервера: не выставлять порты наружу вовсе и ходить в базу изнутри docker-сети, а если доступ снаружи нужен — ставить перед Qdrant nginx с сертификатом. Об общих правилах работы с внутренними данными в ИИ-контуре — в заметке «Как не слить данные через ИИ».

Первая коллекция и первые векторы

Дальше работаем через официальный Python-клиент (pip install qdrant-client). Коллекция — аналог таблицы: фиксированная размерность векторов и метрика близости.

from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams, PointStruct

client = QdrantClient(url="http://localhost:6333", api_key="ваш_секретный_ключ")

client.create_collection(
    collection_name="docs",
    vectors_config=VectorParams(size=1536, distance=Distance.COSINE),
)

Размерность обязана совпадать с той, что выдаёт ваша модель эмбеддингов: поменяли модель — создавайте новую коллекцию. Метрика COSINE — стандартный выбор для текста, DOT — если векторы уже нормализованы, EUCLID нужен редко.

Запись называется upsert: точка с существующим id перезаписывается, с новым — добавляется. Вместе с вектором кладём payload — произвольный JSON, по которому потом можно фильтровать и который вернётся с результатом.

client.upsert(
    collection_name="docs",
    wait=True,
    points=[
        PointStruct(
            id=1,
            vector=embedding,  # список из 1536 чисел от вашей модели
            payload={"text": "текст фрагмента", "doc": "instrukciya.pdf", "year": 2026},
        )
    ],
)

Сами эмбеддинги вы получаете отдельно: через API провайдера (Gemini, OpenAI) или локальной моделью — например, через Ollama на своём сервере. Связка «Ollama для эмбеддингов плюс Qdrant для хранения» даёт полностью автономный контур: данные не покидают вашу инфраструктуру.

Поиск выглядит так:

result = client.query_points(
    collection_name="docs",
    query=query_embedding,
    limit=5,
    with_payload=True,
).points

Нюанс, на котором спотыкаются все: вектор запроса должен быть получен той же моделью, что и векторы в базе. Смешали две модели — выдача будет выглядеть правдоподобно и при этом не иметь смысла.

Фильтры по метаданным

Главное преимущество нормальной векторной базы перед самописным перебором — фильтрация. Условия комбинируются блоками: must (все, логическое И), should (хотя бы одно, ИЛИ), must_not (ни одного). Внутри — match для точного совпадения, range для чисел и дат (gt, gte, lt, lte), полнотекстовый text, гео-условия.

{
  "filter": {
    "must": [
      { "key": "doc", "match": { "value": "instrukciya.pdf" } },
      { "key": "year", "range": { "gte": 2025 } }
    ],
    "must_not": [
      { "key": "status", "match": { "value": "archived" } }
    ]
  }
}

Важная деталь: по полям, которые участвуют в фильтрах, нужно строить индекс — client.create_payload_index(). Без него условие проверяется перебором, и на большой коллекции «быстрый» поиск внезапно начинает занимать секунды. Особенно критично, когда каждый запрос ограничен идентификатором клиента.

Квантизация: как ужать память

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

  • Скалярная (с версии 1.1) — float32 превращается в 8-битное целое, сжатие в 4 раза, потеря точности обычно меньше 1%. Самый безопасный вариант «включил и забыл».
  • TurboQuant (появился в 1.18) — четыре режима: 4 бита (сжатие в 8 раз), 2 бита (16), 1,5 бита (24), 1 бит (32). Запрос при этом остаётся в полной точности, поэтому качество держится на уровне скалярной при вдвое большем сжатии.
  • Бинарная (с 1.5) — самая быстрая по CPU, но требует высокой размерности и «центрированного» распределения; на маленьких эмбеддингах качество проседает.
  • Продуктовая (с 1.2) — до 64 раз, но заметно медленнее. Берут, когда память дороже скорости.

Начинайте со скалярной, а если памяти всё равно не хватает — пробуйте 4-битный TurboQuant и обязательно замерьте качество поиска на своих запросах до и после. Универсального ответа тут нет: на одних данных сжатие в 16 раз проходит незаметно, на других ломает выдачу.

Бэкапы

Самый простой путь — остановить контейнер и скопировать каталог qdrant_storage: работает всегда, но требует простоя. Штатный способ без остановки — снапшоты: POST /collections/docs/snapshots снимает коллекцию, POST /snapshots — всю базу. Файлы кладутся внутрь тома, так что забирать их наружу всё равно придётся — например, ночным заданием cron. И проверьте восстановление хотя бы один раз на тестовом инстансе: бэкап, который никогда не разворачивали, бэкапом не является.

Когда всё-таки взять облако

Свой сервер — не догма. У Qdrant есть управляемое облако с бессрочным бесплатным тарифом: один узел, 0,5 vCPU, 1 ГБ памяти и 4 ГБ диска. Для прототипа и демо этого хватает, а администрировать не нужно вовсе.

Считать стоит по трём пунктам. Данные: если в базе персональные данные клиентов, требования к их размещению часто закрывают вопрос сами. Отказоустойчивость: реплики и автобэкапы в облаке уже есть, на своём сервере это ваша работа. Время: обновления, мониторинг и разбор ночных инцидентов — реальная нагрузка, её надо честно заложить в цену «бесплатного» хостинга. Разумный компромисс — держать своё, пока это один узел без жёстких требований к доступности.

Итог

Минимальный рабочий вариант: VPS с 4 ГБ памяти и NVMe, docker compose с зафиксированной версией, порты на 127.0.0.1, API-ключ в переменной окружения, коллекция с косинусной метрикой, индексы по фильтруемым полям и ночной снапшот в отдельное хранилище. Разворачивается за вечер и спокойно тянет базу знаний на десятки тысяч документов.

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

Частые вопросы

Q.Сколько оперативной памяти нужно для Qdrant?

Считайте по формуле: число векторов × размерность × 4 байта плюс около 154 байт на вектор под индекс HNSW и ещё ~20% запаса. Для миллиона фрагментов с эмбеддингами размерностью 1536 это примерно 7,5 ГБ. Для старта на десятки тысяч документов достаточно VPS с 2 vCPU и 4 ГБ памяти.

Q.Qdrant защищён по умолчанию?

Нет. Свежий инстанс принимает любые запросы: кто дотянулся до порта 6333, тот может читать и удалять коллекции. Обязательно задайте API-ключ через переменную QDRANT__SERVICE__API_KEY, привяжите порты к 127.0.0.1 и включите TLS, если база доступна снаружи.

Q.Чем Qdrant отличается от обычной базы данных?

Обычная база ищет по точному совпадению или диапазону значений, Qdrant — по близости векторов, то есть по смысловому сходству. При этом он умеет и то, и другое одновременно: фильтр по метаданным (клиент, дата, статус) применяется вместе с векторным поиском в одном запросе.

Q.Можно ли обойтись бесплатным облаком вместо своего сервера?

Для прототипа — да: бесплатный тариф Qdrant Cloud даёт один узел с 0,5 vCPU, 1 ГБ памяти и 4 ГБ диска бессрочно. Свой сервер выбирают, когда данные нельзя выносить наружу или когда объём перерастает бесплатные лимиты, но тогда обновления, мониторинг и бэкапы становятся вашей задачей.

Источники

Предыдущая
Векторные базы данных: обзор и как выбрать

Читайте также

Тёмная графитовая комната, крупным планом монитор на рабочем столе с дашбордом векторного поиска: трёхмерное облако точек, сгруппированное в синие кластеры, рядом графики и поле запроса; сбоку второй экран с зелёным терминалом и домашний сервер с индикаторами.Заметки
26 августа 2026 г.

Векторные базы данных: обзор и как выбрать

Векторная база нужна там, где поиск идёт по смыслу, а не по словам. Разбираем, как устроены индексы HNSW и IVF, зачем нужна квантизация, чем отличаются pgvector, Qdrant, Milvus, Weaviate, Chroma и Pinecone — и по какому алгоритму выбирать под свой проект.

Читать →
Тёмная графитовая комната: крупным планом монитор с колонками чисел и диаграммой рассеяния, где синие точки собираются в группы; перед экраном на столе лежит распечатанная страница с обычным текстом, на заднем плане в расфокусе второй экран с терминалом и кабелиЗаметки
25 августа 2026 г.

Эмбеддинги простыми словами: как текст превращается в числа

Эмбеддинг — это набор чисел, в который модель превращает текст, чтобы компьютер мог сравнивать смыслы. Разбираем без формул: откуда берутся эти числа, как считают близость, какие модели актуальны в 2026 году и какие ошибки чаще всего ломают поиск.

Читать →
Рабочий стол разработчика в тёмной комнате: на экране ноутбука открыта веб-консоль с панелью диалога слева и настройками параметров модели справа, позади — монитор с подсвеченным кодомЗаметки
24 августа 2026 г.

Google AI Studio: что это и как с ним работать

Веб-консоль Google для работы с моделями Gemini: чем она отличается от чата Gemini и от Vertex AI, что внутри интерфейса, как перенести промпт в код, что реально бесплатно и почему из России сервис открывается не у всех.

Читать →