Еще недавно поиск в Google или Яндекс был главным инструментом инженера. Сегодня все изменилось: AI-помощники вроде ChatGPT, Gemini или Claude, понимающие запросы на естественном языке, кардинально меняют подход к работе. Однако их использование упирается в серьезные преграды: вопросы конфиденциальности корпоративных данных, географические блокировки и лимиты бесплатных тарифов стали новой головной болью.
Что, если получить все преимущества мощной языковой модели, но без этих недостатков? Решение — развернуть собственную модель на своих серверах. Эта статья — практический гид по созданию автономного чат-бота, который не уходит в облако, работает без интернета и полностью защищает данные. Я пройду путь от теории до работающего локального прототипа.

Выбор платформы и модели
Я разверну модель на виртуальной машине крупного облачного провайдера, но это можно сделать в любом облаке, где есть ВМ с подходящими параметрами.
Вообще я немного слукавил в заголовке, указав, что будет достаточно виртуальной машины за 10$. На самом деле не хватит ни ВМ с free tier с бесплатными 2 vCPU и 4 ГБ ОЗУ, ни даже виртуалки за примерно 10$ — потому что нам понадобится конфигурация как минимум 4 vCPU / 8 ГБ ОЗУ. Дело в том, что демонстрация будет на модели Mistral-7B-Instruct, которая потребляет 5.3 ГБ ОЗУ в квантованной версии q4 и никак не поместится в инстанс с 4 ГБ ОЗУ. Теоретически можно выбрать более легкую модель, например, Gemma 2B, но тогда есть шанс проиграть в качестве ответов, поддержке русского языка и длине контекста.
Основные данные по моделям, которые можно запустить на легкой виртуальной машине:
Модель |
Параметры |
RAM (Q4) |
Контекст |
CPU |
Особ-ти |
Gemma-2B |
2B |
~2 ГБ |
2K–4K |
2 vCPU |
Очень легкая, запускается даже |
TinyLlama |
1.1B |
<4 ГБ |
2K |
2 vCPU |
Максимально компактная, подходит для edge-устройств |
Phi-3 Mini |
3.8B |
~4 ГБ (Q4) |
до 128K |
4 vCPU |
Выше reasoning |
Gemma-3:4B |
4B |
~4 ГБ |
4K–8K |
4 vCPU |
Сбалансирована: легкая + мультиязычная |
Orca-Mini 7B |
7B |
~5 ГБ |
4K–8K |
4–6 vCPU |
Хорошая инструкция-тюнинг модель для чата |
Mistral 7B |
7.3B |
~5–6 ГБ |
8K |
4–8 vCPU |
Лидер среди 7B: reasoning, кодинг, русский язык |
Qwen 2.5 4B |
4B |
~6–7 ГБ |
8K–32K |
4–6 vCPU |
Отличная мультиязычность, поддержка длинных документов |
Развертывание LLM через Ollama
Теперь перейду к практике и попробую самостоятельно запустить модель. В первую очередь закажу сервер. Когда он будет создан, подключусь к нему по SSH:
%ssh root@135.$$$.$$$.230
The authenticity of host '135.$$$.$$$.$$$' can't be established.
ED25519 key fingerprint is SHA256:qix3mEADcUR0ysq0S$$$$$$$$$$$$$$$$$$$c1CcE.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '135.181.81.230' (ED25519) to the list of known hosts.
Welcome to Ubuntu 24.04.3 LTS (GNU/Linux 6.8.0-71-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/pro
System information as of Fri Aug 29 12:07:58 PM UTC 2025
System load: 0.18 Processes: 188
Usage of /: 0.5% of 225.04GB Users logged in: 0
Memory usage: 0% IPv4 address for eth0: 157.$$$.$$$.70
Swap usage: 0% IPv6 address for eth0: 2a01:$$$$:$$$$:138::1
Expanded Security Maintenance for Applications is not enabled.
0 updates can be applied immediately.
Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status
The list of available updates is more than a week old.
To check for new updates run: sudo apt update
root@test-k8s:~#
При первом подключении по SSH, так как сервер неизвестный, система предлагает принять ключ. Это я и сделаю, после чего попаду в консоль.
Обновлю системные пакеты при помощи следующей команды:
root@test-k8s:~# apt update && apt upgrade -y
После обновления установлю ollama:
root@test-k8s:~# curl -fsSL https://ollama.com/install.sh | sh
>>> Installing ollama to /usr/local
>>> Downloading Linux amd64 bundle
######################################################################## 100.0%
>>> Creating ollama user...
>>> Adding ollama user to render group...
>>> Adding ollama user to video group...
>>> Adding current user to ollama group...
>>> Creating ollama systemd service...
>>> Enabling and starting ollama service...
Created symlink /etc/systemd/system/default.target.wants/ollama.service → /etc/systemd/system/ollama.service.
>>> The Ollama API is now available at 127.0.0.1:11434.
>>> Install complete. Run "ollama" from the command line.
WARNING: No NVIDIA/AMD GPU detected. Ollama will run in CPU-only mode.
Вижу, что на сервере не установлены никакие графические ускорители, поэтому модель может быть запущена только на центральном процессоре.
Убеждаюсь в том, что сервис Ollama работает:
root@test-k8s:~# systemctl status ollama
ollama.service - Ollama Service
Loaded: loaded (/etc/systemd/system/ollama.service; enabled; preset: enabled)
Active: active (running) since Fri 2025-08-29 12:10:07 UTC; 15s ago
Main PID: 6124 (ollama)
Tasks: 10 (limit: 37559)
Memory: 9.6M (peak: 10.1M)
CPU: 46ms
CGroup: /system.slice/ollama.service
└─6124 /usr/local/bin/ollama serve
Aug 29 12:10:07 test-k8s ollama[6124]: Couldn't find '/usr/share/ollama/.ollama/id_ed25519'. Generating new private key.
Aug 29 12:10:07 test-k8s ollama[6124]: Your new public key is:
Aug 29 12:10:07 test-k8s ollama[6124]: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAFIhK+O72OW92AqaYzWzKYtr2w+HfKGAQvWiQOBIDop
Aug 29 12:10:07 test-k8s ollama[6124]: time=2025-08-29T12:10:07.673Z level=INFO source=routes.go:1331 msg="server config" env="map[CUDA_VISIBLE_DEVICES: GPU_DEVICE_ORDINAL: HIP_>
Aug 29 12:10:07 test-k8s ollama[6124]: time=2025-08-29T12:10:07.674Z level=INFO source=images.go:477 msg="total blobs: 0"
Aug 29 12:10:07 test-k8s ollama[6124]: time=2025-08-29T12:10:07.674Z level=INFO source=images.go:484 msg="total unused blobs removed: 0"
Aug 29 12:10:07 test-k8s ollama[6124]: time=2025-08-29T12:10:07.674Z level=INFO source=routes.go:1384 msg="Listening on 127.0.0.1:11434 (version 0.11.8)"
Aug 29 12:10:07 test-k8s ollama[6124]: time=2025-08-29T12:10:07.674Z level=INFO source=gpu.go:217 msg="looking for compatible GPUs"
Aug 29 12:10:07 test-k8s ollama[6124]: time=2025-08-29T12:10:07.684Z level=INFO source=gpu.go:379 msg="no compatible GPUs were discovered"
Aug 29 12:10:07 test-k8s ollama[6124]: time=2025-08-29T12:10:07.684Z level=INFO source=types.go:130 msg="inference compute" id=0 library=cpu variant="" compute="" driver=0.0 nam>
Чтобы запустить модель, ее необходимо предварительно скачать:
root@test-k8s:~# ollama pull mistral
pulling manifest
pulling f5074b1221da: 100% ▕████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▏ 4.4 GB
pulling 43070e2d4e53: 100% ▕████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▏ 11 KB
pulling 1ff5b64b61b9: 100% ▕████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▏ 799 B
pulling ed11eda7790d: 100% ▕████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▏ 30 B
pulling 1064e17101bd: 100% ▕████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▏ 487 B
verifying sha256 digest
writing manifest
success
Теперь я могу запустить модель в интерактивном режиме:
root@test-k8s:~# ollama run mistral
>>> Send a message (/? for help)
Модели можно задать любые вопросы, но надо учитывать, что ответы она генерирует не мгновенно, а постепенно. Для выхода нажимаю Ctrl + D. Далее можно убедиться, что после выхода из интерактивного сеанса модель все еще запущена:
root@test-k8s:~# ollama ps
NAME ID SIZE PROCESSOR CONTEXT UNTIL
mistral:latest 6577803aa9a0 5.3 GB 100% CPU 4096 4 minutes from now
Проверка работы через API
Я хочу пользоваться развернутой моделью, как ChatGPT: через postman, sdk или curl. Или хотя бы дать возможность пользоваться ею нескольким пользователям (может кому-то из моих коллег это будет интересно). Этот функционал реализуется через веб-сервер, который уже встроен в ollama server. Можно задать вопрос модели, например, таким образом:
root@test-k8s:~/ollama# curl http://localhost:11434/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "mistral",
"messages": [{"role": "user", "content": "Hello, who are you?"}]
}'
{"id":"chatcmpl-166","object":"chat.completion","created":1756470355,"model":"mistral","system_fingerprint":"fp_ollama","choices":[{"index":0,"message":{"role":"assistant","content":" I am a model of an artificial intelligence, trained to provide responses and interact with users as if it were a conversation partner.\n\nHow can I help you today? Is there anything specific you would like to talk about or learn more about?"},"finish_reason":"stop"}],"usage":{"prompt_tokens":10,"completion_tokens":50,"total_tokens":60}}


