Привет!
Нужен 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. Установка пакетов на VPS (Ubuntu)
Устанавливаем службы 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
2. Установка AnythingLLM через Docker Compose
Создаем рабочую директорию и конфигурационный файл:
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
Обратите внимание на строчку "127.0.0.1:3001:3001" — так мы изолируем контейнер от внешнего мира. Зачем? Правильно! Мы будем использовать Nginx и Certbot для SSL-сертификата нашей веб-морды.
Осталось только запустить контейнер. Находясь в папке ~/anythingllm, выполняем:
docker compose up -d
3. Настройка IPsec (swanctl) на VPS
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" } }
4. Настройка MikroTik
Сначала настроим дефолтные профили 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"
5. Настройка Ollama на домашнем ПК
Скачать и установить Ollama совсем не сложно. На официальном сайте прямо на главной странице дана команда для PowerShell:
irm https://ollama.com/install.ps1 | iex
После установки скачиваем необходимые модели. Для генерации текста отлично подойдёт, например, qwen2.5:9b (или выше, в зависимости от вашей памяти), а для обработки входящих документов и построения эмбеддингов — 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.
6. Настройка клиента L2TP и PPP на VPS
Редактируем файл /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 бесконечно, даже если домашний интернет упал на всю ночь.
7. Настройка порядка запуска демонов при старте Ubuntu
Пока тестировал схему, столкнулся с проблемой потери 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
8. Настройка UFW на VPS
Разрешаем в файрволе необходимые порты для управления сервером и работы веб-интерфейса, после чего активируем его:
sudo ufw allow 22/tcp sudo ufw allow 80/tcp sudo ufw allow 443/tcp sudo ufw enable
Перезапускаем службы сети и туннелей, чтобы все правила применились в чистом виде:
sudo systemctl restart strongswan-swanctl xl2tpd
9. Безопасность веб-интерфейса AnythingLLM (Nginx + SSL)
Поскольку в 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. Не забываем настроить и модель эмбеддингов в соседней вкладке — выбираем Embedding Preference -> Ollama, адрес тот же, модель nomic-embed-text. Также на этапе продакшена можно поиграться с размером чанка (Chunk size) и количеством одновременно обрабатываемых батчей.
10. Скрипт-сторож (Watchdog) для динамического IP
Так как домашний провайдер выдает динамический 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
По этой логике он будет выполнять 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 под полным контролем компании.
powerman
Жестоко. На этой карточке можно на приличной скорости использовать Qwen3.6-35B-A3B.
vadimspriggan Автор
Я использовал ранее qwen2.5:14b, но здесь привёл пример менее требовательный. Не у всех есть 16ГБ VRAM.
powerman
Там дело не столько в VRAM, сколько в правильном инструменте и технологии. У меня она выдаёт 34 t/s на 3060 Ti 8 GB - на Вашей карте должно быть заметно лучше, вполне возможно что даже Qwen-3.6-27B потянет.
vadimspriggan Автор
Спасибо за наводку. Я пока не смотрел в сторону этих больших моделей. Попробую!