Один VPS — кілька Telegram-ботів: ізоляція залежностей, RAM, CPU та логів

Кілька Telegram-ботів нормально працюють на одному VPS, якщо кожен із них можна окремо запустити, зупинити, оновити та продіагностувати. Проблеми починаються не від самої кількості процесів, а від відсутності меж між ними: спільні бібліотеки, незрозумілі PID, необмежене споживання пам’яті, змішані журнали та ручний запуск після перезавантаження сервера.

Для невеликої інфраструктури не потрібна складна оркестрація. Зазвичай достатньо чотирьох рівнів ізоляції: окремі залежності, окремий керований процес, контроль ресурсів і окремий канал діагностики. Якщо для конкретного бота за хвилину можна знайти його процес, RAM, CPU та помилки, система вже значно передбачуваніша.

Чому «просто запустити ще один bot.py» працює лише до першої проблеми

Окремий файл бота ще не означає ізольований сервіс. Якщо три проєкти запускаються під одним користувачем, використовують глобальний Python або Node.js, пишуть у спільний лог і стартують із різних tmux-вікон, то під час збою доводиться спочатку з’ясовувати, що взагалі відбулося.

Чому «просто запустити ще один bot.py» працює лише до першої проблеми

Типова картина проста: один бот працює місяцями, потім додається другий, третій, четвертий. Поки навантаження невелике, усе здається зручним. Після чергового pip install -U один процес отримує нову версію бібліотеки, інший падає з ImportError. Або один бот починає обробляти великі файли, забирає пам’ять, і на сервері зникає вже не він один.

Межі краще визначити до першої аварії. Для кожного бота має бути зрозуміло: який у нього runtime, де його залежності, який процес або сервіс за нього відповідає, скільки ресурсів він споживає і де лежать його помилки. Якщо хоча б на одне з цих питань відповідь звучить як «треба пошукати», ізоляція ще слабка.

ps -eo pid,cmd,%cpu,%mem --sort=-%mem
free -h
systemctl --type=service
journalctl -u bot-a

На практиці проблема часто починається не з нестачі потужності VPS, а з того, що після появи кількох процесів уже ніхто точно не знає, хто саме споживає пам’ять або пише помилки. Поки ботів два, це ще можна тримати в голові. Коли їх стає більше, потрібна структура.

Залежності потрібно ізолювати раніше, ніж вони почнуть конфліктувати

Для кількох Python-ботів кожному проєкту варто мати власний virtual environment, а для Node.js — окремий каталог із власними залежностями. Це не обмежує RAM чи CPU, зате прибирає один із найнеприємніших сценаріїв: оновлення одного проєкту змінює середовище іншого.

Залежності потрібно ізолювати раніше, ніж вони почнуть конфліктувати

Python: окремий venv для кожного бота

Базова структура проста: по одному каталогу на проєкт, а всередині — власний venv та файл із зафіксованими версіями пакетів:

/opt/bots/
  bot-a/
    venv/
    requirements.txt
    .env
  bot-b/
    venv/
    requirements.txt
    .env

Python: окремий venv для кожного бота
Для нового середовища достатньо:

cd /opt/bots/bot-a
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt

Після цього варто перевірити не лише список пакетів, а й те, який саме інтерпретатор використовується:

which python
python --version
pip freeze

Якщо which python показує шлях усередині /opt/bots/bot-a/venv/, залежності цього проєкту не змішуються з глобальним Python. Це особливо важливо, коли bot-a потребує старішої версії Telegram-бібліотеки, а bot-b уже переведений на новішу.

Node.js: власні node_modules і lock-файл

Для Node.js логіка така сама: кожен проєкт має свій package.json, package-lock.json і node_modules. Оновлювати залежності потрібно всередині конкретного каталогу, а не сприймати сервер як один великий JavaScript-проєкт.

cd /opt/bots/bot-b
node -v
npm list

Lock-файл тут не дрібниця. Він допомагає відтворити той самий набір версій після перенесення або повторного розгортання. Глобальний pip чи хаотичні глобальні npm-пакети на сервері з кількома проєктами — майже завжди майбутній сюрприз.