Если надо взаимодействовать с локальным аналогом ChatGPT из какой-то автоматизации или встроить модель в свое приложение, то это тоже легко сделать. Попробую запустить локальный Python-скрипт, который отправит запрос в модель и распечатает вывод. И сделаю все по науке: самым первым делом устанавлияю пакет, позволяющий создавать так называемые виртуальные окружение Python:
root@test-k8s:~# apt install apt install python3.12-venv
Далее я создаю пустое python-окружение в текущем каталоге /root:
root@test-k8s:~# python3 -m venv .
Но этого еще недостаточно — необходимо это самое python-окружение активировать:
root@test-k8s:~# source ./bin/activate
В процессе активации будет видно, что приглашение изменилось:
(root) root@test-k8s:~#
Внутри виртуального окружения можно смело экспериментировать и устанавливать различные пакеты Python, не боясь, что при этом сломается хостовая среда или другие виртуальные окружения на том же хосте. Сразу же в это окружение устанавливаю привязки для OpenAI:
(root) root@test-k8s:~# pip install openai
Collecting openai
Downloading openai-1.102.0-py3-none-any.whl.metadata (29 kB)
Collecting anyio<5,>=3.5.0 (from openai)
Downloading anyio-4.10.0-py3-none-any.whl.metadata (4.0 kB)
Collecting distro<2,>=1.7.0 (from openai)
Downloading distro-1.9.0-py3-none-any.whl.metadata (6.8 kB)
Collecting httpx<1,>=0.23.0 (from openai)
Downloading httpx-0.28.1-py3-none-any.whl.metadata (7.1 kB)
Collecting jiter<1,>=0.4.0 (from openai)
Downloading jiter-0.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (5.2 kB)
Collecting pydantic<3,>=1.9.0 (from openai)
Downloading pydantic-2.11.7-py3-none-any.whl.metadata (67 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 68.0/68.0 kB 10.3 MB/s eta 0:00:00
Collecting sniffio (from openai)
Downloading sniffio-1.3.1-py3-none-any.whl.metadata (3.9 kB)
Collecting tqdm>4 (from openai)
Downloading tqdm-4.67.1-py3-none-any.whl.metadata (57 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 57.7/57.7 kB 10.5 MB/s eta 0:00:00
Collecting typing-extensions<5,>=4.11 (from openai)
Downloading typing_extensions-4.15.0-py3-none-any.whl.metadata (3.3 kB)
Collecting idna>=2.8 (from anyio<5,>=3.5.0->openai)
Downloading idna-3.10-py3-none-any.whl.metadata (10 kB)
Collecting certifi (from httpx<1,>=0.23.0->openai)
Downloading certifi-2025.8.3-py3-none-any.whl.metadata (2.4 kB)
Collecting httpcore==1.* (from httpx<1,>=0.23.0->openai)
Downloading httpcore-1.0.9-py3-none-any.whl.metadata (21 kB)
Collecting h11>=0.16 (from httpcore==1.*->httpx<1,>=0.23.0->openai)
Downloading h11-0.16.0-py3-none-any.whl.metadata (8.3 kB)
Collecting annotated-types>=0.6.0 (from pydantic<3,>=1.9.0->openai)
Downloading annotated_types-0.7.0-py3-none-any.whl.metadata (15 kB)
Collecting pydantic-core==2.33.2 (from pydantic<3,>=1.9.0->openai)
Downloading pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (6.8 kB)
Collecting typing-inspection>=0.4.0 (from pydantic<3,>=1.9.0->openai)
Downloading typing_inspection-0.4.1-py3-none-any.whl.metadata (2.6 kB)
Downloading openai-1.102.0-py3-none-any.whl (812 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 812.0/812.0 kB 84.9 MB/s eta 0:00:00
Downloading anyio-4.10.0-py3-none-any.whl (107 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 107.2/107.2 kB 21.8 MB/s eta 0:00:00
Downloading distro-1.9.0-py3-none-any.whl (20 kB)
Downloading httpx-0.28.1-py3-none-any.whl (73 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 73.5/73.5 kB 14.6 MB/s eta 0:00:00
Downloading httpcore-1.0.9-py3-none-any.whl (78 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 78.8/78.8 kB 16.5 MB/s eta 0:00:00
Downloading jiter-0.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (352 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 352.0/352.0 kB 64.4 MB/s eta 0:00:00
Downloading pydantic-2.11.7-py3-none-any.whl (444 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 444.8/444.8 kB 72.0 MB/s eta 0:00:00
Downloading pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.0 MB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.0/2.0 MB 122.9 MB/s eta 0:00:00
Downloading sniffio-1.3.1-py3-none-any.whl (10 kB)
Downloading tqdm-4.67.1-py3-none-any.whl (78 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 78.5/78.5 kB 16.7 MB/s eta 0:00:00
Downloading typing_extensions-4.15.0-py3-none-any.whl (44 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 44.6/44.6 kB 8.3 MB/s eta 0:00:00
Downloading annotated_types-0.7.0-py3-none-any.whl (13 kB)
Downloading idna-3.10-py3-none-any.whl (70 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 70.4/70.4 kB 13.9 MB/s eta 0:00:00
Downloading typing_inspection-0.4.1-py3-none-any.whl (14 kB)
Downloading certifi-2025.8.3-py3-none-any.whl (161 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 161.2/161.2 kB 24.4 MB/s eta 0:00:00
Downloading h11-0.16.0-py3-none-any.whl (37 kB)
Installing collected packages: typing-extensions, tqdm, sniffio, jiter, idna, h11, distro, certifi, annotated-types, typing-inspection, pydantic-core, httpcore, anyio, pydantic, httpx, openai
Successfully installed annotated-types-0.7.0 anyio-4.10.0 certifi-2025.8.3 distro-1.9.0 h11-0.16.0 httpcore-1.0.9 httpx-0.28.1 idna-3.10 jiter-0.10.0 openai-1.102.0 pydantic-2.11.7 pydantic-core-2.33.2 sniffio-1.3.1 tqdm-4.67.1 typing-extensions-4.15.0 typing-inspection-0.4.1
Создаю минимально рабочий прототип приложения в файле main.py:
from openai import OpenAI
client = OpenAI(base_url="http://127.0.0.1:11434/v1", api_key="not-needed")
response = client.chat.completions.create(
model="mistral",
messages=[{"role": "user", "content": "Напиши шутку про Python"}]
)
print(response.choices[0].message["content"])
и запускаю его:
(root) root@test-k8s:~# python3 main.py
Почему Python так любит песок? Потому что он называется Py и Тон!
(Эта шутка возникла благодаря аналогии между вызовом функции на питонском языке, например Python.append([1, 2, 3]), где «Py» — это префикс Python, и Тон — это имя персонажа из мультфильма «Шестеро монстров» с приставкой «-тон», так что «Pyton-тон» звучит как «песок» на русском языке.)
Вот таким образом я развернул локальный аналог ChatGPT на сервере. Теперь можно экспериментировать: подключать API к своим приложениям, пробовать другие модели или расширять функциональность, например поиск по документам или интеграцию в корпоративные сервисы.
Добавление графического интерфейса
Чего мне не хватает — это графического интерфейса через браузер, потому что отправлять запросы через postman или curl не очень удобно. А тем более без удобного интерфейса не получится дать доступ к модели другим пользователям.
Возможным интерфейсом по умолчанию для моделей LLM является проект Open WebUI. Я запущу Open WebUI на подготовленном сервере. Самым удобным способом будет запуск через docker — это средство управления и запуска контейнерами на ОС Linux.
Для установки docker воспроизведу шаги, которые описаны в официальной инструкции:
root@test-k8s:~# apt-get update
root@test-k8s:~# apt-get install ca-certificates curl
root@test-k8s:~# install -m 0755 -d /etc/apt/keyrings
root@test-k8s:~# curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
root@test-k8s:~# chmod a+r /etc/apt/keyrings/docker.asc
root@test-k8s:~# echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
tee /etc/apt/sources.list.d/docker.list > /dev/null
root@test-k8s:~# apt-get update
root@test-k8s:~# apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Далее запускаю сам Open WebUI:
root@test-k8s:~# docker run -d --network host ghcr.io/open-webui/open-webui:latest
Эта команда запускает Open WebUI с параметрами по умолчанию и на порту 8080. Таким образом я могу зайти на панель по адресу сервера и этому порту:

При первом входе устанавливаю логин и пароль.
Далее при переходе в настройки можно увидеть, что значение по умолчанию для подключения к Ollama API неверное. Его необходимо изменить на http://localhost:11434 и сохранить настройки.

Затем, после возврата в режим диалога, видно, что выбрана нейросеть, которую я запустил ранее:

И можно с ней попробовать пообщаться в интерфейсе, очень похожем на ChatGPT:

Ввожу запрос…

…и получаю ответ от нейронной сети в режиме реального времени.
Следующие шаги для продакшна
Чтобы это учебное решение превратить в хоть сколько-нибудь готовое к промышленной эксплуатации, нужно будет сделать множество вещей, но первый шаг я уже сделал.
Отмечу некоторые нюансы, которые необходимо будет сделать:
Установить реверс прокси вроде nginx, настроить домен, установить сертификат SSL/TLS и настроить работу по этим шифрованным протоколам. С 80-го порта сделать переадресацию на 443.
Настроить мониторинг приложений на сервере.
Добавить к контейнеру с OpenWeb UI так называемый volume, в котором будут храниться данные. Сейчас же при перезапуске контейнера его состояние сбрасывается до заводского.
Подумать о безопасности: настроить правильным образом API-токены на стороне Ollama, настроить пользователей как на самом сервере, так и в приложении OpenWeb UI.
Одним из дальнейших шагов мог бы быть переход с виртуальной машины на платформу Kubernetes, благо Ollama прекрасно работает и на ней. А в облаке для этого есть отдельный сервис Managed Kubernetes. Или может быть другой вариант: сервис с доступом к популярным open source моделям, в котором можно удобно запускать в облаке различные LLM.
Комментарии (7)

SurMaster
27.10.2025 15:46И сколько по времени она генерирует ответ на cpu? По полчаса "привет" пишет? Там же уснуть можно пока ответит. Не говоря уж о том, чтобы с ней несколько юзеров работать будут...

LittleMeN
27.10.2025 15:46После фразы:
Вообще я немного слукавил в заголовке
Можно не читать. Как чисто технически разворачивать ollama, openwebui — наверное многим может быть интересно/полезно. Однако поддержу комментарии выше!
Ожидал увидеть GPU решение, что кто-то нашел способ уложиться в 10$/мес, но увы слукавил. Инференс на CPU для сценария чат с моделью — это полный бред.
Предложенное решение может вменяемо обрабатывать какую-то малую задачу, с очередью сообщений, для которой не требуется минимальная задержка.
По итогу что? Кликбейтный заголовок для рекламы хостинга?

ex_ineris
27.10.2025 15:46Ну или хотя бы для разворачивания дома на каком нибудь мини ПС + google coral, а так да это кликбейт.
SlavikF
"свой ChatGPT" с моделью на 2B - это даже не очень смешно.
Боле-менее вменяемые результаты модели выдают начиная от 30B.
Сравнимо с ChatGPT - это от 120B и выше.
Ollama - оно вроде бы и работает, но это довольно костыльная обёртка вокруг llama.cpp. Если осилите, то лучше уже сразу запускать llama.cpp
sctmnjoe
этого достаточно для спора с училкой - будет подсказывать ответы на: как правильно ирак или иран & etc
sic
Может что-то изменилось, но год назад я не мог найти ни одной текстовой модели, которая в 12 ГБ vram влезала и давала бы хотя бы более-менее связные ответы на простейшие вопросы, которые на Википедии разжеваны. Модели уровня 4-8гб вообще "сами с собой разговаривали", иероглифы вставляли, язык меняли.