• Sy chevron_right

      Настройки таймаута и количества попыток для запросов SNMP в Zabbix 3.4

      pubsub.slavino.sk / sysadmblog · Sunday, 27 September, 2020 - 08:00 edit · 4 minutes

    До версии 2.2.0 в Zabbix для опроса по SNMP использовались настройки опроса по умолчанию. По умолчанию в библиотеке SNMP таймаут опроса составлял 1 секунду, а в случае неудачи делалось до 5 дополнительных попыток опроса. В Zabbix 2.2 для опроса по SNMP используется значение опции Timeout из файла конфигурации и делается только одна попытка опроса.

    Ранее, чтобы уменьшить количество вызовов скриптов внешнего опроса, в конфигурации моих сереров Zabbix было выставлено максимально возможное значение таймаута - 30 секунд, а каждый такой скрипт запрашивал как можно больше значений у устройства и отправлял полученные значения в Zabbix при помощи утилиты zabbix_sender. Если скрипт успевал снять все необходимые данные, укладываясь в отведённые для его работы 30 секунд, то всё хорошо. Если скрипт не укладывался в 30 секунд, то процедуру опроса делили на несколько частей, так чтобы каждая из них уложилась в 30 секунд. Затем в Zabbix'е заводили по отдельному элементу данных для вызова скрипта, указывая ему какую именно часть данных нужно снять с устройства.

    При переходе с Zabbix 2.0 на 2.2 изменение настроек таймаута SNMP привело к большим проблемам: использование процессов Poller выросло до 100% и сервер перестал успевать опрашивать оборудование в необходимом темпе. Происходило это потому, что первый же запрос SNMP к недоступному устройству растягивался до 30 секунд, в течение которых процесс просто ждал ответа от устройства, не занимаясь больше ничем.

    Если вы не используете скриптов внешнего опроса или значение таймаута в конфигурации Zabbix имеет маленькое значение, или на вашем сервере Zabbix много оперативной памяти, то для вас это изменение не будет представлять никаких особых проблем - можно просто увеличить количество процессов Poller. Мне же в моём случае было просто жалко тратить оперативную память на процессы, которые фактически ничем не занимались. Прежний Zabbix без проблем справлялся с опросом, используя значительно меньше оперативной памяти, значит и новый тоже может. Я тогда выполнил откат до прежней версии Zabbix и стал изучать исходные тексты Zabbix с целью вернуть прежние значения таймаута в запросы SNMP.

    Получившиеся исправления ранее уже были описаны в рамках более крупной статьи Установка и настройка Zabbix 2.2.0 в Debian Wheezy , но там им не уделялось достаточного внимания. На этот раз я опишу заплатку более подробно.

    Задеклалируем изменения, которые собираемся внести, отредактировав пример файла конфигурации conf/zabbix_server.conf:
    Index: zabbix-3.4.12-1+buster/conf/zabbix_server.conf
    ===================================================================
    --- zabbix-3.4.12-1+buster.orig/conf/zabbix_server.conf
    +++ zabbix-3.4.12-1+buster/conf/zabbix_server.conf
    @@ -439,6 +439,26 @@ DBUser=zabbix

    Timeout=4

    +### Option: SNMPTimeout
    +# Specifies how long we wait for SNMP device (in seconds).
    +#
    +# Mandatory: no
    +# Range: 1-30
    +# Default:
    +# SNMPTimeout=1
    +
    +SNMPTimeout=1
    +
    +### Option: SNMPRetries
    +# Specifies how many times to trying request for SNMP device
    +#
    +# Mandatory: no
    +# Range: 1-10
    +# Default:
    +# SNMPRetries=3
    +
    +SNMPRetries=3
    +
    ### Option: TrapperTimeout
    # Specifies how many seconds trapper may spend processing new data.
    #
    Объявим переменные CONFIG_SNMP_TIMEOUT и CONFIG_SNMP_RETRIES в том же файле, где объявлена переменная CONFIG_TIMEOUT. Это файл src/libs/zbxconf/cfg.c:
    Index: zabbix-3.4.12-1+buster/src/libs/zbxconf/cfg.c
    ===================================================================
    --- zabbix-3.4.12-1+buster.orig/src/libs/zbxconf/cfg.c
    +++ zabbix-3.4.12-1+buster/src/libs/zbxconf/cfg.c
    @@ -31,6 +31,8 @@ char *CONFIG_LOG_FILE = NULL;
    int CONFIG_LOG_FILE_SIZE = 1;
    int CONFIG_ALLOW_ROOT = 0;
    int CONFIG_TIMEOUT = 3;
    +int CONFIG_SNMP_TIMEOUT = 1;
    +int CONFIG_SNMP_RETRIES = 3;

    static int __parse_cfg_file(const char *cfg_file, struct cfg_line *cfg, int level, int optional, int strict);

    Пропишем эти же объявления в заголовочный файл include/cfg.h, на этот раз - как внешние объявления:
    Index: zabbix-3.4.12-1+buster/include/cfg.h
    ===================================================================
    --- zabbix-3.4.12-1+buster.orig/include/cfg.h
    +++ zabbix-3.4.12-1+buster/include/cfg.h
    @@ -46,6 +46,8 @@ extern char *CONFIG_LOG_FILE;
    extern int CONFIG_LOG_FILE_SIZE;
    extern int CONFIG_ALLOW_ROOT;
    extern int CONFIG_TIMEOUT;
    +extern int CONFIG_SNMP_TIMEOUT;
    +extern int CONFIG_SNMP_RETRIES;

    struct cfg_line
    {
    Теперь нужно научить сервер Zabbix и Zabbix-прокси извлекать значения из файлов конфигурации в эти переменные. Отредактируем файлы src/zabbix_server/server.c и src/zabbix_proxy/proxy.c следующим образом:
    Index: zabbix-3.4.12-1+buster/src/zabbix_server/server.c
    ===================================================================
    --- zabbix-3.4.12-1+buster.orig/src/zabbix_server/server.c
    +++ zabbix-3.4.12-1+buster/src/zabbix_server/server.c
    @@ -602,6 +602,10 @@ static void zbx_load_config(ZBX_TASK_EX
    PARM_OPT, 0, 0},
    {"Timeout", &CONFIG_TIMEOUT, TYPE_INT,
    PARM_OPT, 1, 30},
    + {"SNMPTimeout", &CONFIG_SNMP_TIMEOUT, TYPE_INT,
    + PARM_OPT, 1, 30},
    + {"SNMPRetries", &CONFIG_SNMP_RETRIES, TYPE_INT,
    + PARM_OPT, 1, 10},
    {"TrapperTimeout", &CONFIG_TRAPPER_TIMEOUT, TYPE_INT,
    PARM_OPT, 1, 300},
    {"UnreachablePeriod", &CONFIG_UNREACHABLE_PERIOD, TYPE_INT,
    Index: zabbix-3.4.12-1+buster/src/zabbix_proxy/proxy.c
    ===================================================================
    --- zabbix-3.4.12-1+buster.orig/src/zabbix_proxy/proxy.c
    +++ zabbix-3.4.12-1+buster/src/zabbix_proxy/proxy.c
    @@ -623,6 +623,10 @@ static void zbx_load_config(ZBX_TASK_EX
    PARM_OPT, 0, 0},
    {"Timeout", &CONFIG_TIMEOUT, TYPE_INT,
    PARM_OPT, 1, 30},
    + {"SNMPTimeout", &CONFIG_SNMP_TIMEOUT, TYPE_INT,
    + PARM_OPT, 1, 30},
    + {"SNMPRetries", &CONFIG_SNMP_RETRIES, TYPE_INT,
    + PARM_OPT, 1, 10},
    {"TrapperTimeout", &CONFIG_TRAPPER_TIMEOUT, TYPE_INT,
    PARM_OPT, 1, 300},
    {"UnreachablePeriod", &CONFIG_UNREACHABLE_PERIOD, TYPE_INT,
    Реализация функций опроса по SNMP находится в файле src/zabbix_server/poller/checks_snmp.c, но прежде чем редактировать его, заменим в заголовочном файле src/zabbix_server/poller/checks_snmp.h объявление внешней переменной CONFIG_TIMEOUT на объявления внешних переменных CONFIG_SNMP_TIMEOUT и CONFIG_SNMP_RETRIES:
    Index: zabbix-3.4.12-1+buster/src/zabbix_server/poller/checks_snmp.h
    ===================================================================
    --- zabbix-3.4.12-1+buster.orig/src/zabbix_server/poller/checks_snmp.h
    +++ zabbix-3.4.12-1+buster/src/zabbix_server/poller/checks_snmp.h
    @@ -26,7 +26,8 @@
    #include "sysinfo.h"

    extern char *CONFIG_SOURCE_IP;
    -extern int CONFIG_TIMEOUT;
    +extern int CONFIG_SNMP_TIMEOUT;
    +extern int CONFIG_SNMP_RETRIES;

    #ifdef HAVE_NETSNMP
    void zbx_init_snmp(void);
    А теперь можно отредактировать сам файл src/zabbix_server/poller/checks_snmp.c:
    Index: zabbix-3.4.12-1+buster/src/zabbix_server/poller/checks_snmp.c
    ===================================================================
    --- zabbix-3.4.12-1+buster.orig/src/zabbix_server/poller/checks_snmp.c
    +++ zabbix-3.4.12-1+buster/src/zabbix_server/poller/checks_snmp.c
    @@ -456,8 +456,10 @@ static struct snmp_session *zbx_snmp_ope
    break;
    }

    - session.timeout = CONFIG_TIMEOUT * 1000 * 1000; /* timeout of one attempt in microseconds */
    - /* (net-snmp default = 1 second) */
    + session.timeout = CONFIG_SNMP_TIMEOUT * 1000 * 1000; /* timeout of one attempt in microseconds */
    + /* (net-snmp default = 1 second) */
    + session.retries = CONFIG_SNMP_RETRIES - 1; /* number of retries after failed attempt */
    + /* (net-snmp default = 5) */

    #ifdef HAVE_IPV6
    if (SUCCEED != get_address_family(item->interface.addr, &family, error, max_error_len))
    @@ -1095,7 +1097,7 @@ static int zbx_snmp_walk(struct snmp_ses
    pdu->max_repetitions = max_vars;
    }

    - ss->retries = (0 == bulk || (1 == max_vars && == level) ? 1 : 0);
    + ss->retries = (0 == bulk || (1 == max_vars && 0 == level) ? 1 : 0) * (CONFIG_SNMP_RETRIES - 1);

    /* communicate with agent */
    status = snmp_synch_response(ss, pdu, &response);
    @@ -1304,7 +1306,7 @@ static int zbx_snmp_get_values(struct sn
    goto out;
    }

    - ss->retries = (1 == mapping_num && 0 == level ? 1 : 0);
    + ss->retries = (1 == mapping_num && 0 == level ? 1 : 0) * (CONFIG_SNMP_RETRIES - 1);
    retry:
    status = snmp_synch_response(ss, pdu, &response);

    При помощи команды «grep -R CONFIG_TIMEOUT | grep -i snmp» в исходных текстах можно найти ещё один любопытный фрагмент в файле libs/zbxdbcache/dbconfig.c, где можно увидеть отключение повторных попыток опроса по SNMP всего узла, если он не отвечает по SNMP:
    static void DCincrease_disable_until(const ZBX_DC_ITEM *item, ZBX_DC_HOST *host, int now)
    {
    switch (item->type)
    {
    case ITEM_TYPE_ZABBIX:
    if (0 != host->errors_from)
    host->disable_until = now + CONFIG_TIMEOUT;
    break;
    case ITEM_TYPE_SNMPv1:
    case ITEM_TYPE_SNMPv2c:
    case ITEM_TYPE_SNMPv3:
    if (0 != host->snmp_errors_from)
    host->snmp_disable_until = now + CONFIG_TIMEOUT;
    break;
    case ITEM_TYPE_IPMI:
    if (0 != host->ipmi_errors_from)
    host->ipmi_disable_until = now + CONFIG_TIMEOUT;
    break;
    case ITEM_TYPE_JMX:
    if (0 != host->jmx_errors_from)
    host->jmx_disable_until = now + CONFIG_TIMEOUT;
    break;
    default:
    /* nothing to do */;
    }
    }
    Вопрос, стоит ли исправлять значение таймаута для проверок SNMP в этой функции, я оставлю на размышление. Если значение CONFIG_SNMP_TIMEOUT меньше CONFIG_TIMEOUT, то опрос по SNMP после временных перебоев будет приостанавливаться на меньшее время и, соответственно, восстанавливаться быстрее. Нагрузка на процессы Poller при этом может слегка повыситься, т.к. доступность узлов SNMP будет проверяться чаще. Если вы всё же решитесь поменять значение таймаута в этой функции, то вот очевидный патч для файла libs/zbxdbcache/dbconfig.c:
    Index: zabbix-3.4.12-1+buster/src/libs/zbxdbcache/dbconfig.c
    ===================================================================
    --- zabbix-3.4.12-1+buster.orig/src/libs/zbxdbcache/dbconfig.c
    +++ zabbix-3.4.12-1+buster/src/libs/zbxdbcache/dbconfig.c
    @@ -448,7 +448,7 @@ static void DCincrease_disable_until(con
    case ITEM_TYPE_SNMPv2c:
    case ITEM_TYPE_SNMPv3:
    if (0 != host->snmp_errors_from)
    - host->snmp_disable_until = now + CONFIG_TIMEOUT;
    + host->snmp_disable_until = now + CONFIG_SNMP_TIMEOUT;
    break;
    case ITEM_TYPE_IPMI:
    if (0 != host->ipmi_errors_from)
    Полную версию описанной здесь заплатки можно взять по ссылке zabbix3_4_12_snmp_timeout_retries.patch .

    Značky: #zabbix, #debian, #3.4, #Linux, #buster, #linux