Как обновлять локальные модели без простоя
После ollama pull в приложение иногда продолжают прилетать ответы старой версии модели — будто обновления и не было. А после systemctl restart ollama разом рвутся все соединения: и те, что уже генерировали ответ, и новые запросы, пока сервис не поднимется заново. Ниже — рабочая схема, как обновлять локальные модели и сам Ollama без разрывов: пошагово, с командами, конфигом HAProxy и цифрами с нашего стенда.
Содержание
- Почему «просто перезапустить» — это и есть простой
- Почему `ollama pull` не обновляет то, что уже загружено в память
- Пошаговое обновление модели без разрыва трафика
- Обновление самого Ollama: где риск на самом деле
- Память, потоки CPU и другие нюансы параллельного запуска
- Два инстанса и HAProxy: обновление без единого разрыва
- Какой сервер и локацию брать под эту задачу
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Почему «просто перезапустить» — это и есть простой
Ollama — однопроцессный сервер: ollama serve слушает один порт (по умолчанию 127.0.0.1:11434) и держит в памяти недавно использованные модели. Встроенного кластера у неё нет — любой перезапуск процесса означает интервал, когда порт никого не слушает.
Проверяется за минуту: запустите systemctl restart ollama и в соседнем терминале погоняйте curl -s http://127.0.0.1:11434/api/tags. Пока сервис перезапускается, ответ такой:
curl: (7) Failed to connect to 127.0.0.1 port 11434 after 2 ms: Connection refused
Дальше процесс поднимется, порт откроется — но модель в памяти уже не та: ollama serve стартует пустым. Первый запрос после рестарта — не тёплый ответ, а холодная загрузка весов с диска (чтение нескольких гигабайт плюс инициализация вычислительного графа). На NVMe с прогретым page cache это быстро, на медленном диске или при вытесненном кэше — заметно дольше.
systemctl restart ollama — это не обновление, а окно простоя предсказуемой, но ненулевой длины. Вопрос не в том, как убрать его совсем на одном процессе (никак), а в том, как обновлять модель, вообще не трогая процесс, и как обновлять сам процесс, не оставляя клиента без ответа.
Почему `ollama pull` не обновляет то, что уже загружено в память
Вторая иллюзия безопасности — «я не перезапускал процесс, значит, простоя не будет». Обновили тег командой ollama pull qwen2.5:7b, ollama list показывает новую дату модификации — кажется, что дело сделано.
Но pull меняет только то, что на диске: манифест тега и блобы весов в хранилище (по умолчанию /usr/share/ollama/.ollama/models для systemd-сервиса, ~/.ollama/models — при запуске от пользователя, путь переопределяется переменной OLLAMA_MODELS). Хранилище контентно-адресуемое: слой весов лежит под именем-хэшем содержимого, а тег — указатель на набор таких хэшей. Уже запущенный процесс работает с теми весами, что были на момент загрузки, и не обязан подхватывать смену указателя на лету. Не стройте план на предположении, что pull поверх тега, который прямо сейчас в работе, мгновенно обновит уже поднятый процесс — надёжный сценарий не зависит от этого вообще, он в следующем разделе.
Проверить, что сейчас реально в памяти, а что лежит на диске под тем же именем, можно, сопоставив вывод двух команд:
ollama list # NAME ID SIZE MODIFIED — что лежит на диске
ollama ps # NAME ID SIZE PROCESSOR UNTIL — что загружено в память
Если ID для одного и того же имени в этих двух выводах отличается — в памяти всё ещё старая версия, и её никто не трогал.
Развернуть за пару минут
Готовый образ на VPS MAATRIX: NVMe, AMD EPYC, root-доступ. Локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Развернуть OllamaПошаговое обновление модели без разрыва трафика
Рабочая последовательность не зависит от того, перезагрузит ли pull уже поднятый процесс: модель обновляется под отдельным именем, а трафик — явной командой.
Шаг 1. Снимок для отката. Скопируйте текущий тег — команда мгновенная: cp создаёт только новый манифест, без копирования весов.
ollama cp qwen2.5:7b qwen2.5:7b-rollback
Шаг 2. Загрузите новую версию под её собственным тегом, не перезаписывая тот, что сейчас обслуживает трафик.
ollama pull qwen2.5:7b-2026-08
Если реестр обновляет только тот же тег — шаг 1 уже подстраховал: откат на qwen2.5:7b-rollback не требует повторной загрузки весов.
Прежде чем греть вторую версию — проверьте лимит загруженных моделей. Без явного OLLAMA_MAX_LOADED_MODELS Ollama сама решает, сколько держать резидентно, по свободной памяти, и в стеснённых условиях это может быть 1 — тогда прогрев новой версии вытеснит ту, что обслуживает трафик, и вы получите тот самый разрыв, которого избегаете. Нужно минимум 2:
sudo systemctl edit ollama
# [Service]
# Environment="OLLAMA_MAX_LOADED_MODELS=2"
sudo systemctl daemon-reload && sudo systemctl restart ollama
Шаг 3. Прогрейте новую модель заранее, не дожидаясь первого клиентского запроса. API загружает модель в память даже без текста в поле prompt:
curl -s http://127.0.0.1:11434/api/generate -d '{
"model": "qwen2.5:7b-2026-08",
"keep_alive": "30m"
}'
keep_alive держит модель резидентной 30 минут вместо дефолтных пяти — хватит на проверку и переключение без риска выгрузки таймером.
Шаг 4. Проверьте, что она в памяти и отвечает вменяемо.
ollama ps
curl -s http://127.0.0.1:11434/api/chat -d '{"model":"qwen2.5:7b-2026-08","messages":[{"role":"user","content":"ping"}],"stream":false}'
Шаг 5. Переключите приложение. Если сервис читает имя модели из конфига или переменной окружения — это единственная правка: смените значение на новый тег и перезагрузите конфиг приложения, не Ollama. Ради этого стоит называть модели версионными тегами, а не держать всё на latest: переключение становится осознанной правкой, а не тихой подменой.
Шаг 6. Выгрузите старую версию явно, не дожидаясь, пока её вытеснит таймер простоя.
ollama stop qwen2.5:7b
Шаг 7. Удалите старый тег, когда новая версия отработает под нагрузкой хотя бы день-два.
ollama rm qwen2.5:7b-rollback
Если теги делят часть слоёв весов, rm освободит только блобы без ссылок — общие слои останутся, пока на них указывает хотя бы одна версия.
Обновление самого Ollama: где риск на самом деле
Замена версии модели не трогает бинарник ollama. Но рано или поздно нужно обновить и сам сервер — новый релиз, багфикс, поддержка свежей архитектуры модели. Официальный путь один и тот же и для установки, и для апдейта:
curl -fsSL https://ollama.com/install.sh | sh
Скрипт заменяет файл /usr/local/bin/ollama и сам перезапускает ollama.service, без возможности отложить рестарт. Это тот же простой, что и в первом разделе, только неизбежный: новая версия бинарника и есть новый процесс.
Видно это в journalctl -u ollama -n 50 --no-pager: Active: inactive (dead) → activating → active (running), в логе строка level=INFO source=routes.go msg="Listening on 127.0.0.1:11434". Порт снова принимает соединения, но модели не загружены — тот же холодный старт.
Если сервер один, честный минимум таков: интервал недоступности будет всегда, вопрос только в длине и в том, попадёт ли он на реальный запрос. Сократить до секунд помогает прогрев тем же приёмом, что в шаге 3, плюс окно минимальной нагрузки под само обновление. Полностью убрать интервал на одном процессе нельзя — а на двух это решаемая задача.
Память, потоки CPU и другие нюансы параллельного запуска
Держать две версии модели в памяти одновременно или гонять два процесса Ollama — не бесплатно, вот во что это упирается на практике.
Память удваивается, а не складывается с запасом. Документация и практика квантизации сходятся: модель 7B в Q4 занимает около 4,5 ГБ весов на диске. В памяти во время работы больше — добавляются буферы контекста. По нашим замерам на стенде с AMD EPYC 9554 (16 vCPU, 8 физических ядер с HT), Ollama 0.33.1, вот что реально резидентно (не вес файла и не «минимум с запасом», а занятая RAM во время работы):
| Модель (Q4_K_M) | Резидентная память |
|---|---|
| qwen2.5:3b | 2,2 ГБ |
| mistral:7b | 5,0 ГБ |
| qwen2.5:7b | 5,1 ГБ |
| llama3.1:8b | 5,6 ГБ |
Значит, держать старую и новую версию класса 7B одновременно — не 5, а 10–11 ГБ только под веса, плюс память процесса и запас на контекст.
Потоки CPU не масштабируются линейно, а оверсабскрипция ломает скорость. num_thread — параметр модели (PARAMETER num_thread N в Modelfile или поле options в API), не переменная окружения; без явного значения Ollama забирает все доступные логические ядра. На том же стенде для qwen2.5:7b:
| num_thread | Генерация, ток/с | Обработка промпта, ток/с |
|---|---|---|
| 2 | 5,7 | 12,6 |
| 4 | 7,6 | 31,0 |
| 8 | 7,6 | 61,7 |
| 16 | 7,4 | 68,0 |
| 32 | 0,35 | 10,9 |
Генерация выходит на полку уже на 4 потоках — дальше упирается в память, не в CPU. А 32 потока на 16 vCPU дают не прирост, а обвал в двадцать раз: планировщик тратит больше времени на переключение контекста, чем на вычисления. Два инстанса Ollama с настройками по умолчанию на этом сервере заберут по 16 ядер каждый — совокупная оверсабскрипция попадёт в ту же провальную зону. Ограничивайте num_thread для каждого инстанса явно — например, по 4, там, где генерация уже вышла на полку.
Если упёрлись в память — процесс просто убьют, без вежливой ошибки от Ollama. Признак — запись ядра в dmesg или journalctl -k:
Out of memory: Killed process 8842 (ollama) total-vm:14235184kB, anon-rss:5320412kB
На GPU запас обычно теснее — VRAM меньше RAM и не наращивается без смены сервера. Держать две копии модели в VRAM часто негде; практичнее выгрузить старую (ollama stop) и сразу поднять новую, приняв короткий разрыв на загрузку вместо blue-green в памяти.
Два инстанса и HAProxy: обновление без единого разрыва
Всё выше убирает разрыв при смене версии модели. Но апдейт самого бинарника Ollama на одном процессе всё равно означает интервал недоступности. Убрать и его можно только одним способом — не апдейтить единственную точку входа, а держать две и обновлять по очереди.
Схема: два процесса Ollama и балансировщик перед ними с активной проверкой здоровья и выводом бэкенда из ротации по команде. Открытый Nginx для этого не годится — активные health-check только в платной Nginx Plus. HAProxy умеет их бесплатно.
Второй инстанс — отдельный systemd-юнит на другом порту, указывающий на то же хранилище, чтобы не тянуть веса дважды:
# /etc/systemd/system/ollama-b.service
[Unit]
Description=Ollama Service (instance B)
After=network-online.target
[Service]
User=ollama
Group=ollama
Environment="OLLAMA_HOST=127.0.0.1:11435"
Environment="OLLAMA_MODELS=/usr/share/ollama/.ollama/models"
ExecStart=/usr/local/bin/ollama serve
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.target
Конфиг HAProxy — фронт на 8080, два бэкенда с активной проверкой (Ollama на GET / отвечает текстом Ollama is running):
# /etc/haproxy/haproxy.cfg
global
maxconn 512
stats socket /run/haproxy/admin.sock mode 660 level admin
defaults
mode http
timeout connect 5s
timeout client 300s
timeout server 300s
frontend ollama_front
bind 127.0.0.1:8080
default_backend ollama_pool
backend ollama_pool
balance roundrobin
option httpchk GET /
http-check expect string Ollama\ is\ running
default-server inter 3s fall 2 rise 2
server ollama_a 127.0.0.1:11434 check
server ollama_b 127.0.0.1:11435 check
Таймауты клиента и сервера подняты до 300 секунд намеренно: генерация длинного ответа — не быстрый REST-запрос, дефолтные значения HAProxy оборвут поток на середине.
Обновление — поочерёдный вывод из ротации через runtime-сокет, без правки конфига и рестарта HAProxy:
# увести инстанс A из-под нагрузки, дав текущим соединениям доработать
echo "set server ollama_pool/ollama_a state drain" | sudo socat stdio /run/haproxy/admin.sock
# весь трафик уже на B — обновляем бинарник, это перезапустит ollama.service (=A)
curl -fsSL https://ollama.com/install.sh | sh
# A на новой версии, порт открыт, health-check зелёный — возвращаем в пул
echo "set server ollama_pool/ollama_a state ready" | sudo socat stdio /run/haproxy/admin.sock
# то же самое для B: увести и перезапустить процесс на уже обновлённом бинарнике
echo "set server ollama_pool/ollama_b state drain" | sudo socat stdio /run/haproxy/admin.sock
sudo systemctl restart ollama-b
echo "set server ollama_pool/ollama_b state ready" | sudo socat stdio /run/haproxy/admin.sock
Повторный install.sh для B не нужен: файл /usr/local/bin/ollama уже обновлён на диске, процессу B достаточно перечитать его через systemctl restart. В пуле всегда остаётся минимум один живой инстанс — клиент видит лишь чуть более медленные ответы, не обрыв.
Честно: без реального SLA на аптайм это инфраструктура ради инфраструктуры. Для внутреннего инструмента связка «версионные теги плюс прогрев плюс ollama stop» закрывает большинство случаев без HAProxy. Второй инстанс нужен, когда простой считается в деньгах или прописан в SLA, а не когда он просто неудобен.
Какой сервер и локацию брать под эту задачу
Ollama есть в каталоге приложений MAATRIX (apps.maatrix.io) — при заказе она разворачивается автоматически, без ручного install.sh, доступы появляются сразу в кабинете. Всё описанное выше — теги, ollama cp, прогрев, HAProxy — уже эксплуатация поднятого инстанса, и не важно, кто ставил Ollama: вы или автоматика при заказе.
Конфигурация под задачу считается прямо из разделов выше:
| Вариант | Ресурсы | Что получаете |
|---|---|---|
| Честный минимум | 2 vCPU, 8 ГБ RAM, 40 ГБ NVMe | Одна модель класса 7B (5–6 ГБ резидентно) с запасом под ОС, схема «теги плюс ollama cp плюс ollama stop» |
| Комфортный вариант | 4 vCPU, 16 ГБ RAM, 80–100 ГБ NVMe | Старая и новая версия модели в памяти одновременно (≈10–11 ГБ на две копии 7B), место под сосуществование тегов на диске |
| Под HAProxy и два инстанса | вдвое от комфортного варианта по CPU и RAM либо два сервера поменьше | Обновление бинарника Ollama вообще без разрыва |
Ограничение честного минимума прямое: интервал недоступности при обновлении бинарника на одном сервере не убрать — секунды на прогретом NVMe, не часы, но он есть. За полное отсутствие разрыва отвечает третий вариант.
Локация — Великобритания, Лондон. Причина конкретная, не общая: ollama pull тянет по несколько гигабайт на каждое обновление, и скорость этого шага определяет, сколько модель «на прогреве» до переключения трафика. Из Лондона канал до инфраструктуры, раздающей веса моделей (крупные CDN в США и ЕС), быстрый и предсказуемый — в отличие от маршрутов из России, где связность с такими хостами не гарантирована и может проседать именно в нужный момент.
Оплата — картами российских банков, СБП, криптовалютой или токеном MAAT; загранкарта для сервера в UK не нужна.
Развернуть за пару минут
Готовый образ на VPS MAATRIX: NVMe, AMD EPYC, root-доступ. Локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Развернуть OllamaОбсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Частые вопросы
Что произойдёт с запросом, который в момент systemctl restart ollama уже генерирует ответ?
Соединение оборвётся вместе с процессом: клиент увидит обрыв на середине потока — типично curl: (56) Recv failure: Connection reset by peer. Дождаться завершения генераций systemctl restart не умеет. Критично — используйте HAProxy: там state drain перед остановкой даёт запросам время закончиться.
Обязательно ли поднимать второй инстанс и HAProxy, чтобы обновляться без простоя?
Нет, если речь о смене версии модели: тегированный pull плюс прогрев плюс ollama stop работают на одном сервере. Второй инстанс нужен для другой задачи — обновления самого бинарника без единого разрыва: без второй точки входа этот интервал не убрать, новую версию бинарника нельзя запустить, не остановив старую в том же процессе.
Как проверить, что после обновления в памяти действительно новая версия модели, а не старая?
Сравните колонку ID в выводе ollama list (что лежит на диске под этим тегом) и ollama ps (что реально загружено в память прямо сейчас). Разные значения — в памяти всё ещё старая версия. ollama stop <модель> и повторный запрос заставят загрузиться заново, уже с новыми весами.
Нужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.