Еще недавно поиск в 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.nnHow 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.
Источник: habr.com



