Кожен бот має бути окремим керованим процесом, а не вкладкою tmux

Окремий systemd service для кожного бота дає важливішу ізоляцію, ніж окрема директорія: процес можна незалежно стартувати, перезапускати, увімкнути автозапуск і одразу подивитися його журнал. Для Linux VPS із кількома довгоживучими ботами це часто найпростіша робоча схема.

Наприклад, мінімальний unit для Python-бота може виглядати так:

[Unit]
Description=Telegram bot A
After=network.target

[Service]
User=bot-a
WorkingDirectory=/opt/bots/bot-a
EnvironmentFile=/opt/bots/bot-a/.env
ExecStart=/opt/bots/bot-a/venv/bin/python /opt/bots/bot-a/bot.py
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

WorkingDirectory прибирає плутанину з відносними шляхами, ExecStart одразу прив’язує сервіс до потрібного venv, User не дає запускати застосунок від root без потреби, а Restart=on-failure повертає процес після аварійного завершення. Користувач bot-a має існувати й мати права на каталог проєкту та потрібні файли, але не обов’язково на каталоги сусідніх ботів.

Файл, вказаний через EnvironmentFile, має відповідати синтаксису systemd для змінних середовища. Звичайний dotenv-файл, який читає бібліотека застосунку, краще не підставляти сюди без перевірки: shell-конструкції або специфічний синтаксис можуть трактуватися інакше.

Якщо бот повинен працювати безперервно навіть після штатного виходу, політика перезапуску може бути іншою — її краще вибирати за поведінкою конкретного застосунку, а не копіювати навмання.

systemctl daemon-reload
systemctl enable --now bot-a
systemctl status bot-a
systemctl restart bot-a
journalctl -u bot-a

Після цього bot-a, bot-b і bot-c стають трьома окремими керованими одиницями. Перезапуск одного не вимагає закривати tmux, шукати потрібний PID або боятися зачепити сусіда. Процес, який треба «підняти руками», рано чи пізно забудуть підняти — особливо після reboot.

Один бот може з’їсти всю RAM: як побачити це до OOM Killer

Для VPS із кількома ботами небезпечне не просто високе споживання RAM, а процес, пам’ять якого неконтрольовано росте й залишає без ресурсу сусідів. Одне значення в top мало що говорить: стабільні 300 МБ і послідовне зростання 300 → 500 → 800 МБ — різні ситуації.

Як знайти процес, що споживає пам’ять

free -h
ps aux --sort=-%mem
top

Тут зазвичай і знаходиться перший підозрюваний. У списку процесів корисно дивитися на RSS — фактичну фізичну пам’ять, яку процес зараз тримає в RAM. VSZ показує ширший віртуальний адресний простір і часто виглядає значно страшніше, ніж реальне споживання, тому порівнювати ботів лише за VSZ не варто.

free -h також потрібно читати обережно. Linux активно використовує вільну пам’ять під файловий кеш, тому невелике поле free саме по собі не означає аварію. Дивитися треба на доступну пам’ять, swap і поведінку процесів у часі.

Як відрізнити нормальний пік від витоку

 

Бот може короткочасно брати більше RAM під час генерації звіту, розпакування архіву або обробки великого файлу. Якщо після завершення задачі споживання повертається до звичного рівня, це схоже на нормальний пік. Якщо RSS монотонно росте годинами, варто перевіряти кеші, черги, незакриті об’єкти й фонові задачі.

Симптом Що перевірити Ймовірна причина
RAM процесу постійно росте RSS того самого PID у динаміці Витік пам’яті або черга, що не очищається
Вільної RAM мало, але все працює free -h, available, swap Частину пам’яті займає кеш; аварії може не бути
Процес раптово зник Kernel journal Можливе завершення OOM Killer
Під час важкої задачі гальмують усі боти RAM до та під час задачі Короткий великий пік одного процесу

Що перевіряти після OOM

journalctl -k | grep -i -E "oom|killed process"
dmesg | grep -i -E "oom|killed process"
systemctl show bot-a -p MemoryCurrent -p MemoryMax

