MAATRIX / Блог / Мультибот: несколько ботов на одном сервере

Мультибот: несколько ботов на одном сервере

Мультибот: несколько ботов на одном сервере

MAATRIX

Рано или поздно у вас набирается три-пять ботов, и держать под каждый отдельный VPS становится дорого и бессмысленно — большинство ботов почти не грузят сервер. Проблема в другом: без правильной архитектуры один упавший или зациклившийся бот утаскивает за собой остальные, а разбираться, чей это лог, превращается в квест. Разберём, как разложить несколько ботов на одном сервере так, чтобы они жили независимо друг от друга.

Два подхода: systemd-сервисы или Docker-контейнеры

Есть два рабочих способа изолировать ботов на одном VPS, и оба используются на практике. Первый — каждый бот как отдельный systemd-сервис: обычный процесс Python или Node.js, запущенный юнитом, с собственным виртуальным окружением и собственным пользователем. Второй — каждый бот в своём Docker-контейнере со своим образом, зависимостями и лимитами.

Разница ощущается не в том, «работает или нет» — работают оба варианта надёжно, — а в удобстве эксплуатации и степени изоляции:

Критерийsystemd-сервисыDocker-контейнеры
Изоляция зависимостейОбщий Python/Node на хосте, версии конфликтуютПолная — у каждого бота свой образ
Расход RAM на «обвязку»Минимальный, чистый процесс+20–50 МБ на контейнер сверху
Порог входаНиже, если уже знаете systemdНужно знать Docker и compose
Деплой нового ботаСкопировать код, создать unitdocker compose up -d, воспроизводимо
Откат к прошлой версииВручную, через git/бэкап кодаdocker run со старым тегом образа
Лимиты CPU/RAM «из коробки»Через systemd (MemoryMax, CPUQuota)Через compose (mem_limit, cpus)
Изоляция при паденииПроцесс падает — рестарт systemd, соседей не трогаетКонтейнер падает — рестарт Docker, соседей не трогает

Если у вас 2-3 бота на одном языке и стеке, без сложных зависимостей — systemd даёт меньше накладных расходов и проще в отладке (обычный journalctl, обычный процесс). Если ботов пять и больше, они на разных версиях Python/Node или вы часто деплоите новые — Docker окупает свою сложность воспроизводимостью и чистой изоляцией зависимостей. Многие держат смесь: старые проверенные боты на systemd, новые — сразу в контейнерах.

Systemd-сервисы: практическая настройка

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

sudo useradd -r -s /usr/sbin/nologin -d /opt/bots/weatherbot botweather
sudo mkdir -p /opt/bots/weatherbot
sudo chown botweather:botweather /opt/bots/weatherbot

Разворачиваете код и создаёте отдельное виртуальное окружение под конкретного бота — это как раз то, что решает проблему конфликта версий библиотек между ботами:

sudo -u botweather python3 -m venv /opt/bots/weatherbot/venv
sudo -u botweather /opt/bots/weatherbot/venv/bin/pip install -r /opt/bots/weatherbot/requirements.txt

Юнит-файл /etc/systemd/system/bot-weather.service:

[Unit]
Description=Weather Telegram Bot
After=network.target

[Service]
Type=simple
User=botweather
Group=botweather
WorkingDirectory=/opt/bots/weatherbot
Environment=BOT_TOKEN_FILE=/opt/bots/weatherbot/.env
ExecStart=/opt/bots/weatherbot/venv/bin/python bot.py
Restart=on-failure
RestartSec=5

# лимиты ресурсов — ключевая часть изоляции
MemoryMax=300M
CPUQuota=50%

[Install]
WantedBy=multi-user.target

Так для каждого бота: bot-weather.service, bot-support.service, bot-shop.service — со своими лимитами под каждую задачу. Включаете и запускаете:

sudo systemctl daemon-reload
sudo systemctl enable --now bot-weather.service
sudo systemctl status bot-weather.service

