Как Я Патчил Zabbix

На днях я наконец-то дошел до обновления Zabbix. С момента прочтения статьи Забфикс 2.2 выпущен в соответствующем блоге я не мог дождаться, когда Gentoo разоблачит версию 2.2. В этой версии практически не было нововведений, которые бы мне не показались интересными и полезными в повседневной жизни.

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

Иногда я откладываю свою рутину и занимаюсь чем-то «параллельным» срочным и важным задачам и работе.

В этот раз я вспомнил о желании обновить Zabbix до версии 2.2. Проверил в маске*, ну наконец то есть 2.2.5 Ну идём дальше, уже год прошёл с момента релиза, стабильной версии нет, так что что бы ни случилось, мы решим эту проблему.

Демаскируем его, собираем куда нужно (главное, конечно, сервер и прокси), и перезапускаем.

Переустановка веб-интерфейса iii. И ничего, база данных обновляется.

Дело это не быстрое, база у меня не маленькая, а потом процесс совсем завис.

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

В журналах MySQL была ошибка номер 28, означающая «На устройстве не осталось места», везде было более чем достаточно места.

Как говорится «с Гугла я богоподобен» (с), не сразу, но мне удалось найти/догадаться, что это устройства ibdata1 и ibdata2, размер которых регулируется параметром innodb_data_file_path. После того, как я изменил max с 256M на 512M, обновление базы данных завершилось успешно и сервер запустился.

Также были проблемы с прокси, и тоже из-за базы данных.

Просто sqlite не обновляется, поэтому останавливаем прокси, удаляем старую базу и запускаем прокси.

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

Через пару часов посмотрите на график:

Как я патчил Zabbix

День начинал становиться томным.

У нас очередь.

Где?.

Основные просроченные данные с одного из прокси.

Какие данные? А данные получены по SNMPv3. Большой.

Раньше у меня были вопросы по поводу этого функционала, но руки так и не дошли, но была надежда, что обновление решит эти проблемы.

И здесь система становится практически неработоспособной.

В свое время, читая о том, как люди мониторят сотни сетевых устройств с помощью одного сервера или прокси, я не мог понять, в чем дело? У меня несколько десятков устройств, и все работает на пределе.

База данных чрезвычайно оптимизирована, а сервер основан на быстром хранилище данных и ему выделено много памяти.

Откатываться не хотелось, поэтому было решено попытаться решить проблему во что бы то ни стало.

Я выбрал один из прокси, за которым стояло больше всего SNMP-устройств, и начал разбираться.

Вот что у нас есть в журналах прокси**:

  
  
  
  
  
  
  
  
  
  
  
  
  
  
   

4447:20141218:124053.605 SNMP agent item "ifAdminStatus.["10130"]" on host "co-xx02" failed: first network error, wait for 15 seconds 4468:20141218:124108.270 resuming SNMP agent checks on host "co-xx02": connection restored

И так рандомно по всем SNMP хостам, с разными элементами.

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

Сам SNMP проверяем с помощью snmpwalk и вообще дёргаем всё дерево.

Без проблем.

Давайте погуглим.

У кого-то поллеры забиты, у кого-то неправильные таймауты, какой-то баг, связанный с этим, исправлен в версии 2.2.3. У кого-то каверзная проблема с сетью и UDP потерян.

Но это не наш случай.

Что тогда?.

Интересный факт, если перезапустить прокси

/etc/init.d/zabbix-proxy restart

Видно, как линия начинает сокращаться, но потом бац, что-то происходит снова и она моментально растет***.



Как я патчил Zabbix

Что происходит?.

Включить расширенное журналирование на zabbix-прокси Уровень отладки=4 Перезапустите zabbix-proxy и дождитесь появления ошибок.

Теперь вместо Network Error мы видим более полную информацию Это выглядит примерно так

