Как я прикрутил телеграм-бота на Python для мониторинга сервера
С чего я начал
Fail2ban я поставил, n8n подкрутил, но мне хотелось чего-то более гибкого. Конкретно: я хочу написать в Telegram боту «статус» — и получить ответ с загрузкой CPU, RAM, диском и аптаймом. Или написать «бекап» — и запустить резервное копирование баз данных.
n8n такое умеет, но мне надоело тыкать в блоки. Хотелось кода. Чистого, гибкого, без визуального редактора, где линии вечно разъезжаются.
Выбрал aiogram — асинхронную библиотеку для работы с Telegram API на Python. Почему её? Во-первых, она современная, на asyncio. Во-вторых, документация внятная. В-третьих, я уже писал на ней бота для комментариев на сайте — опыт был.
Ставлю aiogram и пишу первого бота
Первым делом — создать виртуальное окружение, чтобы не засорять систему. Я работаю на Ubuntu 24.04, Python 3.12 уже в комплекте:
python3 -m venv tg_monitor
source tg_monitor/bin/activate
pip install aiogram==3.x psutil aiofiles
psutil берёт системные метрики, aiofiles пригодится для логов. Сразу же уткнулся в проблему — aiogram 3.x сильно отличается от второй версии. Там новый API, хендлеры через декораторы, роутеры, и всё завязано на Dispatcher. Пришлось читать документацию с нуля.
Бот получился минимальный: токен берётся из переменной окружения, на команду /start отвечает приветствием, на /status — собирает метрики. Код я закинул в отдельную папку /opt/tg_monitor и завернул в systemd-сервис, чтобы стартовал при загрузке.
Команда /status — глаза сервера
Вот тут я развернулся. Команда шлёт запросы к psutil и возвращает сообщение с:
- CPU — загрузка в процентах по каждому ядру
- RAM — used/total с процентом
- Диск — корневой раздел, /var/log, /backup
- Аптайм — сколько сервер пашет без перезагрузки
- Топ-3 процесса по CPU
Оформил это в красивый Markdown-отчёт. Telegram его отображает — глаз радуется. Первая же отправка показала проблему: бот тупил. Ответ приходил через 5-6 секунд. Почему? psutil на VPS с 4 ядрами делает syscall, который на некоторых виртуализациях тормозит. Пришлось закешировать метрики на 30 секунд.
Обёртка простая:
from functools import lru_cache
import time
@lru_cache(maxsize=1)
def get_metrics(_cache_key):
# собираем всё за один проход
return {"cpu": psutil.cpu_percent(interval=1, percpu=True),
"ram": psutil.virtual_memory()._asdict(),
"disk": psutil.disk_usage('/')._asdict()}
В качестве _cache_key я передаю текущую минуту, округлённую до 30 секунд. Хитро, но работает.
Команда /backup — с риском для данных
Самая опасная функция. Бот запускает bash-скрипт, который дампит все базы MySQL, пакует в архив и кидает в /backup. Когда я первый раз добавил эту команду, я ночь не спал — вдруг кто-то угадает токен и дропнет базы. На всякий случай добавил:
- Ограничение по user_id — команду /backup может выполнить только мой Telegram ID
- Подтверждение через Inline-кнопку: «Точно запустить бекап?»
- Логирование всех вызовов в отдельный файл
aiogram позволяет сделать это элегантно. Фильтры навешиваются прямо на хендлер:
from aiogram.filters import Command
from aiogram.types import Message
@dp.message(Command("backup"), F.from_user.id == MY_ID)
async def backup_cmd(message: Message):
# запускаем через subprocess
pass
Без проверки я однажды чуть не запустил бекап на тестовой VM вместо боевого сервера. Хорошо, что вовремя заметил — просто перепутал окна в терминале.
Что пошло не так
Первая проблема — aiogram 3.x несовместим с aiogram 2.x. Я обновил библиотеку и сломал всех старых ботов. Пришлось переписывать хендлеры под новый синтаксис. Это отняло вечер.
Вторая проблема — утечка памяти. Я повесил на бота фоновую задачу, которая проверяла свободное место на диске каждые 5 минут и асинхронно собирала psutil. Через два дня бот сожрал 500 МБ RAM. Оказалось, asyncio-задача держала ссылку на старые данные. Починил через явное удаление кеша раз в час.
Третья проблема — реконнект. Если Telegram API ложится (а это бывает), aiogram бросает исключение. По дефолту он переподключается, но я не поставил exponential backoff. Бот просто падал и не вставал, пока systemd его не перезапускал.
Деплой и стабильность
Сейчас бот работает уже месяц. Ни одного падения. Я добавил ещё пару команд — /logs (последние 50 строк journalctl) и /uptime (с красивым форматированием). Плюс настроил автоуведомления: если загрузка CPU выше 90% больше минуты — бот сам пишет мне.
Для этого пришлось добавить фоновый воркер внутри aiogram. aiogram поддерживает middleware и фоновые задачи — я запилил цикл с asyncio.sleep(60) и проверкой метрик. Если что-то не так — шлёт сообщение. Удобно.
Кстати, токен храню в .env файле, который читается через python-dotenv. Так безопаснее, чем в коде. Сам .env файл лежит в /opt/tg_monitor с правами 600 и владельцем root.
Итог
Теперь я захожу в Telegram, пишу /status и через секунду вижу, как поживает мой сервер. Могу запустить бекап прямо с телефона, не открывая SSH. Бот стал моей палочкой-выручалочкой — особенно когда я в дороге и надо быстро проверить, всё ли в порядке.
Исходники я выложил в закрытый репозиторий, но если надумаешь повторить — пиши в комменты, скину пример. Aiogram реально стоит освоить, хотя порог входа в третьей версии выше, чем во второй. Но оно того стоит.