MAATRIX / Блог / Как установить и настроить Milvus на VPS

Как установить и настроить Milvus на VPS

Как установить и настроить Milvus на VPS

MAATRIX

Milvus обещает миллиарды векторов и субсекундный поиск, а начинается с того, что вместо одного бинарника — три контейнера, и один из них etcd. Установка на VPS упирается не в команду запуска, а в память под индекс, неизменяемую размерность коллекции и порты, которые официальный compose выставляет наружу без пароля. Разберём путь: подготовка сервера, standalone в Docker, первая коллекция, расчёт RAM, авторизация, бэкап.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Что такое Milvus и почему установка сложнее, чем у Qdrant

Milvus — векторная СУБД под лицензией Apache 2.0: ядро на Go, поиск — Knowhere на C++. От Qdrant и Chroma её отличает не API, а устройство: распределённая система из ролей — proxy, координаторы, query/data/index-ноды, а standalone сворачивает их в один процесс. Отсюда: Milvus — это всегда минимум три сервиса, если не выбран Lite.

РежимЧто это на самом делеКому подходит
Milvus Liteбиблиотека pymilvus, база в файлепрототип, до миллиона векторов
StandaloneMilvus + etcd + MinIO, три контейнера90% задач на VPS, десятки миллионов
DistributedKubernetes, Helm-чартшардирование, раздельное масштабирование

Lite сервера не поднимает вообще:

from pymilvus import MilvusClient
client = MilvusClient("./milvus_demo.db")   # это Milvus Lite, а не сервер

Собирается под Linux и macOS — без авторизации, сети и части индексов.

Порты связки:

КомпонентПортЗачем
Milvus19530gRPC-API + RESTful v2 (cmux)
Milvus9091/healthz, /metrics
etcd2379метаданные: схемы, сегменты, узлы
MinIO9000 / 9001сегменты, бинлоги, индексы, консоль

Очередь сообщений встроенная — RocksMQ в /var/lib/milvus/rdb_data, Pulsar и Kafka нужны только распределённой сборке. Документация просит минимум 4 ядра, 8 ГБ RAM и 100 ГБ диска, рекомендует 8/32 — коллекция грузится в RAM целиком, это не перестраховка.

Общая для векторных баз граница: Milvus не считает эмбеддинги, генерация есть только для разреженного BM25. Как считать векторы без видеокарты — в статье про эмбеддинги на CPU.

Подготовка VPS: Docker, диск и лимиты

Ubuntu 24.04 или Debian 12, SSH под пользователем с sudo. Milvus требует Docker 19.03+ и Docker Compose v2:

curl -fsSL https://get.docker.com | sh
docker --version && docker compose version

Три проверки дешевле до установки, чем после.

Свободные порты. Если на 9000 уже живёт MinIO, контейнер не поднимется: Bind for 0.0.0.0:9000 failed: port is already allocated.

ss -ltnp | grep -E '19530|9091|2379|9000|9001'

Скорость диска — главный неочевидный пункт: etcd пишет каждую транзакцию с fsync, на медленном диске Milvus разваливается под нагрузкой без понятной ошибки. Разработчики etcd рекомендуют 99-й перцентиль fsync ниже 10 мс — проверяется их тестом:

sudo apt install -y fio
fio --rw=write --ioengine=sync --fdatasync=1 --directory=/var/lib/docker \
    --size=200m --bs=2300 --name=etcd-fsync

Смотрите 99.00th в блоке fsync/fdatasync/sync_file_range: на NVMe — единицы миллисекунд, на перегруженном SATA — десятки и сотни. В бою: apply request took too long в логе etcd, Milvus падает на context deadline exceeded. На HDD Milvus ставить не надо.

Память и swap. Своп векторную базу не спасает: промах в подкачанный индекс растит задержку на порядки, а etcd от паузы отваливается по таймауту — подробнее, что делать при нехватке RAM. Заодно поднимите лимит дескрипторов: ulimits: nofile: { soft: 65536, hard: 65536 }.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Установка standalone: скрипт за минуту или compose для боя

Путь первый — официальный скрипт: один контейнер, Milvus со встроенным etcd (ETCD_USE_EMBED=true) и локальным хранилищем вместо MinIO (COMMON_STORAGETYPE=local).

curl -sfL https://raw.githubusercontent.com/milvus-io/milvus/master/scripts/standalone_embed.sh -o standalone_embed.sh
bash standalone_embed.sh start

Рядом появятся volumes/milvus, embedEtcd.yaml, user.yaml; скрипт понимает start/stop/delete/upgrade. Подвох: публикует -p 2379:2379 — etcd со всеми метаданными доступен без пароля. Проверьте ss -ltnp | grep 2379 и закройте порт.