Якщо ядро завершило процес через нестачу пам’яті, збільшення VPS може бути одним із рішень, але не першим кроком. Спочатку варто зрозуміти, чому RAM росла. Для окремого systemd-сервісу можна задати MemoryMax, а в Docker — memory limit. Ліміт при цьому має залишати запас для нормальних піків: ставити межу рівно на середнє споживання — хороший спосіб отримати хибні аварії.

CPU потрібно контролювати окремо: 100% одного ядра — це ще не 100% VPS

CPU для Telegram-ботів потрібно оцінювати за процесами та піками, а не лише за середнім завантаженням VPS. Більшість звичайних ботів значну частину часу чекають мережеві операції, але парсинг, конвертація файлів, генерація зображень або невдалий цикл здатні повністю зайняти одне ядро.

top
uptime
ps -eo pid,cmd,%cpu,%mem --sort=-%cpu
systemd-cgtop

На сервері з кількома vCPU один однопотоковий процес може забити одне ядро, не показуючи повного завантаження всього VPS. Через це загальна цифра виглядає терпимо, а затримки інших ботів уже ростуть. На середньому графіку такий пік легко не помітити.

Окремо перевіряють load average та I/O wait. Якщо процеси багато часу чекають диск, додаткове CPU не усуне вузьке місце. Якщо ж саме bot-b регулярно забирає процесор під важку задачу, для systemd можна задати CPUQuota, а для контейнера — обмеження CPU. У systemd CPUQuota=100% відповідає приблизно одному повному CPU-ядру; на VPS із чотирма vCPU це не означає 100% усієї доступної процесорної потужності. nice змінює пріоритет планування, але не встановлює такої жорсткої межі.

Якщо під час роботи одного бота latency інших помітно погіршується, дивитися потрібно його %CPU саме в момент піку, а не середню цифру за день. Важку операцію іноді правильніше винести в окремий worker, ніж без кінця збільшувати ліміт усьому сервісу.

Логи мають відповідати на питання «що сталося саме з цим ботом»

Логи кількох ботів потрібно розділяти так, щоб помилки одного сервісу можна було переглянути без фільтрації спільного багатогігабайтного файлу. Для systemd найпростіший варіант — окремий unit для кожного бота й перегляд через journalctl -u.

journalctl -u bot-a --since "1 hour ago"
journalctl -u bot-a -f

Першою командою зручно перевірити недавні події, другою — дивитися журнал у реальному часі під час відтворення проблеми. Якщо застосунок пише власні файли логів, потрібна ротація через logrotate або інший контрольований механізм: обмеження розміру, кількості архівів і строку зберігання. Для journald так само варто перевірити політику retention, а не покладатися на нескінченне накопичення записів.