5414:20141218:125955.481 zbx_snmp_get_values() snmp_synch_response() status:1 errstat:-1 mapping_num:94 5414:20141218:125955.481 End of zbx_snmp_get_values():NETWORK_ERROR 5414:20141218:125955.481 End of zbx_snmp_process_standard():NETWORK_ERROR 5414:20141218:125955.481 In zbx_snmp_close_session() 5414:20141218:125955.481 End of zbx_snmp_close_session() 5414:20141218:125955.481 getting SNMP values failed: Cannot connect to "192.168.x.x:161": Too long. 5414:20141218:125955.481 End of get_values_snmp() 5414:20141218:125955.481 In deactivate_host() hostid:10207 itemid:43739 type:6 5414:20141218:125955.481 query [txnlev:1] [begin;] 5414:20141218:125955.481 query [txnlev:1] [update hosts set snmp_errors_from=1418896795,snmp_disable_until=1418896810,snmp_error='Cannot connect to "192.168.x.x:161": Too long.' where hostid=10207] 5414:20141218:125955.481 query [txnlev:1] [commit;] 5414:20141218:125955.481 SNMP agent item "ifOperStatus.["10143"]" on host "co-xx04" failed: first network error, wait for 15 seconds 5414:20141218:125955.481 deactivate_host() errors_from:1418896795 available:1 5414:20141218:125955.482 End of deactivate_host()

Статус 1, статус ошибки -1, количество элементов 94 Вот вывод, что это NETWORK_ERROR А чуть ниже расшифровка Too Long и деактивация хоста.

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

Меня сразу заинтересовал параметр errstat Сделайте cat /var/log/zabbix/zabbix_proxy.log | grep errstat

5412:20141218:130351.410 zbx_snmp_get_values() snmp_synch_response() status:0 errstat:0 mapping_num:11 5433:20141218:130351.470 zbx_snmp_get_values() snmp_synch_response() status:1 errstat:-1 mapping_num:94 5430:20141218:130351.476 zbx_snmp_get_values() snmp_synch_response() status:1 errstat:-1 mapping_num:94 5417:20141218:130353.442 zbx_snmp_get_values() snmp_synch_response() status:0 errstat:0 mapping_num:5 5420:20141218:130353.534 zbx_snmp_get_values() snmp_synch_response() status:1 errstat:-1 mapping_num:94

Да, давайте копнем глубже Должна быть картинка «Здесь нужно копнуть глубже», но ее не будет. Я думаю, она всех поймала.

Сделайте cat /var/log/zabbix/zabbxi_proxy.log | греп ошибка:-1

5416:20141218:130353.540 zbx_snmp_get_values() snmp_synch_response() status:1 errstat:-1 mapping_num:94 5412:20141218:130355.571 zbx_snmp_get_values() snmp_synch_response() status:1 errstat:-1 mapping_num:94 5417:20141218:130355.591 zbx_snmp_get_values() snmp_synch_response() status:1 errstat:-1 mapping_num:94 .

5420:20141218:130453.187 zbx_snmp_get_values() snmp_synch_response() status:1 errstat:-1 mapping_num:94 5412:20141218:130455.206 zbx_snmp_get_values() snmp_synch_response() status:1 errstat:-1 mapping_num:94 5413:20141218:130455.207 zbx_snmp_get_values() snmp_synch_response() status:1 errstat:-1 mapping_num:94

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

иначе в логе будет слишком сложно разобраться.

Все равно система в нынешнем виде для мониторинга не пригодна.

Выключи это, сделай это кот /var/log/zabbix/zabbxi_proxy.log | grep Mapping_num и перезагрузите прокси Ошибок поначалу нет, а Mapping_num медленно растет от 1 до все большего и большего (отдельные строки вырезаны, чтобы показать принцип) Вы можете смотреть вечно, как растет Mapping_num

