Продолжаем серию материалов об использовании PHP на серверах. Сегодня речь пойдёт о том как можно мониторить порты сервера и сообщать о проблемах в Telegram чат. Для организации мониторинга потребуется:
- Наличие учётной записи Telegram
- Знание PHP
- Сервера которые необходимо мониторить
- Созданный бот в Telegram и полученный токен бота
- Ubuntu сервер с CRON, на котором будет работать скрипт проверки
Предыстория:
На сегодняшний момент есть масса средств мониторинга серверной инфраструктуры, пожалуй подобных инструментом на столько много и они на столько «круты», что не всегда находится время для изучения этих монстров мониторинга, а иногда просто необходимо быть вкурсе работы своей серверной инфраструктуры.
Идея:
Всё очень просто, надо 1-н раз в минуту при помощи CRON запускать скрипт проверки, который «пропингует» сервера и если какой-то из них не ответил в заданный интервал времени — скрипт сообщит об этом в телеграм чат. Или можно просто «демонизировать» скрипт, тут на вкус и цвет….
Список серверов для мониторинга будет представлять из себя массив с параметрами, из важного здесь адрес и порт куда будет «стучаться» скрипт. Для того что бы в момент падения сервера скрипт не заспамил нас сообщениями, требуется ограничить отправку сообщений не чаще чем раз в N секунд. Время отправки нужно сохранять по каждому серверу.
Для записи времени отправки по каждому серверу будет достаточно сохранять данные в файл, в нашем случае будет использован тип переменной array, значение которой будет сохранено в php файл и значение переменной из этого файла будет присваиваться в момент выполнения скрипта.
Ниже код всего скрипта по мониторингу (пингу) серверов, при помощи php и отправка уведомлений в Телеграм чат:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 |
<?php $patch_folder = dirname(__FILE__); /** * База данных в виде массива php */ $file_db = $patch_folder . '/pinger_dbdata.php'; $dbpinger = require($file_db); if (empty($dbpinger) || !is_array($dbpinger)) { $dbpinger = []; } $servers = [ [ 'name' => '192.162.1.121', //Название, может быть любым 'address' => '192.162.1.121', //Адрес сервера 'port' => 3389, //Порт сервиса или службы 'whaitimeout' => 1, //Ожидание ответа 'sendmessagetimer' => '600', //Сообщать по этому поводу не чаще чем это время ], [ 'name' => '192.162.1.4', 'address' => '192.162.1.4', 'port' => 22, 'whaitimeout' => 1, 'sendmessagetimer' => '600', ], ]; $sender = TelegramSender::getInstance(); foreach ($servers as $server) { if (!isActiveService($server)) { //Если файл в кодировки 1251 - нужно сообщение конвертировать в utf-8 $message = iconv('WINDOWS-1251', 'UTF-8', "Тест сервера {$server['name']} - Не удался!"); $chat_id = '-11111111'; //ИД общего чата $expire_time = $dbpinger[$chat_id][$server['address']]['sendtime'] ?? 0; if ($expire_time > time()) { continue; } $res = $sender->sendMessage($chat_id, $message); if (!empty($res)) { $dbpinger[$chat_id][$server['address']]['sendtime'] = time() + intval($server['sendmessagetimer']); } } } /** * Формирование бд опроса */ file_put_contents($file_db, '<?php' . PHP_EOL . 'return '); file_put_contents($file_db, var_export($dbpinger, true), FILE_APPEND); file_put_contents($file_db, ';', FILE_APPEND); die('end_code'); /** * True - если сервак работает * @param type $server * @return boolean */ function isActiveService($server): bool { if ($fp = fsockopen($server['address'], $server['port'], $errCode, $errStr, $server['whaitimeout'])) { fclose($fp); return true; } return false; } /** * Отправка сообщения в Телеграм */ class TelegramSender { protected $proxyAddr = '111.111.111.111'; protected $proxyPort = '333'; protected $proxyUser = 'user_proxy'; protected $proxyPass = 'user_proxy111'; /** * Токен бота * * @var string */ protected $botToken = '111107125008:AGGAz_yCw2b3O-TtpB-5VEpl_nBr3ie2fXy'; /** * Получатели сообщений * @var array */ protected $clientIds = []; /** * * @var type */ protected static $_instance = null; /** * Отправка сообщения */ public function sendMessage(string $user_id, string $message): string { $postdata = http_build_query( [ 'chat_id' => $user_id, 'text' => $message, 'disable_notification' => false ] ); $url = "https://api.telegram.org/bot{$this->botToken}/sendMessage"; return $this->requestCurl($url, $postdata); } /** * Вернёт список отправленых сообщений боту * Нужно только для извлечения ИД чата * @return string */ public function getBotUpdate(): string { $url = "https://api.telegram.org/bot{$this->botToken}/getUpdates"; return $this->requestCurl($url, ''); } protected function requestCurl(string $url, string $postdata) { $curl_handle = curl_init(); $curl_params = [ CURLOPT_URL => $url, CURLOPT_CONNECTTIMEOUT => 10, CURLOPT_RETURNTRANSFER => 1, CURLOPT_TIMEOUT => 10, CURLOPT_USERAGENT => 'Mozilla/47.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)', CURLOPT_POSTFIELDS => $postdata, CURLOPT_FOLLOWLOCATION => 10, CURLOPT_SSL_VERIFYHOST => 0, CURLOPT_SSL_VERIFYPEER => 0, CURLOPT_HTTPHEADER => [ 'Content-type: application/x-www-form-urlencoded' ], ]; //Если прокси не нужен, можно отключить $curl_params[CURLOPT_PROXYTYPE] = CURLPROXY_SOCKS5_HOSTNAME; $curl_params[CURLOPT_PROXYUSERPWD] = "{$this->proxyUser}:{$this->proxyPass}"; $curl_params[CURLOPT_PROXY] = $this->proxyAddr; $curl_params[CURLOPT_PROXYPORT] = $this->proxyPort; curl_setopt_array($curl_handle, $curl_params); $result = curl_exec($curl_handle); curl_close($curl_handle); return $result; } public function getBotToken(): string { return $this->botToken; } public function getClientIds(): array { return $this->clientIds; } /** * Singletone * @return TelegramSender */ public static function getInstance() { if (is_null(self::$_instance)) { self::$_instance = new self(); } return self::$_instance; } public function __clone() { throw new \Exception('Forbiden instance __clone'); } public function __wakeup() { throw new \Exception('Forbiden instance __wakeup'); } protected function __construct() { } } |
Немного пояснения по коду (сверху вниз):
- Подключаем файл с «базой данных», в котором хранится информация об отправка сообщений( что бы можно было ограничить частоту отправлений сообщений)
- Указываем массив серверов которые необходимо мониторить на предмет работоспособности
- В цикле пробегаемся по каждому серверу и проверяем его активность при помощи функции
- В теле цикла у нас задан ИД чата куда слать уведомления
- И тут же в цикле проверяем, если время предыдущей отправки старше текущего времени, то отправлять сообщение не нужно
- Отправляем сообщение, если телеграм ответил нам не пустым сообщением, сохраняем информацию о времени отправки в массив нашей файловой бд
- Записываем нашу бд в файл для последующего использования
- Всё, скрипт свою работу сделал!
Пояснение к классу:
- Для работы с Telegram нужен бот, токен бота,и ид чата куда отправлять сообщения (для группового чата ид начинается с знака «-«)
- Отправка осуществляется через CURL, если телеграм заблокирован — можно использовать настройки PROXY
- Для отправки сообщения в Telegram необходимо вызвать 1-н метод sendMessage куда передать ИД чата и сообщение
Немного про Telegram:
- Для того что бы сообщения приходили вам, как пользователю Telegram, достаточно подписаться на этого бота через токен которого вы отправляете сообщения. Для того что бы сообщения приходили в групповой чат, необходимо в групповой чат добавить вашего бота.
- Если необходимо узнать ИД пользователя Telegram или ИД публичного:
- Для личных сообщений бота, просто напишите боту несколько сообщений (от 3 до 7)
- Для сообщений в групповой чат, так же напишите в общий чат, каждое сообщение должно упоминать имя вашего бота
- Затем воспользуйтесь методом класса getBotUpdate, в ответ получите JSON с сообщениями которые вы отправили боту. В этом JSON найдёте необходимые вам ИД чата или ИД вашего Telegram аккаунта
Стуктура файла БД в виде php файла:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<?php return array ( 500537532 => array ( '192.168.0.241' => array ( 'sendtime' => 55551, ), ' 192.162.0.129' => array ( 'sendtime' => 1, ), ), ); |