Если у вас уже есть один бот на Python и вы разворачиваете его с нуля, шаги установки окружения и первого юнита подробно разобраны в статье про запуск телеграм-бота на Python на VPS — тот же принцип, просто повторённый для нескольких ботов с разными именами юнитов и папок.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать VPS

Docker-контейнеры: практическая настройка

Для нескольких контейнеров удобнее один docker-compose.yml на все боты сразу — так видно всю мультибот-конфигурацию в одном файле:

version: "3.9"
services:
  bot-weather:
    build: ./weatherbot
    container_name: bot-weather
    restart: unless-stopped
    env_file: ./weatherbot/.env
    mem_limit: 300m
    cpus: "0.5"
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"

  bot-support:
    build: ./supportbot
    container_name: bot-support
    restart: unless-stopped
    env_file: ./supportbot/.env
    mem_limit: 500m
    cpus: "0.5"
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"

  bot-shop:
    build: ./shopbot
    container_name: bot-shop
    restart: unless-stopped
    env_file: ./shopbot/.env
    mem_limit: 400m
    cpus: "0.7"
    depends_on:
      - db
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"

  db:
    image: postgres:16
    container_name: bots-db
    restart: unless-stopped
    mem_limit: 512m
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:

Каждый бот — свой каталог с Dockerfile и requirements.txt, свой .env с токеном, свои лимиты mem_limit/cpus под конкретную нагрузку. Общая база — отдельный сервис, если несколько ботов действительно должны делить данные; если нет, каждому боту логичнее свою SQLite или свою схему в общей базе. Поднимаете всё разом:

docker compose up -d
docker compose ps

Обновление одного бота не трогает остальные:

docker compose build bot-shop
docker compose up -d bot-shop

Разделение ресурсов: чтобы один бот не положил остальные

Главный риск мультибот-сервера — не падение бота (systemd и Docker сами перезапустят упавший процесс), а бот, который сжирает всю память или процессор и душит соседей. Причины типичны: утечка памяти в долго живущем процессе, бесконечный цикл из-за бага в обработке апдейтов, resize или обработка тяжёлых файлов без ограничений.

Лимиты из примеров выше — не формальность, а страховка. MemoryMax в systemd и mem_limit в Docker обрывают процесс при превышении, а не дают ему забрать всю оперативную память сервера и уронить через OOM killer заодно и соседних ботов. CPUQuota/cpus не дают одному боту с тяжёлой задачей (обработка изображений, локальные вычисления) забить все ядра так, что остальные боты начнут захлёбываться на входящих сообщениях.

Практическое правило распределения на VPS с 2 ГБ RAM и 2 ядрами под 4-5 лёгких ботов: закладывайте каждому 200-400 МБ RAM и 0.3-0.5 CPU, оставляя 300-500 МБ и заведомо больше половины CPU-мощности в резерве системе, СУБД и на всплески нагрузки. Не раздавайте лимиты «под ноль» от общего объёма — сервер без запаса начинает тормозить целиком при любом скачке у одного бота.

Если у одного бота задача тяжелее остальных — он обрабатывает медиа, работает с внешним API с задержками, ведёт активную базу, — не бойтесь дать ему лимит заметно выше, а лёгким ботам-уведомителям срезать до минимума. Смысл не в равенстве, а в том, чтобы сумма лимитов не превышала реальные ресурсы сервера с запасом, и чтобы каждый бот физически не мог выйти за отведённые ему рамки.

Логи: отдельно для каждого бота

Общий журнал на все боты — то, из-за чего расследование инцидента превращается в мучение. Решение простое: у каждого бота свой источник логов, который можно смотреть и ротировать независимо.

Для systemd-сервисов это получается само по себе — journalctl фильтрует по юниту:

journalctl -u bot-weather.service -f
journalctl -u bot-support.service --since "1 hour ago"

Если предпочитаете плоские файлы, направьте вывод каждого бота в свой лог и настройте отдельный logrotate-конфиг на файл, чтобы логи одного активного бота не забивали диск и не мешали читать логи остальных:

# /etc/logrotate.d/bot-weather
/opt/bots/weatherbot/bot.log {
    daily
    rotate 7
    compress
    missingok
    notifempty
}