df -h
du -sh /var/log/*
du -sh /opt/bots/*

Перед тим як шукати проблему в Telegram API, варто банально перевірити df -h. Ситуація «бот завис» інколи виявляється просто заповненою файловою системою.

Для production не варто безконтрольно залишати рівень DEBUG. Він корисний під час діагностики, але створює багато шуму й швидко збільшує журнал. У записах мають бути час, рівень помилки, зрозумілий ідентифікатор задачі або update, але не Telegram token, паролі чи інші секрети. Лог, який ніхто не може швидко відфільтрувати, майже не допомагає під час аварії.

systemd чи Docker: який рівень ізоляції справді потрібен кільком ботам

Для двох-трьох простих власних ботів зв’язка окремий каталог + venv або node_modules + systemd часто вирішує завдання без зайвого шару. Docker стає особливо корисним, коли проєкти потребують різних runtime, системних бібліотек, відтворюваного середовища або чітко заданих ресурсних меж.

Критерій systemd + venv Docker Compose
2–3 прості боти Зазвичай достатньо Може бути надлишковим
Різні Python/Node runtime Можливо, але більше ручної роботи Зручно ізолювати образами
Різні системні бібліотеки Ізоляція слабша Ізоляція сильніша
Ліміти RAM/CPU Через systemd/cgroups Декларативно в конфігурації
Перенесення на інший VPS Середовище треба відтворити Простіше за наявності готових образів і compose-файлу
Діагностика для новачка Зазвичай простіша Потрібне розуміння Docker

Не варто контейнеризувати ботів лише тому, що «так сучасніше». Ізоляція має зменшувати кількість проблем, а не додавати ще один технологічний шар. Для трьох Python-ботів однієї команди systemd і venv можуть бути найпрозорішою схемою. Для п’яти проєктів на Python і Node.js із різними системними пакетами Docker Compose уже часто спрощує розгортання та ресурсні межі.

Коли ботів уже кілька й важливо бачити їх окремими керованими одиницями, можна використовувати середовище для запуску кількох Telegram-ботів; незалежно від способу керування принцип залишається тим самим: у кожного проєкту мають бути свої залежності, процес або контейнер, ресурсні межі та зрозумілі логи.

Спільні PostgreSQL, Redis або інші інфраструктурні сервіси не обов’язково дублювати для кожного контейнера. Ізолювати потрібно те, що реально може конфліктувати або створити неконтрольований вплив на сусідні застосунки.

Практична схема: як розмістити кілька ботів так, щоб їх можна було обслуговувати окремо

Найпростіший тест правильної ізоляції — можливість зупинити, оновити й запустити будь-якого бота, не впливаючи на решту. Це працює і для systemd, і для Docker Compose. Якщо для перезапуску bot-b доводиться торкатися bot-a та bot-c, межі між ними ще недостатньо чіткі.

  1. Створіть окремий каталог для кожного проєкту.
  2. Винесіть токени, паролі та іншу конфігурацію з коду в окреме захищене середовище або файл.
  3. Створіть власний venv, node_modules або контейнер для кожного бота.
  4. Зафіксуйте версії залежностей.
  5. Перевірте ручний запуск кожного проєкту окремо.
  6. Створіть окремий systemd service або Docker service.
  7. Налаштуйте автоматичний запуск і зрозумілу restart policy.
  8. Перевірте RAM і CPU кожного процесу під реальним навантаженням.
  9. За потреби встановіть ресурсні межі з запасом на короткі піки.
  10. Налаштуйте окремий перегляд логів і контроль їхнього розміру.
  11. Перевірте вільне місце на диску та поведінку після reboot.
  12. Навмисно перезапустіть один бот і переконайтеся, що інші не змінили стан.
/opt/bots/
  bot-a/
  bot-b/
  bot-c/

/etc/systemd/system/
  bot-a.service
  bot-b.service
  bot-c.service

Після налаштування варто пройти короткий аварійний тест. Зупинити один сервіс, перевірити відповіді інших, запустити його назад, знайти його PID, подивитися RAM, CPU та журнал. Хороша архітектура не гарантує, що бот ніколи не впаде. Вона гарантує інше: один бот упав — один бот і ремонтуємо.

Контрольний чек-лист

  • Кожен бот має окремий каталог і власні залежності.
  • Версії пакетів зафіксовані.
  • Секрети не зберігаються безпосередньо в коді та не потрапляють у логи.
  • Кожен бот має окремий сервіс або контейнер.
  • Один бот можна перезапустити, не зачіпаючи інші.
  • Відомо, скільки RAM використовує кожен процес і чи росте вона з часом.
  • Відомо, який процес створює CPU-піки.
  • Для кожного сервісу є окремий спосіб переглянути помилки.
  • Логи не можуть безконтрольно заповнювати диск.
  • Після reboot потрібні боти запускаються автоматично.
  • На VPS залишається запас пам’яті для ОС, файлового кешу та короткочасних піків.

Схема добра не кількістю контейнерів, користувачів чи unit-файлів, а чіткими межами між проєктами. Кожен бот має залишати достатньо даних для діагностики й не тягнути сусідів за собою під час збою. Тоді VPS перестає бути «чорною скринькою» з випадковими процесами й стає середовищем, яке можна нормально обслуговувати.

nBook - найцікавіше зі світу IT, Hi-Tech
Додати коментар