Предыстория

Исторически так сложилось, что я занимаюсь разработкой в digital‑агентстве полного цикла в центре столицы. Знаете, бывают такие компании, которые делают контекст, smm, seo, пилят или допиливают сайты, всяческие автоматизации, интеграции b2b, b2c и т. д.? Вот в такой компании и работаю.

У моей команды много сайтов на поддержке, а в нашей стране много праздников. В праздники интернет продолжает работать, реклама продолжает крутиться, люди продолжают покупать на сайтах, а значит и сайты должны функционировать 24/7 вне зависимости от цвета дня календаря. А как узнать, что все в порядке, если ты жаришь шашлык на даче в 100 км. от столицы? Все очевидно — нужен сервис, который будет мониторить сайты и пинговать дежурного программиста, если что‑то пошло не так.

Честно скажу, сперва я, как ленивый умный человек, решил воспользоваться сторонним сервисом. Погуглил, почитал обзоры на Хабре, посмотрел сервисы и не впечатлился. Не то, что бы ничего интересного не нашел. Но не нашел рабочего продукта, который бы меня устраивал на все 100%. Печально конечно, однако время было перед Новым Годом, я был в отпуске и мне пришла в голову мысль: «А почему бы мне не написать свой сервис для мониторинга сайтов с блекджеком и прочими радостями?» Что еще делать человеку в праздники?

Что еще делать человеку в праздники?
Что еще делать человеку в праздники?

Требования к сервису

Я посмотрел старые кейсы, в которых что‑то пошло не так и решил мониторить только то, что нужно мне, а именно:

  • HTTP ответ сервера (работает/не работает(и как именно не работает))

  • Срок действия сертификата (бывало заканчивался сертификат в самый неудобный момент)

  • Инфу о доменном имени (бывало, что и домен заканчивался внезапно)

  • Время отклика от сервера (достаточно часто на крупных высоконагруженных проектах эта метрика нужна)

Для минимально рабочей версии вполне достаточно. Алгоритм действий следующий: будем смотреть все это, логировать и класть в базу данных. Если что‑то пошло не так — напишем всем подписанным на этот сайт в телеграм. Ну и сделаем какой‑нибудь веб‑интерфейс с авторизацией в котором можно будет посмотреть логи. Фронт пусть будет Vue3+VUEX на фронте, а с бэком пусть по рест‑апишке общается. Бэк традиционный php+mySql. Звучит достаточно просто. Вперед!

Авторизация

В этом проекте, я мог делать все, что хочу, и не делать то, чего не хочу. Например, мне не хотелось писать свою регистрацию. Даже брать свою уже готовую не хотелось. «Прикрутить стороннюю — вот отличное решение!» — подумал я. Пусть какой‑нибудь другой сервис забирает на себя все эти истории с восстановлением пароля, заходов с непонятных мест и т. д. Я опросил коллег и мой выбор пал на Яндекс (у 100% людей опрошенных оказался аккаунт, который они используют для музыки/такси/шеринга или доставки еды).