7876:20141218:131251.660 zbx_snmp_get_values() snmp_synch_response() status:0 errstat:0 mapping_num:4 7872:20141218:131251.681 zbx_snmp_get_values() snmp_synch_response() status:0 errstat:0 mapping_num:6 7872:20141218:131251.919 zbx_snmp_get_values() snmp_synch_response() status:0 errstat:0 mapping_num:8 7876:20141218:131251.919 zbx_snmp_get_values() snmp_synch_response() status:0 errstat:0 mapping_num:9 7868:20141218:131351.965 zbx_snmp_get_values() snmp_synch_response() status:0 errstat:0 mapping_num:13 10502:20141218:135237.884 zbx_snmp_get_values() snmp_synch_response() status:0 errstat:0 mapping_num:31 10507:20141218:135238.244 zbx_snmp_get_values() snmp_synch_response() status:0 errstat:0 mapping_num:62 12429:20141218:141637.942 zbx_snmp_get_values() snmp_synch_response() status:0 errstat:0 mapping_num:31 12429:20141218:141637.966 zbx_snmp_get_values() snmp_synch_response() status:0 errstat:0 mapping_num:31 12433:20141218:141651.142 zbx_snmp_get_values() snmp_synch_response() status:1 errstat:-1 mapping_num:94

А тут оппа 94, и -1 и слишком долго.

Те.

Сразу после запуска прокси тестирует устройства, отправляет им SNMP-запросы, увеличивая количество элементов в одном запросе.

Очередь начинает быстро сокращаться.

Затем он (то есть прокси) достигает магического числа 94, происходит сбой и устройства Zabbix начинают отключаться на 15 секунд, что в свою очередь начинает резко увеличивать очередь.

Как видите, это вовсе не ошибка сети, это уже слишком долго.

Ладно, пытаемся что-то найти, используя zabbix snmp слишком долго, ничего.

Опять таймауты, перегрузка поллера.

В одном интересном посте была информация, что такая ошибка возникала при неправильном формировании OID предмета, поэтому я перепроверил все свои OID, в том числе и через snmpget Те.

В конце концов, Google не смог мне помочь.

Разберемся сами, это полезно.

Итак, что мы имеем? Как только количество элементов достигает 94 (т. е.

становится достаточно большим), что-то происходит и система выходит из строя.

И снова картинка, которой там не будет ;) Пришло время разобраться с кодом.

В gentoo ничего скачивать не нужно, все уже есть, поэтому я просто распаковал все в рабочий каталог.

Давайте сначала найдем, где отображается ошибка.

Поиск по errstat Удивительно, но в файле с говорящим названием checks_snmp.c таких мест всего два.

эти два места: со строки 745

/* communicate with agent */ status = snmp_synch_response(ss, pdu, &response); zabbix_log(LOG_LEVEL_DEBUG, "%s() snmp_synch_response() status:%d errstat:%ld max_vars:%d", __function_name, status, NULL == response ? (long)-1 : response->errstat, max_vars);

и из строки 938

status = snmp_synch_response(ss, pdu, &response); zabbix_log(LOG_LEVEL_DEBUG, "%s() snmp_synch_response() status:%d errstat:%ld mapping_num:%d", __function_name, status, NULL == response ? (long)-1 : response->errstat, mapping_num);

Пока нас интересует второй кусок (исходя из того, что Mapping_num есть только в нем) Даже непрограммисту видно, что наш ответ NULL, но почему?.

Давайте вспомним, что с errstat:-1, откуда оно теперь понятно, у нас есть status:1. Те.

функция snmp_synch_response возвращает 1, но что это значит?.

А это значит STAT_ERROR(1) (а ещё он знает STAT_TIMEOUT(2) и STAT_SUCCESS(0)) Как говорится, непонятно, но здорово.

Давайте зайдём с другой стороны, где-то здесь в этом файле должен быть возврат NETWORK_ERROR, попробуем разобраться где и почему.

Самая первая запись в функцию zbx_get_snmp_response_error (что вроде намекает) Посмотреть код zbx_get_snmp_response_error