Путь второй — Docker Compose, так ставят на бой. Официальный файл с нужным тегом публикует наружу 19530, 9000 и 9001:

wget https://github.com/milvus-io/milvus/releases/download/v2.5.10/milvus-standalone-docker-compose.yml -O docker-compose.yml

Правленый вариант — всё на петле:

services:
  etcd:
    container_name: milvus-etcd
    image: quay.io/coreos/etcd:v3.5.18
    environment:
      - ETCD_AUTO_COMPACTION_MODE=revision
      - ETCD_AUTO_COMPACTION_RETENTION=1000
      - ETCD_QUOTA_BACKEND_BYTES=4294967296
      - ETCD_SNAPSHOT_COUNT=50000
    volumes:
      - ./volumes/etcd:/etcd
    command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd
    healthcheck:
      test: ["CMD", "etcdctl", "endpoint", "health"]
      interval: 30s
      timeout: 20s
      retries: 3

  minio:
    container_name: milvus-minio
    image: minio/minio:RELEASE.2023-03-20T20-16-18Z
    environment:
      MINIO_ROOT_USER: milvus
      MINIO_ROOT_PASSWORD: ЗАМЕНИТЕ_МЕНЯ
    ports:
      - "127.0.0.1:9001:9001"
    volumes:
      - ./volumes/minio:/minio_data
    command: minio server /minio_data --console-address ":9001"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
      interval: 30s
      timeout: 20s
      retries: 3

  standalone:
    container_name: milvus-standalone
    image: milvusdb/milvus:v2.5.10
    command: ["milvus", "run", "standalone"]
    security_opt:
      - seccomp:unconfined
    environment:
      ETCD_ENDPOINTS: etcd:2379
      MINIO_ADDRESS: minio:9000
      MINIO_ACCESS_KEY_ID: milvus
      MINIO_SECRET_ACCESS_KEY: ЗАМЕНИТЕ_МЕНЯ
    volumes:
      - ./volumes/milvus:/var/lib/milvus
      - ./user.yaml:/milvus/configs/user.yaml
    ports:
      - "127.0.0.1:19530:19530"
      - "127.0.0.1:9091:9091"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9091/healthz"]
      interval: 30s
      start_period: 90s
      timeout: 20s
      retries: 3
    depends_on: [etcd, minio]

  attu:
    image: zilliz/attu:v2.5
    environment:
      MILVUS_URL: milvus-standalone:19530
    ports:
      - "127.0.0.1:8000:3000"

Четыре строки нельзя выкидывать: ETCD_QUOTA_BACKEND_BYTES поднимает лимит etcd с 2 до 4 ГиБ (без него — etcdserver: mvcc: database space exceeded, и Milvus встаёт целиком), ETCD_AUTO_COMPACTION_* уплотняет ревизии, иначе лимит выбирается за недели, seccomp:unconfined нужен Milvus — дефолт блокирует часть системных вызовов, start_period: 90s — холодный старт больше минуты.

mkdir -p volumes && touch user.yaml
docker compose up -d && docker compose ps
curl -s http://127.0.0.1:9091/healthz

Здоровая система отвечает OK, docker compose psUp ... (healthy). Restarting — смотрите docker compose logs --tail=80 standalone: dial tcp: lookup minio on 127.0.0.11:53: no such host — разные сети или не встал MinIO; context deadline exceeded у etcd — сам не стартовал, чаще из-за прав на ./volumes/etcd или места на диске. Веб-интерфейс Attu — на туннеле ssh -N -L 8000:127.0.0.1:8000 user@server.

Первая коллекция: схема, размерность и индекс

Клиент не должен быть старше сервера:

pip install -U pymilvus
python3 - <<'PY'
from pymilvus import connections, utility
connections.connect(uri="http://127.0.0.1:19530")
print(utility.get_server_version())
PY

Ответ v2.5.10 значит, что сервер жив. Вместо версии — MilvusException: (code=2, message=Fail connecting to server on 127.0.0.1:19530, illegal connection params or server unavailable): контейнер не поднялся либо publish на localhost, а стучитесь снаружи.

Создавайте коллекцию явно, а не через create_collection("docs", dimension=768) из быстрого старта — со схемой видно все решения:

from pymilvus import MilvusClient, DataType

client = MilvusClient(uri="http://127.0.0.1:19530")

schema = client.create_schema(auto_id=False, enable_dynamic_field=True)
schema.add_field("id", DataType.INT64, is_primary=True)
schema.add_field("vector", DataType.FLOAT_VECTOR, dim=1024)
schema.add_field("text", DataType.VARCHAR, max_length=8192)
schema.add_field("lang", DataType.VARCHAR, max_length=8)

