Свой мониторинг сайтов: от идеи до реализации

Предыстория:

Исторически так сложилось, что я занимаюсь разработкой в 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-ю попавшуюся статью (вроде бы эту) и сделал так же, как написано в ней.

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

56f2a8b57cc5895232d6e6e79d44a0d3.png

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

обычный эхо-бот с 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 дней в неделю не взирая на праздники и прочие обстоятельства. Все довольны.

daeb15d72658434f17ea71dc28383b20.png

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

© Habrahabr.ru