static int zbx_get_snmp_response_error(const struct snmp_session *ss, const DC_INTERFACE *interface, int status, const struct snmp_pdu *response, char *error, int max_error_len) { int ret; if (STAT_SUCCESS == status) { zbx_snprintf(error, max_error_len, "SNMP error: %s", snmp_errstring(response->errstat)); ret = NOTSUPPORTED; } else if (STAT_ERROR == status) { zbx_snprintf(error, max_error_len, "Cannot connect to \"%s:%hu\": %s.", interface->addr, interface->port, snmp_api_errstring(ss->s_snmp_errno)); switch (ss->s_snmp_errno) { case SNMPERR_UNKNOWN_USER_NAME: case SNMPERR_UNSUPPORTED_SEC_LEVEL: case SNMPERR_AUTHENTICATION_FAILURE: ret = NOTSUPPORTED; break; default: ret = NETWORK_ERROR; } } else if (STAT_TIMEOUT == status) { zbx_snprintf(error, max_error_len, "Timeout while connecting to \"%s:%hu\".

", interface->addr, interface->port); ret = NETWORK_ERROR; } else { zbx_snprintf(error, max_error_len, "SNMP error: [%d]", status); ret = NOTSUPPORTED; } return ret; }

Ага.

Те.

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

Мы уже поняли, что этот дефолт нас дезориентирует; нам нужно выяснить, что это за ошибка на самом деле.

Код ошибки хранится в ss-> s_snmp_errno, добавим вывод переменной в лог.

Программист я не очень, поэтому быстро собрал патч с помощью лома и чьей-то матери, вот так:

diff -urN zabbix-2.2.5/src/zabbix_server/poller/checks_snmp.c zabbix-2.2.5.new/src/zabbix_server/poller/checks_snmp.c --- zabbix-2.2.5/src/zabbix_server/poller/checks_snmp.c 2014-07-17 17:49:45.000000000 +0400 +++ zabbix-2.2.5.new/src/zabbix_server/poller/checks_snmp.c 2014-10-10 16:38:31.000000000 +0400 @@ -938,7 +938,7 @@ status = snmp_synch_response(ss, pdu, &response); zabbix_log(LOG_LEVEL_DEBUG, "%s() snmp_synch_response() status:%d errstat:%ld mapping_num:%d", - __function_name, status, NULL == response ? (long)-1 : response->errstat, mapping_num); + __function_name, status, NULL == response ? (STAT_ERROR == status ? (long) ss->s_snmp_errno : (long)-1) : response->errstat, mapping_num); if (STAT_SUCCESS == status && SNMP_ERR_NOERROR == response->errstat) {

Если статус STAT_ERROR, отобразите ss-> s_snmp_errno. Я загрузил исходный код Zabbix в локальный репозиторий, быстро подправил ебилд и пошёл.

Компилируем, перезапускаем, ждем.

И это наша настоящая ошибка.



11211:20141218:155253.362 zbx_snmp_get_values() snmp_synch_response() status:0 errstat:0 mapping_num:18 11210:20141218:155253.393 zbx_snmp_get_values() snmp_synch_response() status:1 errstat:-5 mapping_num:94

Ошибка -5 Смотрим Net-SNMP snmp_api.h

#define SNMPERR_TOO_LONG (-5)

Нечто подобное было видно в логах, но по фразе Too Long нам ничего не удалось найти, давайте посмотрим, что это за ошибка и когда она возникает. вы можете увидеть это в snmp_api.c еще немного кода

/* * Make sure we don't send something that is bigger than the msgMaxSize * specified in the received PDU. */ if (pdu->version == SNMP_VERSION_3 && session->sndMsgMaxSize != 0 && length > session->sndMsgMaxSize) { DEBUGMSGTL(("sess_async_send", "length of packet (%lu) exceeds session maximum (%lu)\n", (unsigned long)length, (unsigned long)session->sndMsgMaxSize)); session->s_snmp_errno = SNMPERR_TOO_LONG; SNMP_FREE(pktbuf); return 0; } /* * Check that the underlying transport is capable of sending a packet as * large as length. */ if (transport->msgMaxSize != 0 && length > transport->msgMaxSize) { DEBUGMSGTL(("sess_async_send", "length of packet (%lu) exceeds transport maximum (%lu)\n", (unsigned long)length, (unsigned long)transport->msgMaxSize)); session->s_snmp_errno = SNMPERR_TOO_LONG; SNMP_FREE(pktbuf); return 0; }

Есть только два варианта: 1. Длина данных, которые мы хотим отправить, больше, чем параметр msgMaxSize, определенный в полученном PDU. 2. Базовый транспорт не способен отправить пакет такой длины.

Возникает вопрос, как исправить эту ошибку.

Из вышесказанного следует, что нам нужно посмотреть, получаем ли мы msgMaxSize, правильно ли мы его обрабатываем и т.д. и т.п.

Но исходный код zabbix я вижу в первый раз, а C во второй (ну ладно, в третий).

Короче, энтузиазма не внушает. И наверное что-то могло сломаться.

Лирическое отступление: Надо сказать, что исследуя эту проблему, я также наткнулся на информацию о массовой обработке SNMP. Те.

zabbix может запрашивать несколько элементов данных SNMP одним запросом.

Подробности массовой обработки SNMP Суть в том, что zabbix может запрашивать до 128 значений за один запрос, но не все устройства способны обрабатывать эти 128 значений одновременно.

А zabbix использует стратегию поиска максимального значения для каждого конкретного устройства.

Кстати, мы это видели в логах.

Постепенное увеличение Mapping_num. Как только zabbix получает ошибку от устройства SNMPERR_TOO_BIG, он использует определенный алгоритм поиска максимального значения, который возвращает результаты без ошибок.

Почему я говорю? В Zabbix есть механизм обработки ошибок переполнения (назовем его так), нужно просто расширить его еще на один случай.

Сам алгоритм описан ниже вывода нашей ошибки.

Этот код снова

else if (1 < mapping_num && ((STAT_SUCCESS == status && SNMP_ERR_TOOBIG == response->errstat) || STAT_TIMEOUT == status)) { /* Since we are trying to obtain multiple values from the SNMP agent, the response that it has to */ /* generate might be too big. It seems to be required by the SNMP standard that in such cases the */ /* error status should be set to "tooBig(1)".

However, some devices simply do not respond to such */ /* queries and we get a timeout. Moreover, some devices exhibit both behaviors - they either send */ /* "tooBig(1)" or do not respond at all. So what we do is halve the number of variables to query - */ /* it should work in the vast majority of cases, because, since we are now querying "num" values, */ /* we know that querying "num/2" values succeeded previously. The case where it can still fail due */ /* to exceeded maximum response size is if we are now querying values that are unusually large. So */ /* if querying with half the number of the last values does not work either, we resort to querying */ /* values one by one, and the next time configuration cache gives us items to query, it will give */ /* us less. */ if (*min_fail > mapping_num) *min_fail = mapping_num; if (0 == level) { /* halve the number of items */ int base; ret = zbx_snmp_get_values(ss, items, oids, results, errcodes, query_and_ignore_type, num / 2, level + 1, error, max_error_len, max_succeed, min_fail); if (SUCCEED != ret) goto exit; base = num / 2; ret = zbx_snmp_get_values(ss, items + base, oids + base, results + base, errcodes + base, NULL == query_and_ignore_type ? NULL : query_and_ignore_type + base, num - base, level + 1, error, max_error_len, max_succeed, min_fail); } else if (1 == level) { /* resort to querying items one by one */ for (i = 0; i < num; i++) { if (SUCCEED != errcodes[i]) continue; ret = zbx_snmp_get_values(ss, items + i, oids + i, results + i, errcodes + i, NULL == query_and_ignore_type ? NULL : query_and_ignore_type + i, 1, level + 1, error, max_error_len, max_succeed, min_fail); if (SUCCEED != ret) goto exit; } } }

То есть все просто, нам нужно добавить свое условие, не нарушая работу уже имеющихся.

У нас есть все данные для этого:

  • статус должен быть STAT_ERROR
  • ss-> s_snmp_errno должно быть SNMPERR_TOO_LONG
Еще учтем, что таких мест у нас два (а также два выхода в лог-файл) и итоговый патч будет таким: Окончательно

diff -urN zabbix-2.2.5/src/zabbix_server/poller/checks_snmp.c zabbix-2.2.5.new/src/zabbix_server/poller/checks_snmp.c --- zabbix-2.2.5/src/zabbix_server/poller/checks_snmp.c 2014-07-17 17:49:45.000000000 +0400 +++ zabbix-2.2.5.new/src/zabbix_server/poller/checks_snmp.c 2014-10-10 16:38:31.000000000 +0400 @@ -746,10 +746,10 @@ status = snmp_synch_response(ss, pdu, &response); zabbix_log(LOG_LEVEL_DEBUG, "%s() snmp_synch_response() status:%d errstat:%ld max_vars:%d", - __function_name, status, NULL == response ? (long)-1 : response->errstat, max_vars); + __function_name, status, NULL == response ? (STAT_ERROR == status ? (long)ss->s_snmp_errno : (long)-1) : response->errstat, max_vars); if (1 < max_vars && - ((STAT_SUCCESS == status && SNMP_ERR_TOOBIG == response->errstat) || STAT_TIMEOUT == status)) + ((STAT_SUCCESS == status && SNMP_ERR_TOOBIG == response->errstat) || STAT_TIMEOUT == status || (STAT_ERROR == status && SNMPERR_TOO_LONG == ss->s_snmp_errno))) { /* The logic of iteratively reducing request size here is the same as in function */ /* zbx_snmp_get_values().

Please refer to the description there for explanation. */ @@ -938,7 +938,7 @@ status = snmp_synch_response(ss, pdu, &response); zabbix_log(LOG_LEVEL_DEBUG, "%s() snmp_synch_response() status:%d errstat:%ld mapping_num:%d", - __function_name, status, NULL == response ? (long)-1 : response->errstat, mapping_num); + __function_name, status, NULL == response ? (STAT_ERROR == status ? (long) ss->s_snmp_errno : (long)-1) : response->errstat, mapping_num); if (STAT_SUCCESS == status && SNMP_ERR_NOERROR == response->errstat) { @@ -1001,7 +1001,7 @@ } } else if (1 < mapping_num && - ((STAT_SUCCESS == status && SNMP_ERR_TOOBIG == response->errstat) || STAT_TIMEOUT == status)) + ((STAT_SUCCESS == status && SNMP_ERR_TOOBIG == response->errstat) || STAT_TIMEOUT == status || (STAT_ERROR == status && SNMPERR_TOO_LONG == ss->s_snmp_errno))) { /* Since we are trying to obtain multiple values from the SNMP agent, the response that it has to */ /* generate might be too big. It seems to be required by the SNMP standard that in such cases the */

Компилируем, перезапускаем.

И вот результат:

Как я патчил Zabbix

Ошибка сети исчезла из журналов.

Ура!.



Послесловие

Конечно, на самом деле поиск ошибки и решение заняло больше времени.

Мне пришлось покопаться глубже в исходниках zabbix и net-snmp, чтобы в итоге остановиться на двух местах кода.

Но ощущение победы над «косной материей» бесценно.

*желание пришло 7 октября, а тогда еще 2.2.5 замаскировалась.

По совпадению, его разоблачили 10 октября; ** на время не смотрите, я смоделировал ситуацию позже, чтобы написать статью.

Во время разборок совершенно не было времени вытаскивать данные из логов, не было потока, куда идти; *** да-да, я тоже моделировала картинку.

Представьте, что там, где вначале зелено, там все красное ;) А потом я пил во время рестартов.

31.12.2014 UPD: По итогам обсуждения статьи тикет был открыт (спасибо Алексвл ): сбой при отправке запросов SNMPv3, которые являются «слишком длинными», не обрабатывается должным образом пакетной операцией SNMP Он был успешно закрыт (спасибо Александру Савельеву), начиная с версий 2.2.9rc1, 2.4.4rc1, 2.5.0. Теги: #*nix #Системное администрирование #Администрирование сервера #никто не читает теги #zabbix #SNMP #snmp_err_toobig #snmperr_too_long #I am ROOT

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.