- BrainTools - https://www.braintools.ru -
Привет!
Нужен LLM? Если для себя лично можно как-то извернуться и купить подписку на ChatGPT, Claude или Gemini, то для бизнеса это не так-то просто. И я сейчас говорю не про зарубежные карточки, а про разделение доступа и локальное хранение чувствительных данных компании.
Передо мной встала задача обеспечить моим клиентам локальную LLM, в которую можно закидывать любые документы и получать по ним ответы. Как водится, бюджета на это не выделили. Нужна демоверсия в боевом режиме для нескольких сотрудников, чтобы оценить профит.
Я подготовил эту инструкцию для сисадминов, которые по ней смогут настроить рабочую версию RAG-системы на базе открытого ПО для небольшой компании. Сразу скажу, что для себя лично вполне можно обойтись без серверной версии AnythingLLM, она нужна только для реализации многопользовательского режима. Десктопный AnythingLLM даст вам и вашим домашним доступ одной галочкой.
Я принял решение купить обычный VPS, а в качестве вычислительного ядра использовать свою домашнюю 4060ti на 16 ГБ. На моём домашнем сервере уже развёрнута Ollama с целым набором моделей. Его связь с миром обеспечивает MikroTik 4011 с гигабитным каналом и белым динамическим IP.
Дальше нам остаётся только правильно развернуть AnythingLLM на VPS и подключить его к вычислительному ядру. Об этом и пойдёт речь ниже.
Установка пакетов на VPS (Ubuntu)
Установка AnythingLLM через Docker Compose
Настройка IPsec (swanctl) на VPS
Настройка MikroTik
Настройка Ollama на домашнем ПК
Настройка клиента L2TP и PPP на VPS
Настройка порядка запуска демонов при старте Ubuntu
Настройка UFW на VPS
Безопасность веб-интерфейса AnythingLLM (Nginx + SSL)
Скрипт-сторож (Watchdog) для динамического IP
Пропустим этап первоначальной настройки VPS, об этом я писал в этой статье [1].
Устанавливаем службы VPN и необходимые плагины шифрования:
# Установка зависимостей туннеля
sudo apt update
sudo apt install -y strongswan-swanctl xl2tpd libstrongswan-standard-plugins libstrongswan-extra-plugins
# Установка Docker через официальный скрипт
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
Создаем рабочую директорию и конфигурационный файл:
mkdir -p /var/lib/anythingllm/storage
mkdir -p ~/anythingllm
cd ~/anythingllm
nano docker-compose.yml
Копируем туда следующий YAML-конфиг:
services:
anythingllm:
image: mintplexlabs/anythingllm
container_name: anythingllm
ports:
- "127.0.0.1:3001:3001"
environment:
- STORAGE_DIR=/app/server/storage
restart: unless-stopped
volumes:
- /var/lib/anythingllm/storage:/app/server/storage
- /var/lib/anythingllm/.env:/app/server/.env
Обратите внимание [2] на строчку "127.0.0.1:3001:3001" — так мы изолируем контейнер от внешнего мира. Зачем? Правильно! Мы будем использовать Nginx и Certbot для SSL-сертификата нашей веб-морды.
Осталось только запустить контейнер. Находясь в папке ~/anythingllm, выполняем:
docker compose up -d
AnythingLLM будет отправлять Ollama документы для анализа. Чтобы они проходили по защищённой трубе, поднимем туннель L2TP/IPsec по классике. Боялся, что всё это заблокировано, но нет: VPS в РФ, и всё работает стабильно.
Чтобы не платить провайдеру за статический IP, используем функцию DDNS MikroTik. Главное, чтобы IP был белым, а не находился за NAT.
Создаём файл конфигурации /etc/swanctl/conf.d/l2tp.conf.
connections {
myvpn {
version = 1
local_addrs = %defaultroute
remote_addrs = ДДНС_ДОМЕН_МИКРОТИКА
proposals = aes128-sha1-modp2048, aes256-sha256-modp2048
local {
auth = psk
}
remote {
auth = psk
}
children {
l2tp {
mode = transport
esp_proposals = aes128-sha1, aes256-sha256
local_ts = dynamic[udp/1701]
remote_ts = dynamic[udp/1701]
start_action = start
dpd_action = clear
}
}
}
}
secrets {
ike-mikrotik {
secret = "IPSEC_SECRET"
}
}
Сначала настроим дефолтные профили IPsec для полной совместимости со StrongSwan (IKEv1, modp2048).
В Winbox:
Открываем IP -> IPsec, переходим на вкладку Profiles, открываем профиль default. Устанавливаем Hash Algorithm: sha1, sha256, Encryption Algorithm: aes-128, aes-256, DH Group: modp2048.
Переходим на вкладку Proposals, открываем default. Выставляем Auth. Algorithms: sha1, sha256, Enc. Algorithms: aes-128 cbc, aes-256 cbc.
Через CLI:
/ip ipsec profile set [ find default=yes ] dh-group=modp2048 enc-algorithm=aes-128,aes-256 hash-algorithm=sha1,sha256
/ip ipsec proposal set [ find default=yes ] auth-algorithms=sha1,sha256 enc-algorithms=aes-128-cbc,aes-256-cbc
Теперь включаем L2TP-сервер с требованием обязательного IPsec.
В Winbox:
Переходим в раздел PPP, на вкладке Interface нажимаем кнопку L2TP Server. В открывшемся окне ставим галочку Enabled, в выпадающем списке Use IPsec выбираем required, а в поле IPsec Secret вписываем ваш секретный ключ. Default Profile оставляем default-encryption.
Через CLI:
/interface l2tp-server server set enabled=yes use-ipsec=required ipsec-secret="IPSEC_SECRET" default-profile=default-encryption
Создаём учётную запись пользователя для туннеля и жёстко фиксируем IP-адреса внутри него.
В Winbox:
В разделе PPP переходим на вкладку Secrets и нажимаем на «плюс». Заполняем поля: Name = ЛОГИН_L2TP, Password = ПАРОЛЬ_L2TP, Service = l2tp. В полях адресов строго прописываем: Local Address = 10.10.10.1, Remote Address = 10.10.10.2.
Через CLI:
/ppp secret add name="ЛОГИН_L2TP" password="ПАРОЛЬ_L2TP" service=l2tp local-address=10.10.10.1 remote-address=10.10.10.2
Я не хочу, чтобы с арендованного сервера был доступ в мою домашнюю локальную сеть, поэтому никакие маршруты на сервере мы не прописываем. На MikroTik в цепочке forward для безопасности стоит общее правило drop на все входящие из туннеля сессии.
Нам нужен только единственный порт 11434 на компьютере с Ollama. Настроим Destination NAT для безопасного проброса порта Ollama строго для IP-адреса VPS.
В Winbox:
Переходим в IP -> Firewall, вкладка NAT. Добавляем новое правило:
Вкладка General: Chain = dstnat, Src. Address = 10.10.10.2, Dst. Address = 10.10.10.1, Protocol = 6 (tcp), Dst. Port = 11434.
Вкладка Action: Action = dst-nat, To Addresses = IP_ДОМАШНЕГО_ПК.
Через CLI:
/ip firewall nat add chain=dstnat src-address=10.10.10.2 dst-address=10.10.10.1 dst-port=11434 protocol=tcp action=dst-nat to-addresses=IP_ДОМАШНЕГО_ПК comment="Forward Ollama from VPS"
Скачать и установить Ollama совсем не сложно. На официальном сайте прямо на главной странице дана команда для PowerShell:
irm https://ollama.com/install.ps1 | iex
После установки скачиваем необходимые модели. Для генерации текста отлично подойдёт, например, qwen2.5:9b (или выше, в зависимости от вашей памяти [3]), а для обработки входящих документов и построения эмбеддингов — nomic-embed-text:
ollama run qwen2.5:9b
ollama run nomic-embed-text
Важный нюанс: по умолчанию Ollama слушает только localhost. Нам нужно разрешить ей принимать внешние подключения по сети. Для этого в Windows создаём системную переменную окружения OLLAMA_HOST = 0.0.0.0 и перезапускаем службу Ollama. Также не забудьте в брандмауэре Windows разрешить входящие TCP-подключения на порт 11434.
Редактируем файл /etc/xl2tpd/xl2tpd.conf, указывая DDNS-домен роутера и автоматический дозвон:
[lac myvpn]
lns = ДДНС_ДОМЕН_МИКРОТИКА
ppp debug = yes
pppoptfile = /etc/ppp/options.l2tpd.client
length bit = yes
autodial = yes
Создаём файл опций авторизации /etc/ppp/options.l2tpd.client. Не ставьте таймаут, чтобы сессия не отваливалась по бездействию, а вот опцию persist нужно указать обязательно — она заставит систему держать туннель постоянно.
ipcp-accept-local
ipcp-accept-remote
refuse-eap
require-mschap-v2
noccp
noauth
noipdefault
novj
novjccomp
mtu 1410
mru 1410
persist
maxfail 0
name "ЛОГИН_L2TP"
password "ПАРОЛЬ_L2TP"
Пояснения по опциям конфигурации:
ipcp-accept-local и ipcp-accept-remote: Разрешают нашей Ubuntu принять те IP-адреса внутри туннеля, которые ей диктует MikroTik.
refuse-eap: Запрещает использовать небезопасный тип авторизации EAP.
require-mschap-v2: Жёсткое требование использовать для передачи пароля только протокол MS-CHAPv2.
noccp: Отключает протокол сжатия CCP. Трафик и так сжимается и шифруется на уровне IPsec.
noauth: Указывает, что сервер Ubuntu сам не требует авторизации от MikroTik.
noipdefault: Запрещает демону pppd использовать основной IP-адрес сервера для туннеля. Мы ждём адрес строго от роутера.
novj и novjccomp: Отключают алгоритм сжатия заголовков Ван Якобсона. В современных сетях он часто ломает маршрутизацию.
mtu 1410 и mru 1410: Уменьшают максимальный размер пакета (MTU/MRU), так как инкапсуляция в IPsec и L2TP забирает часть полезного размера пакета под свои заголовки.
persist: Заставляет демон автоматически переподключаться при любом случайном обрыве связи.
maxfail 0: Снимает лимит на количество неудачных попыток подключения. Сервер будет стучаться до MikroTik бесконечно, даже если домашний интернет упал на всю ночь.
Пока тестировал схему, столкнулся с проблемой потери ppp интерфейса. В логах MikroTik видел refuse на подключение без активного IPsec. Это происходило потому, что L2TP пытался пробиться раньше, чем демоны IPsec успевали установить защищённое соединение.
Чтобы избежать этой гонки служб, добавим принудительную зависимость и задержку запуска L2TP.
Выполняем команду sudo systemctl edit xl2tpd.service и в открывшемся буфере вставляем:
[Unit]
After=strongswan-swanctl.service
Requires=strongswan-swanctl.service
[Service]
ExecStartPre=/bin/sleep 5
Применяем изменения в systemd:
sudo systemctl daemon-reload
Разрешаем в файрволе необходимые порты для управления сервером и работы веб-интерфейса, после чего активируем его:
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
Перезапускаем службы сети и туннелей, чтобы все правила применились в чистом виде:
sudo systemctl restart strongswan-swanctl xl2tpd
Поскольку в docker-compose.yml мы изолировали контейнер, указав локальный хост 127.0.0.1:3001, пришло время настроить Nginx в качестве обратного прокси для доступа извне.
Устанавливаем веб-сервер и Certbot:
sudo apt install -y nginx certbot python3-certbot-nginx
Создаём конфигурационный файл виртуального хоста /etc/nginx/sites-available/anythingllm:
server {
listen 80;
server_name ТВОЙ_ДОМЕН_VPS;
location / {
proxy_pass http://127.0.0.1:3001;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Для долгих генераций ответов ИИ увеличим таймауты
proxy_read_timeout 300;
proxy_connect_timeout 300;
proxy_send_timeout 300;
}
}
Активируем сайт, проверяем конфигурацию и получаем автоматический SSL-сертификат от Let’s Encrypt:
sudo ln -s /etc/nginx/sites-available/anythingllm /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
sudo certbot --nginx -d ТВОЙ_ДОМЕН_VPS
Если всё прошло удачно, переходим в браузере по адресу вашего домена VPS. Подключение защищено, никаких предупреждений безопасности. При первичной настройке в веб-интерфейсе AnythingLLM выбираем многопользовательский режим (Multi-user) и создаём аккаунт администратора.
В настройках AnythingLLM переходим в конфигурацию ИИ и указываем Ollama Base URL как http://10.10.10.1:11434. Наш сервер тут же подтянет список моделей с домашнего ПК. В поле моделей выберите qwen2.5:9b. Не забываем [4] настроить и модель эмбеддингов в соседней вкладке — выбираем Embedding Preference -> Ollama, адрес тот же, модель nomic-embed-text. Также на этапе продакшена можно поиграться с размером чанка (Chunk size) и количеством одновременно обрабатываемых батчей.
Так как домашний провайдер выдает динамический IP, MikroTik обновляет свою запись DDNS. Однако запущенный демон xl2tpd на стороне VPS не умеет самостоятельно переопределять изменившийся IP-адрес закешированного домена. Напишем простой скрипт автоматического перезапуска туннеля.
Создаём файл /usr/local/bin/vpn-watchdog.sh:
#!/bin/bash
TARGET="10.10.10.1"
LOGFILE="/var/log/vpn-watchdog.log"
if ! ping -c 10 -W 5 $TARGET > /dev/null 2>&1; then
echo "$(date '+%Y-%m-%d %H:%M:%S') - Связь с $TARGET потеряна. Перезапуск туннеля..." >> $LOGFILE
systemctl restart strongswan-swanctl xl2tpd
fi
По этой логике [5] он будет выполнять 10 пингов с таймаутом 5 секунд, если все 10 провалятся, сервис будет перезапущен. Это убережёт нас от случайных сбоев интернета. Скрипт сработает, только если связь стабильно потеряна. Делаем его исполняемым:
sudo chmod +x /usr/local/bin/vpn-watchdog.sh
Добавляем задание в планировщик Cron для проверки каждые 5 минут. Создаем файл /etc/cron.d/vpn-watchdog:
*/5 * * * * root /usr/local/bin/vpn-watchdog.sh
Рабочее демо RAG-системы для заказчика всего за один вечер. Если клиенту понравится, мы оставляем ему этот настроенный VPS, и сотрудники безопасно работают через браузер — именно так, как они привыкли при использовании публичных LLM.
При этом архитектура полностью готова к масштабированию:
Ресурсы VPS (диск под базу данных, оперативную память под веб-сервер) можно увеличивать на лету у хостера.
Для огромного объема загружаемых документов всегда можно подключить внешнее S3-хранилище.
Если клиент созреет купить собственное железо в офис или арендовать выделенный сервер с GPU, мы просто перенаправим AnythingLLM на новый IP-адрес хоста. Ollama здесь не является эксклюзивным решением.
Вся обученная база данных и история переписок (embedded_db) остаются в целости на нашем VPS под полным контролем компании.
Автор: vadimspriggan
Источник [6]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/31352
URLs in this post:
[1] об этом я писал в этой статье: https://habr.com/ru/articles/977026/
[2] внимание: http://www.braintools.ru/article/7595
[3] памяти: http://www.braintools.ru/article/4140
[4] забываем: http://www.braintools.ru/article/333
[5] логике: http://www.braintools.ru/article/7640
[6] Источник: https://habr.com/ru/articles/1044140/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1044140
Нажмите здесь для печати.