index_params = client.prepare_index_params()
index_params.add_index(
    field_name="vector", index_type="HNSW", metric_type="COSINE",
    params={"M": 16, "efConstruction": 200},
)

client.create_collection("docs", schema=schema, index_params=index_params)
client.load_collection("docs")

Что решается навсегда:

  • dim изменить нельзя — сменили модель эмбеддингов, заводите новую коллекцию. Ориентиры: bge-m3 — 1024, text-embedding-3-small — 1536; несовпадение — the dim (768) of field data(vector) is not equal to schema dim (1024).
  • metric_type должен соответствовать модели: COSINE/IP для нормализованных векторов, L2 для остальных — перепутали, и поиск молча вернёт мусор, что хуже падения.
  • Без индекса и load_collection поиск не работает — незагруженная коллекция вернёт collection not loaded. Не хотите выбирать параметры — index_type="AUTOINDEX", за ним тот же HNSW.

Две ловушки на вставке: Milvus не проверяет уникальность id — повтор insert даёт второй документ и дубли в поиске, для перезаливки нужен upsert(...). Свежее находится не сразу: consistency по умолчанию Bounded, между вставкой и видимостью — секунды; в тестах — consistency_level="Strong", в бою оставьте как есть — дороже по задержке.

client.insert("docs", [{"id": 1, "vector": vec, "text": "…", "lang": "ru"}])
res = client.search("docs", data=[query_vec], limit=5,
                    search_params={"params": {"ef": 64}},
                    filter='lang == "ru"', output_fields=["text"])

Память под индекс: расчёт и способы ужаться

Milvus держит загруженную коллекцию в RAM целиком, поэтому объём памяти определяет не число документов, а размерность векторов. float32 — 4 байта, вектор — dim × 4: для 1024 измерений это 4096 байт, миллион векторов — 4,1 ГБ сырых данных; граф HNSW при M = 16 добавляет ~0,13 ГБ на миллион (M × 2 × 4 байт на элемент).

МодельdimВектор1 млн5 млн
bge-small3841,5 КБ1,4 ГБ7,2 ГБ
e5-base / open-source7683,0 КБ2,9 ГБ14,3 ГБ
bge-m310244,0 КБ3,8 ГБ19,1 ГБ
text-embedding-3-large307212,0 КБ11,4 ГБ57,2 ГБ

Это оценка объёма данных, а не замер процесса: прибавляйте payload и запас на слияние сегментов. Отсюда документированные 8 ГБ минимума — на них живёт около миллиона 768-мерных векторов, не больше. Когда цифры не сходятся, есть три рычага с честной ценой у каждого.

  • mmap. С версии 2.4 Milvus умеет отображать векторы с диска вместо RAM; ключ жил под queryNode.mmap, в 2.5 стал верхнеуровневым. Цена — диск в горячем пути: терпимо на NVMe, неприемлемо на сетевом хранилище.
  mmap:
    mmapEnabled: true
    vectorField: true
  • Квантование. IVF_SQ8 сжимает float32 до int8, уменьшая объём вчетверо, IVF_PQ — сильнее; recall падает, насколько — зависит от данных, проверяйте на своей выборке.
  • DISKANN. Индекс на диске, в памяти — сжатое представление. Требует queryNode.enableDisk: true и локальный NVMe.

При переполнении срабатывает защита: quotaAndLimits.limitWriting.memProtection задаёт пороги queryNodeMemoryHighWaterLevel: 0.95 и dataNodeMemoryHighWaterLevel: 0.95, запись отклоняется с quota exceeded[reason=memory quota exceeded] — хороший исход. Плохой — sudo dmesg -T | grep -i 'killed process' со строкой milvus: сервер меньше коллекции. Расчёт с другими индексами — в материале про RAM для векторной базы.

Безопасность, бэкап и обновление

Авторизации по умолчанию нет — кто дотянулся до 19530, тот читает и удаляет коллекции. Включается через user.yaml, смонтированный в /milvus/configs/user.yaml:

common:
  security:
    authorizationEnabled: true

После docker compose restart standalone появляется root с паролем Milvus, одинаковым у всех установок. Меняйте сразу; для приложения — отдельный пользователь через create_user/create_role/grant_privilege.

client = MilvusClient(uri="http://127.0.0.1:19530", token="root:Milvus")
client.update_password(user_name="root", old_password="Milvus", new_password="…")

MinIO с дефолтными ключами. В compose из релиза — minioadmin:minioadmin, консоль на 9001 публикуется наружу. Меняйте до первого запуска — после придётся синхронно править minio.accessKeyID и minio.secretAccessKey, иначе Milvus не увидит бакет.

Фаервол и Docker. -p 19530:19530 обходит ufw — Docker пишет свои правила DNAT, порт открыт при любых ufw deny. Надёжно — петля, как выше; ufw allow 22/tcp, ufw deny 19530/tcp и 2379/tcp вторым рубежом.

