Как перестать беспокоиться и начать жить

  • Tutorial

Мониторинг работы организации с помощью докера и телеграм-бота

Многим хотелось бы знать, если вдруг что-то случится с их сайтом, магазином и т.п., особенно, если это не требует ни денег, ни времени, ни усилий. А ведь это довольно легко сделать: потратив совсем немного свободного времени, создадим докер контейнер, который будет периодически делать запросы в БД, расчитывать на их основании некие метрики, сравнивать их с пороговыми значениями и в случае превышения этих порогов, оповещать заинтересованных лиц.

Первым делом надо завести бота. Как это сделать можно нагуглить за 5 минут. Например, тут недавно была статья. Также нашему боту нужно разрешение на добавление его в группы, если получать сообщения планирует не один человек, а несколько. Для этого при создании бота надо написать BotFather команду «/setjoingroups». После этого, собственно надо создать группу, добавить в нее нашего бота и всех заинтересованных в получении сообщений.

Далее воспользуемся библиотекой pytelegrambotapi и закончим подготовительный этап тем, что узнаем CHAT_ID Его можно узнать, например, включив логирование и, воспользовавшись командой из той же статьи, включить своего бота на ожидание команды «/start». Затем ввести эту команду в созданной нами группе. Команда придет к боту, он ответит, логах будет выведена масса разной информации, в том числе и нужный нам CHAT_ID группы:

import logging

import telebot

bot = telebot.TeleBot(TOKEN)
chat_id = CHAT_ID
logger = telebot.logger
telebot.logger.setLevel(logging.DEBUG)

@bot.message_handler(commands=['start'])
def start_message(message):
    bot.send_message(message.chat.id, 'Привет, ты написал мне /start')
bot.polling()

Теперь мы можем посылать сообщения ботом в нашу группу:

bot.send_message(chat_id=CHAT_ID, text=TEXT)

Чтобы было что посылать рассчитаем метрику, по данным полученным из БД. Допустим, у нас каждый бизнес процесс подтверждается СМС клиенту. Соответственно, если давно не посылались СМС, что-то идет не так.

Создадим класс, который будет отвечать за подключение к БД и расчет метрик. Я использовал для доступа в БД библиотеку pymysql. Она легкая и понятная:


import datetime
import os
from contextlib import closing
import pymysql
from pymysql.cursors import DictCursor
from constants import *

class Monitor:
    
def __init__(self):
        self.starttime = datetime.datetime.today()
        self.port = 3306
        self.host = os.environ.get('MYSQL_HOST')
        self.user = os.environ.get('USER')
        self.password = os.environ.get('MYSQL_PWD')
        self.db_name = 'backend'

def sms_log(self):
    with closing(pymysql.connect(host=self.host, port=self.port, user=self.user, password=self.password,db=self.db_name, charset='utf8', cursorclass=DictCursor)) as connection:
        end_time = datetime.datetime.today()
        start_time = end_time - datetime.timedelta(minutes=TIME_PERIOD)
        st_time = start_time.strftime('%Y-%m-%d %H:%M:%S')        
        end_time = end_time.strftime('%Y-%m-%d %H:%M:%S')
        with connection.cursor() as cursor:
            query = f"SELECT COUNT(*) FROM sms_log WHERE created_at BETWEEN '{st_time}' AND '{end_time}'"
            cursor.execute(query)
            for row in cursor:
                result = row['COUNT(*)']
                if result < SMS_COUNT_ALERT:
                    return f"За {TIME_PERIOD} минут с \
            f"{start_time.strftime('%H:%M')} " \
                 f"было отправлено {result} СМС, предел: {SMS_COUNT_ALERT}"

Подобным образом рассчитываем все интересующие нас метрики, сравниваем их с некими пределами, при превышении которых, высылаем сообщения. Для чего подправим главную функцию бота следующим образом:


def main():
    bot = telebot.TeleBot(TOKEN)
    chat_id = CHAT_ID
    logger = telebot.logger
    telebot.logger.setLevel(logging.DEBUG)  
    monitor = Monitor()
    while True:
        """ 
        Выполняем запросы в БД каждые 15 (TIME_PERIOD) минут. 
        """
      result = monitor.sms_log()
      if result:
            bot.send_message(
            chat_id=chat_id, 
            text=result,
            disable_notification=not monitor.is_day()
        )

      . . .

      sleep(TIME_PERIOD*60)

Для того, чтобы сообщения ночью приходили бесшумно, используем следующую конструкцию:

disable_notification=not monitor.is_day()

где

@staticmethod
def is_day():
    return 9 <= (datetime.datetime.today()).hour <= 23

также данный метод помогает сравнивать метрики с отличными от дневных пределами в ночное время.

Теперь, когда бот настроен и метрики рассчитаны, остается запихнуть все это добро в контейнер и задеплоить его на какую-нибудь круглосуточно работающую машину, либо в облако.

Докерфайл будет выглядеть примерно следующим образом:

FROM python:3.6-alpine

WORKDIR /monitor-docker

COPY requirements.txt .gitignore *.py /monitor-docker/

RUN pip install -r requirements.txt&& \
		rm -rf /tmp/* /var/tmp/*

CMD python bot.py

где requirements.txt:

PyMySQL==1.0.2
pyTelegramBotAPI==3.7.6

Образ можно выбрать поновее, но и этот отлично справится.

Теперь его надо сбилдить:

docker build -t bot:latest .

И запустить, передав переменные окружения для подключения к БД в команде:

docker run --name bot -d -e USER=ххх -e MYSQL_HOST=ххх -e \
MYSQL_PWD=ххх bot:latest

И можно ждать сообщений. Ну или не ждать, тут все зависит от метрик. Делов-то на полдня максимум. Однако, потратив их, мы получаем круглосуточное наблюдение за системой. Почти даром.

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

UPD.: По результатам ревью в комментариях произвел небольшой рефакторинг: поправил метод is_day, избавился от venv - виртуальные окружения в докере не нужны (по крайней мере для таких простых приложений), избавился от boot.sh - теперь запускаю бота непосредственно из докерфайла.

Ads
AdBlock has stolen the banner, but banners are not teeth — they will be back

More

Comments 18

    +5
    А ведь это довольно легко сделать: потратив совсем немного свободного времени, создадим докер контейнер, который будет периодически делать запросы в БД, расчитывать на их основании некие метрики, сравнивать их с пороговыми значениями и в случае превышения этих порогов, оповещать заинтересованных лиц.

    Самое сложное — это не запросы в БД делать и уведомления слать. Самое сложное — это понять, что же конкретно надо мониторить и с какими порогами.

      –3
      Ну, что мониторить, обычно понятно. Сложность в подборе порогов.
        +3
        Ну, что мониторить, обычно понятно

        Это кому как, к сожалению.

          0
          Тогда, методом научного тыка: сперва будем мониторить всё, особоненно то, что раньше уже падало, начиная с низких порогов. Затем пороги можно повышать: в течении нескольких дней интуитивно можно прийти к какому-то не очень высокому уровню ненужных сообщений.
            +1

            Мониторить все невозможно. Просто невозможно. Поэтому вопрос, как выбрать то, что важно мониторить.

              0

              Ну, статья то не про это, а как организовать мониторинг системы, используя указанные в статье инструменты. Выбору того, что важно мониторить, можно посвятить отдельную статью...

                +2

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

                  +1
                  >Дальше можно, например, настроить передачу рассчитанных метрик в Zabbix
                  Дело в том, что Zabbix, насколько я помню, уже умеет отсылать в телеграмм. Ну и зачем тогда вот это затевалось? Проще уж начинать сразу с него. По крайней мере, лимиты и пределы, на которые нужно реагировать, все равно очень желательно иметь в UI, а не в коде. И код, который что-то меряет, не должен пределы проверять, вообще говоря.
        +4
        <зануда>Метод is_day можно сделать однострочным, просто return выражения из if</зануда>
          –3
          Можно конечно. Но статья-то для новичков, с максимально простым кодом.
            +4
            Вот как раз новичков-то и не стоит учить плохому.
              0
              Исправил на более питонический вид.
                0
                Да, так лучше)
          +2

          Если используется Docker, то можно отказаться от venv и прямо в docker файле определить entry point.

            0
            Кстати, да. По привычке создал venv, да так и оставил, при переносе в докер.
              0
              Поправил — для таких простых приложений venv совершенно не нужен.
              +2
              def is_day()…
              а потом удивляемся — почему некоторые заинтересованные лица восточнее нас пишут гневные письма чтобы отключили эти уведомления по ночам
                0

                Если просто нужно слать уведомления через бота, то достаточно простого curl с минимальным json. Как сформировать запрос хорошо описано в документации Telegram Bot API.

                Only users with full accounts can post comments. Log in, please.