Про саму ротацию и типичные грабли с растущими логами подробно — в статье про ротацию логов, чтобы не забивался диск.

Для Docker-контейнеров изоляция логов встроена по умолчанию: docker logs bot-shop покажет только этого бота, а параметры max-size/max-file в примере compose-файла выше не дадут логу одного контейнера разрастись бесконтрольно. Если хотите единую точку просмотра логов по всем ботам сразу — не смешивая их физически — рассмотрите Grafana Loki с метками по имени контейнера или юнита; каждый бот остаётся отдельным источником, а дашборд просто показывает их рядом.

Когда одного VPS уже не хватает

Мультибот-архитектура на одном сервере работает отлично, пока суммарная нагрузка укладывается в его ресурсы с запасом. Ориентировочные пороги, по которым стоит начинать думать о расширении (это именно ориентир — у вас цифры будут отличаться в зависимости от того, что делают конкретные боты):

  • RAM устойчиво выше 80% при нормальной работе, без пиков. Если в обычный день, без всплесков активности, память забита под завязку — новому боту уже некуда встать, а старым не хватает запаса на скачки нагрузки.
  • CPU load average выше числа ядер продолжительное время, а не только в моменты обработки тяжёлой задачи одним ботом.
  • Три и больше бота одновременно упираются в свои лимиты и начинают тормозить или получать MemoryMax/OOM-килы — сигнал, что лимиты уже не «страховка на всякий случай», а постоянный потолок.
  • Один бот вырос настолько, что его пиковая нагрузка (обработка медиа, локальные модели, тяжёлая база) регулярно давит на остальных, даже с лимитами. Такому боту логичнее отдельный сервер, а не место в общей мультибот-конфигурации.

Первый шаг при упоре в потолок — не сразу новый сервер, а вертикальное масштабирование: увеличить VPS по CPU/RAM в панели, это обычно занимает минуты и без переноса данных. Второй шаг, если апгрейд одного сервера больше не оправдан по деньгам или упирается в железо, — развести ботов по нескольким VPS: например, тяжёлые и лёгкие боты отдельно, или по проектам. Когда счёт идёт на десятки ботов и стабильную нагрузку, а не редкие пики, есть смысл сравнить это с выделенным сервером — разница по деньгам и по гарантированным ресурсам разобрана в статье про то, когда пора переезжать с VPS на выделенный сервер.

Перед тем как расширяться, полезно свериться с ориентирами по ресурсам под ботов вообще — статья сколько ресурсов нужно VPS для телеграм-ботов даёт базовые цифры на один бот, которые можно умножить и прикинуть, сколько ботов реально влезает в конкретный тариф.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать VPS

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Что проще для новичка: systemd или Docker?

Systemd, если вы уже знакомы с юнитами и хотите минимум накладных расходов. Docker требует освоить сам инструмент, но окупается на 5+ ботах воспроизводимостью и чистой изоляцией зависимостей.

Можно ли смешивать systemd-боты и Docker-боты на одном сервере?

Да, это рабочая практика: старые проверенные боты остаются на systemd, новые сразу разворачиваются в контейнерах. Единственное, за чем нужно следить, — суммарные лимиты обоих типов не должны превышать реальные ресурсы сервера.

Нужна ли отдельная база данных для каждого бота?

Не обязательно. Лёгким ботам достаточно своей SQLite. Общую PostgreSQL стоит заводить, только если боты реально должны делить данные, и в этом случае лимитировать её отдельно от ботов.

Что делать, если один бот регулярно упирается в лимит памяти?

Сначала проверить код на утечки и бесконечные циклы — это частая причина. Если нагрузка действительно легитимная, поднять лимит именно этому боту за счёт резерва сервера, а не за счёт лимитов соседей.

Как понять, что пора переносить бота на отдельный сервер?

Когда его пиковая нагрузка регулярно давит на остальных ботов даже с выставленными лимитами, или когда сам сервер устойчиво упирается в CPU/RAM без пиков от других процессов.