Доступ снаружи. Для проверок хватит ssh -N -L 19530:127.0.0.1:19530 user@server. Постоянному — reverse-proxy с TLS и gRPC: proxy_pass Nginx не годится, нужен grpc_pass с HTTP/2 — в Ubuntu 24.04 версия Nginx 1.24, а http2 on; заработала только с 1.25.1. Либо TLS Milvus напрямую — tlsMode: 1.

Бэкап. Архивировать volumes/ на работающей базе нельзя — etcd и MinIO снимутся в разные моменты, восстановление даст коллекцию с потерянными сегментами. Холодный путь: docker compose stop, архив, старт обратно. Горячий — milvus-backup от Zilliz:

./milvus-backup check
./milvus-backup create -n backup_2026_08
./milvus-backup restore -n backup_2026_08 -s _restored

check подтверждает, что configs/backup.yaml смотрит на тот же MinIO и bucket — частая ошибка настройки. Восстановление создаёт коллекции с суффиксом docs_restored, не затирая живые.

Обновление. Внутри минорной ветки — сменить тег и docker compose up -d. Между версиями — release notes: миграция метаданных бывает односторонней, понижение версии официально не поддерживается. Порядок один: бэкап, снимок диска, смена тега.

Какой сервер под Milvus брать в MAATRIX

Сразу честно: Milvus нет в каталоге apps.maatrix.io, автоустановки не будет. Сервер приезжает чистым — Ubuntu 24.04 или Debian 12, — ставите по третьему разделу, минут за пятнадцать. В каталоге есть готовые сборки соседей: Qdrant, Ollama, Open WebUI и AnythingLLM как интерфейсы, LiteLLM — шлюз к внешним API, Portainer для контейнеров.

Честный минимум: 4 vCPU, 8 ГБ RAM, 100 ГБ NVMe — то, что просит документация, и не зря. На 2–4 ГБ standalone стартует, отвечает OK на /healthz, но первый load_collection на миллионе векторов кончается записью в dmesg. На 8 ГБ спокойно живёт около миллиона 768-мерных векторов. Корпус — десятки тысяч документов, растёт медленно? Разумнее Qdrant: один контейнер, вдвое больше свободной памяти на том же железе.

Комфортный вариант: 8 vCPU, 32 ГБ RAM, 200–300 ГБ NVMe — рекомендация разработчиков, точка, где Milvus окупает сложность: 5–10 млн векторов на 768 измерениях без mmap, место под MinIO, бэкапы, вторую коллекцию под новую модель. Для 3072-мерных — 64 ГБ либо сразу mmap и DISKANN. Диск — с коэффициентом три: MinIO, индексы, WAL RocksMQ (retentionSizeInMB: 8192, трое суток), бэкапы. Только NVMe — из-за fsync etcd.

Локация — Великобритания, Лондон. Milvus редко стоит один: рядом модель эмбеддингов, LLM-шлюз, приложение. С британского адреса Hugging Face, PyPI, GitHub и реестры контейнеров открываются напрямую, без зеркал и обрывов на середине образа, пинг до Европы — единицы-десятки миллисекунд. US — если рядом прокси к OpenAI/Anthropic, RU — если данные обязаны оставаться в РФ по 152-ФЗ. Схема связки — в статье про RAG по своим документам.

Оплата — картами российских банков, по СБП, криптовалютой или токеном MAAT; иностранная карта не нужна, доступы — в личном кабинете. Перед заказом посчитайте dim × 4 × число_векторов — арифметика на салфетке точнее тарифного калькулятора, сразу покажет, нужен ли Milvus или хватит базы попроще.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

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

Можно ли поднять Milvus на VPS с 2 ГБ RAM?

Серверный standalone — нет: три контейнера и загруженная коллекция не поместятся, dmesg покажет убитый процесс. На 2 ГБ — только Milvus Lite внутри Python-процесса, без сети и авторизации.

Вставил векторы, а поиск их не находит. База сломалась?

Почти наверняка нет. Проверьте: выполнен ли load_collection (иначе collection not loaded), есть ли индекс на векторном поле, не мешает ли Bounded consistency по умолчанию — записи видны с задержкой в секунды. Для проверки передайте consistency_level="Strong".

Milvus упал после недель работы, в логах mvcc: database space exceeded. Что это?

Переполнилась база etcd: лимит по умолчанию 2 ГиБ, без автоуплотнения выбирается активной записью — отсюда ETCD_QUOTA_BACKEND_BYTES и ETCD_AUTO_COMPACTION_* в compose. Лечится etcdctl defrag в контейнере etcd, затем флаг NOSPACE снимают через etcdctl alarm disarm.

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.