Не буду сильно углубляться в детали реализации, официальная документация вполне понятна, хоть и разбросана среди всех документаций Яндекса. Просто основные моменты со ссылками:

  1. Заходим в на страничку, заводим там новое приложение. Получаем ClientID, ClientSecret приложения и указываем адрес, на который перенаправить человека после авторизации.

  2. При помощи ClientID получаем код подтверждения для получения токена пользователя пользователя (посылаем его по ссылке вида: https://oauth.yandex.ru/authorize?response_type=code&client_id=YOU_CLIENT_ID&redirect_uri=URL_TO_REDIRECT)

  3. Имея код подтверждения (он приходит в виде гет-параметра после редиректа на URL_TO_REDIRECT), ClientID и ClientSecret, меняем их на токен пользователя при помощи POST-запроса

  4. Теперь, когда у нас есть токен, поменяем его на данные о пользователе. В нашем случае это аватар и логин. Оформляем по вкусу. ( Я в тот вечер похоже пересидел в документации Яндекса и мои вкусы стали специфичными.)

    то чувство, когда понимаешь, что в логине без :first-letter не обойтись
    то чувство, когда понимаешь, что в логине без :first-letter не обойтись

База данных

Итак, пользователь есть, нам надо его где‑то хранить. Поэтому самое время подумать над структурой базы данных. У меня вышло 4 таблички:

  1. users — хранит необходимые данные про пользователя;

  2. sites — название сайта, когда домены/сертификаты заканчиваются;

  3. siteToUser — чтобы понимать кто какие сайты смотрит;

  4. errLog — если ошибка‑пишем, если исправилась тоже пишем.

В 1-ю табличку кладем то, что получили в результате авторизации. Осталось только прикрутить к юзеру телеграм.

Telegram

Про создание ботов для телеграм на php очень много всего написано, я взял 1-ю попавшуюся статью (вроде бы эту) и сделал так же, как написано в ней.

Теперь, когда у нас есть бот, нам надо как‑то связать его с пользователем, для этого юзер берет в ЛК число (которое идентифицирует его) и шлет боту.

Бот посмотрит есть ли он в базе и добавит нас, если будет совпадение.

обычный эхо-бот с 1-м запросом в DB и 1-м if под капотом
обычный эхо-бот с 1-м запросом в DB и 1-м if под капотом

Если честно, это лишнее движение мне не нравится. С точки зрения юзабилити «скопируй‑пошли» как то не очень. Однако, к моему сожалению, я не придумал другого механизма. Бот не умеет писать первый и надо как‑то понять, кто именно написал боту.

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

Cписок сайтов

Реализация списка сайтов сводится к реализации обычного CRUD (CRD если быть точнее). Добавляем, если нет такого сайта, сразу смотрим его статус и пишем в базу. Если же есть, то просто линкуем юзера и сайт в таблице sitesToUsers. Удаляем просто отлинковкой (все-равно если за сайтом никто не смотрит, то он и не проверяется). Не забываем спросить, точно ли пользователь хочет удалить сайт.

обычный CRUD
обычный CRUD

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

 const reg = /^http([s]{0,1})\:\/\/([\wёa-я-]{2,}\.)+[\wёa-я-]{2,}$/i

Список сайтов есть. Осталось получить данные по ним.

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

HTTP

  1. сделаем массив со всеми возможными кодами ответов

  2. получим ответ при помощи curl (CURLINFO_HTTP_CODE)

PING

По факту пинг — это время ответа сервера, как известно curl тоже умеет это делать, поэтому не будем ничего придумывать, а просто добавим в предыдущий запрос получение еще одного параметра (CURLINFO_CONNECT_TIME_T)

http и аналог ping
http и аналог ping

HTTPS

SSL сертификат получаем с помощью контекста потоков и разбираем его при помощи openssl_x509_parse. В итоге нам интересен параметр validTo_time_t который мы и получаем. Результат выглядит так:

таймстамп даты окончания сертификата
таймстамп даты окончания сертификата

WHOIS

Чтобы получить информацию о домене, посылаем TCP запрос (43-й порт) на WHIOS-сервер. Для разных доменных зон, бывают разные WHOIS сервера.

Логика следующая: мы берем домен смотрим какая у него там зона и шлем уже на нужный сервер. В ответе получаем текст, из которого в результате парсинга достаем значение paid-till. (практически для всех доменов этот параметр есть)

на самом деле ответ больше, но мы следим только за 1 параметром
на самом деле ответ больше, но мы следим только за 1 параметром

В тексте с ответом приходит еще ссылка на правила использования данного WHOIS сервера (для зоны ru выглядит так)

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

CRON

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

Количество дней до окончания сертификата и домена обновляется раз в сутки, поэтому проверяем все сайты раз в сутки на эти значения и если что-то обновилось - актуализирует в базе данных. Предупреждаем всех причастных за 5 дней до окончания сроков. Стараемся уложиться в лимиты для запросов к WHOIS серверам.

Яндекс проверяет http статус сайта раз в 15 минут и если сайт еще через 15 минут лежит - отключает рекламу (если верить техподдержке). Мы делаем быстрее. Статус обновляется раз в 11 минут и если что-то идет не так дежурный программист сразу приступает к решению вопроса. На практике, в 90% случаев сайт поднимается до того, как Яндекс успел среагировать.

Еще немного интерфейса

На этом можно было бы и закончить, но мне (да и нашим клиентам) порой интересна статистика, поэтому мы ее покажем.

  1. текущее состояние сайта (данные актуализируются, когда мы выбираем сайт в списке сайтов)

  2. данные за месяц (помечаем дни без ошибок зеленым, дни с ошибками - красным)

    Когда у общества нет цветовой дифференциации штанов, то нет цели! А когда нет цели — нет будущего! (с)
    Когда у общества нет цветовой дифференциации штанов, то нет цели! А когда нет цели — нет будущего! (с)
  3. Ну и конечно же детализация по дню. Лог выводим просто в окошке, если что-то не так раскрашиваем.

    спустя 2 дня мониторинга хабр начал отдавать404
    спустя 2 дня мониторинга хабр начал отдавать404

    Для отображения пинга используем обычный график от chart.js

    наглядно видно, что что-то пошло не так, а потом все починили и все так, как надо.
    наглядно видно, что что-то пошло не так, а потом все починили и все так, как надо.

Результат:

Помните в самом начале, я описывал требования к минимально рабочей версии? Так вот, все они выполнены. Время от времени выскакивают баги, но их все меньше и они достаточно быстро исправляются. В целом вышла достаточно удобная штука, которая мониторит и говорит, если что-то не так. Это решает 99% наших вопросов нашего отдела по мониторингу.

Однако есть еще и другие отделы, поэтому я планирую сделать несколько доработок:

  1. Необходимо осуществлять проверку из разных точек (для начала возьмем города-миллионники в России)

  2. Необходимо будет сделать возможность отслеживания статуса отдельных разделов сайтов (мы же живем в эпоху микросервисов)

  3. Люди любят отчеты, надо добавить рассылку писем с еженедельным и ежемесячным отчетом.

  4. Подумать над масштабированием базы данных. Сейчас все вертится на хостинге, который мы используем как песочницу. Однако, если возникнет необходимость придется поиграть с выделенным серверами и докерами-кубернетисами. (С другой стороны, кому интересно что там было с сайтом несколько месяцев назад? Важно то, что здесь и сейчас) Посмотрим по обстоятельствам.

  5. Конечно же нужно адаптировать сервис под мобилку.

Итоги

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

Для тех, кто дочитал до конца ссылка на результат с ограничением в 1 сайт для добавления. Пользуйтесь.

Комментарии (17)


  1. gmtd
    00.00.0000 00:00

    Вопрос только один - почему VUEX, а не Pinia?


    1. babilonsuxx Автор
      00.00.0000 00:00

      Тут все просто. Уже несколько лет для внутренних продуктов используем vue+vuex. Когда начинал, была глубокая ночь и править дефолтный конфиг мне не хотелось. Для маленьких приложений пинья лучше, тут вы правы. Наверное пора сделать отдельный конфиг с pinia.


  1. pavia
    00.00.0000 00:00
    +2

    Почему не https://t.me/uptimeVBI_bot?start=числоизадминки?


  1. aborouhin
    00.00.0000 00:00
    +3

    А чем не устроил Zabbix? Все перечисленные задачи решает или из коробки или легко гуглящимися шаблонами / скриптами. Интеграция с Телеграм есть готовая. Ну и при последующем расширении объём велосипедостроения будет явно меньше. Разве что авторизации через Яндекс нет :) , но эта идея как-то и так не кажется самой удачной...

    Ну или какой-нибудь exporter к Prometheus запилить, если стильно, модно... :) Всё-таки не полностью с нуля свой велосипед (P.S. комментом ниже подсказали готовый exporter для этого)


    1. Kuznetsov_pa
      00.00.0000 00:00

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


      1. babilonsuxx Автор
        00.00.0000 00:00

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


        1. aborouhin
          00.00.0000 00:00

          А как Вы это собирались делать своими силами? Всё равно надо в каждой такой геолокации иметь сервер, с которого осуществляется проверка... Ну а на него и zabbix agent поставить можно.


        1. Kuznetsov_pa
          00.00.0000 00:00
          +3

          Ставим несколько zabbix agent/proxy в разных геолокациях.


    1. babilonsuxx Автор
      00.00.0000 00:00

      Тут тоже все просто. Моя команда растет и необходим проект, который был бы точкой роста для джунов. Не сильно объемный, но чтобы включал стек с которым им придется постоянно работать. Таким образом, помимо мониторинга, я решаю вопрос с обучением ребят. Мне кажется - это нормальный подход. Можно поиграть со сторонними апишками (например мы очень много работаем с апи директа, поэтому яндекс), пописать свою апишку, есть фронтенд (vue+vuex, который мы используем для внутренней разработки), есть бэкенд, есть немного join'ов в mySql и т.д. Zabbix конечно хорош, но он бы решил только задачу с мониторингом.


      1. Kuznetsov_pa
        00.00.0000 00:00

        Вот как обучение, да, согласен. Проходить повторный круг по мониторингу придётся, когда нужно будет делать графики, отчёты, монитринг ос и битрикса внутри и его интеграцией, алертирг на группы и прочее прочее прочее


  1. SLASH_CyberPunk
    00.00.0000 00:00
    +1

    Вы изобрели blackbox_exporter?


  1. imjustwatching
    00.00.0000 00:00
    +2

    Очень круто, но выглядит как изобретение велосипеда


  1. mxav
    00.00.0000 00:00
    +1

    Для мониторинга сайтов можно использовать phpservermon


  1. gmtd
    00.00.0000 00:00

    Согласен с автором, что лучше писать свой продукт для таких целей. Он не такой сложный, а всё равно рано или поздно полезет кастомная логика. Плюс потом возможно потребуется не только мониторить, но у управлять. Изучение и кастомизация чужого продукта иногда занимает больше времени, чем написание своего.


  1. inkvizitor68sl
    00.00.0000 00:00
    +1

    Kuma uptime ?


  1. r0ck3r
    00.00.0000 00:00

    Разрабатываю аналогичный проект, но список проверок немного отличается, включая в себя возможность мониторинга ресурсов сервера (память, диск, uptime, load average). Достигается это путем установки на сервер приложения-монитора. Также есть возможность мониторинга узлов за NAT'ом при помощи приложения-агента, которое связывается с основным сервером и получает от него задания. Вот тоже думаю не написать ли об этом проекте статью


  1. yascherko
    00.00.0000 00:00

    Telegram вроде того как-то можно ж было? Ссылку поставить сразу с текстом, чтобы без копипасты