Диагностика кратковременных потерь ICMP между центральным филиалом и филиалом
Zabbix, лёгкий blackbox, action-диагностика и контроль ICMP pinger
1. Назначение инструкции
Инструкция описывает схему диагностики кратковременных срабатываний Zabbix вида:
Коммутатор филиала недоступен по ICMP
Задача - не просто получить факт недоступности, а разделить возможные причины:
- проблема маршрута, WAN или провайдера между центральным филиалом и филиалом;
- проблема самого центрального Zabbix: ICMP pinger, очередь проверок, перегрузка;
- проблема конкретного коммутатора, адреса, ACL или management VLAN;
- кратковременная ICMP-проблема, которую обычный action-скрипт может не успеть поймать.
В рамках данной схемы локальная проблема на стороне филиала считается маловероятной, если в момент отвалов в syslog коммутаторов нет событий link down/up, STP, loop, errdisable, reload, storm-control и других тревожных сообщений.
2. Термины
| Термин | Значение |
|---|---|
| Центральный филиал | Площадка, где расположен центральный Zabbix / Zabbix-кластер |
| Филиал | Удалённая площадка с коммутаторами |
| Шлюз филиала | L3-шлюз, маршрутизатор или firewall, через который доступна сеть управления филиала |
| Коммутаторы филиала | Коммутаторы, до которых центральный Zabbix выполняет ICMP-проверки |
| Blackbox | Лёгкий независимый файловый логгер, который постоянно проверяет несколько контрольных адресов |
| Action-диагностика | Диагностический скрипт, запускаемый Zabbix при срабатывании триггера |
| ICMP pinger | Внутренний процесс Zabbix, выполняющий ICMP-проверки icmpping, icmppingloss, icmppingsec |
3. Итоговая архитектура
Центральный филиал
|
+-- Zabbix server / cluster
| +-- ICMP-проверки до коммутаторов филиала
| +-- ICMP-проверка до шлюза филиала
| +-- Zabbix internal items: icmp pinger / queue / poller
| +-- информационные триггеры по состоянию Zabbix
| +-- action-скрипт расширенной диагностики
|
+-- Blackbox-логгер
+-- fping до шлюза филиала
+-- fping до 1-2 ключевых коммутаторов филиала
+-- fping до gateway центрального филиала
+-- fping до контрольного адреса другого направления
Важное решение: blackbox не отправляет значения в Zabbix. Он работает как независимый файловый журнал. Это снижает сложность, не создаёт дополнительных trapper items и не смешивает диагностический журнал с основной системой мониторинга.
4. Логика работы
4.1. Что делает Zabbix
Zabbix штатно проверяет:
- шлюз филиала;
- коммутаторы филиала;
- контрольный адрес другого направления;
- состояние собственных ICMP pinger-процессов;
- очередь проверок;
- общую занятость poller и unreachable poller.
ICMP-проверки icmpping, icmppingloss, icmppingsec являются simple checks. Для них не нужен Zabbix agent на проверяемом устройстве, потому что проверку выполняет сам Zabbix server или proxy.
4.2. Что делает blackbox
Blackbox постоянно, например раз в 5 секунд, проверяет несколько контрольных IP и пишет результат в файл.
Blackbox нужен, чтобы после события можно было посмотреть, был ли реальный кратковременный провал связи в тот момент, когда Zabbix сообщил о недоступности коммутатора.
Blackbox не заменяет Zabbix. Он используется как независимое доказательство.
4.3. Что делает action-скрипт
Action-скрипт запускается только при аварии, например при недоступности шлюза филиала или ключевого коммутатора. Он собирает расширенную диагностику:
pingдо проблемного узла;pingдо шлюза филиала;fpingпо контрольному списку;traceroute;mtr;- состояние CPU/RAM/load;
- состояние сетевых интерфейсов;
- состояние процессов Zabbix/fping;
- последние значения Zabbix internal items через Zabbix API;
- фрагмент blackbox-лога за последние строки.
Важно: action-скрипт может опоздать, если реальная потеря длилась меньше времени срабатывания триггера. Поэтому используется гибрид: лёгкий blackbox фиксирует кратковременные провалы постоянно, а action-скрипт собирает расширенную диагностику только при событии.
5. Этап 1. Контроль шлюза филиала
5.1. Создать host в Zabbix
Пример имени:
FILIAL-GW
Интерфейс:
IP address: <IP_ШЛЮЗА_ФИЛИАЛА>
Шаблон:
Template Module ICMP Ping
или собственный шаблон ICMP.
5.2. Основные ICMP items
Минимально нужны:
icmpping
icmppingloss
icmppingsec
Пример ключа:
icmpping[,5,200,56,1000]
Расшифровка параметров:
| Параметр | Значение |
|---|---|
5 |
Количество ICMP-запросов в одной проверке |
200 |
Интервал между запросами, мс |
56 |
Размер ICMP-пакета |
1000 |
Timeout, мс |
5.3. Триггер недоступности шлюза филиала
Пример выражения:
max(/FILIAL-GW/icmpping[,5,200,56,1000],#3)=0
Рекомендуемое имя:
[HIGH] Шлюз филиала недоступен по ICMP из центрального филиала
Смысл: последние 3 проверки ICMP до шлюза филиала были неуспешны.
6. Этап 2. Зависимости триггеров
Для каждого коммутатора филиала триггер:
Коммутатор филиала недоступен по ICMP
должен зависеть от триггера:
Шлюз филиала недоступен по ICMP
Логика:
Если недоступен шлюз филиала, не нужно создавать пачку вторичных аварий по каждому коммутатору филиала.
Рекомендуемая цепочка:
Коммутатор филиала недоступен
зависит от
Шлюз филиала недоступен
7. Этап 3. Контроль состояния Zabbix ICMP pinger
7.1. Зачем это нужно
Если Zabbix сообщает, что коммутатор филиала недоступен, нужно понять:
Zabbix действительно увидел потерю связи
или
сам Zabbix был перегружен и не успевал выполнять ICMP-проверки.
Для этого создаются internal items на host:
Zabbix server
Тип item:
Zabbix internal
7.2. Обязательные internal items
| Название | Ключ | Тип | Ед. | Интервал |
|---|---|---|---|---|
| Zabbix ICMP pinger avg busy | zabbix[process,"icmp pinger",avg,busy] |
Numeric float | % |
1m |
| Zabbix ICMP pinger max busy | zabbix[process,"icmp pinger",max,busy] |
Numeric float | % |
1m |
| Zabbix ICMP pinger count | zabbix[process,"icmp pinger",count] |
Numeric unsigned | 5m | |
| Zabbix queue over 1m | zabbix[queue,1m,] |
Numeric unsigned | 1m | |
| Zabbix queue over 5m | zabbix[queue,5m,] |
Numeric unsigned | 1m | |
| Zabbix required performance | zabbix[requiredperformance] |
Numeric float | vps |
1m |
7.3. Что означает каждый item
zabbix[process,"icmp pinger",avg,busy]
Показывает среднюю занятость всех ICMP pinger-процессов.
Если значение низкое, например 10-20%, в среднем pinger-процессы не перегружены. Но этот item может скрыть ситуацию, когда один процесс занят почти полностью, а остальные свободны.
zabbix[process,"icmp pinger",max,busy]
Самый важный показатель для данной задачи.
Он показывает занятость самого загруженного ICMP pinger-процесса. Если значение 90-100%, значит хотя бы один ICMP pinger близок к насыщению.
zabbix[process,"icmp pinger",count]
Показывает количество запущенных ICMP pinger-процессов. Это косвенно связано с параметром StartPingers в конфигурации Zabbix server.
Если значение равно 1, а ICMP-проверок много, один pinger может стать узким местом.
zabbix[queue,1m,]
Показывает количество элементов данных, которые задержаны в очереди более чем на 1 минуту.
Если одновременно с этим появляются ICMP-аварии, возможна проблема не в филиале, а в производительности Zabbix.
zabbix[queue,5m,]
Показывает элементы, задержанные более чем на 5 минут. Это уже серьёзный признак деградации мониторинга.
zabbix[requiredperformance]
Показывает требуемую производительность Zabbix в values per second. Сам по себе этот item не является аварией, но полезен для анализа роста нагрузки.
7.4. Желательные internal items
| Название | Ключ | Интервал |
|---|---|---|
| Zabbix poller avg busy | zabbix[process,poller,avg,busy] |
1m |
| Zabbix poller max busy | zabbix[process,poller,max,busy] |
1m |
| Zabbix unreachable poller avg busy | zabbix[process,"unreachable poller",avg,busy] |
1m |
| Zabbix unreachable poller max busy | zabbix[process,"unreachable poller",max,busy] |
1m |
Они нужны, чтобы не смотреть только на ICMP pinger, а видеть общую деградацию процессов Zabbix.
8. Информационные триггеры по состоянию Zabbix
Эти триггеры не заменяют основные аварии по ICMP. Их задача - предупреждать, что в момент ICMP-аварии сам Zabbix мог быть перегружен.
8.1. ICMP pinger загружен более 75%
| Поле | Значение |
|---|---|
| Имя | [INFO] Zabbix ICMP pinger загружен более 75% |
| Severity | Information |
| Выражение | avg(/Zabbix server/zabbix[process,"icmp pinger",max,busy],5m)>75 |
| Смысл | Самый загруженный ICMP pinger в течение 5 минут был занят более чем на 75%. При одновременных ICMP-авариях нужно учитывать возможную задержку проверок. |
8.2. ICMP pinger близок к перегрузке
| Поле | Значение |
|---|---|
| Имя | [WARNING] Zabbix ICMP pinger близок к перегрузке |
| Severity | Warning |
| Выражение | avg(/Zabbix server/zabbix[process,"icmp pinger",max,busy],5m)>90 |
| Смысл | ICMP pinger стабильно загружен. Результаты ICMP-проверок в этот период нельзя считать полностью достоверным доказательством проблемы канала. |
8.3. Есть очередь проверок более 1 минуты
| Поле | Значение |
|---|---|
| Имя | [INFO] В Zabbix есть очередь проверок более 1 минуты |
| Severity | Information |
| Выражение | avg(/Zabbix server/zabbix[queue,1m,],5m)>10 |
| Смысл | Zabbix начал отставать по выполнению проверок. При одновременных ICMP-алертах нужно проверить состояние Zabbix server. |
8.4. Есть очередь проверок более 5 минут
| Поле | Значение |
|---|---|
| Имя | [WARNING] В Zabbix есть очередь проверок более 5 минут |
| Severity | Warning |
| Выражение | last(/Zabbix server/zabbix[queue,5m,])>0 |
| Смысл | Признак деградации мониторинга. Если в этот момент появляются ICMP-проблемы, они могут быть следствием задержек на стороне Zabbix. |
8.5. Количество ICMP pinger-процессов меньше ожидаемого
Пример, если ожидаем минимум 3 процесса:
last(/Zabbix server/zabbix[process,"icmp pinger",count])<3
Рекомендуемое имя:
[INFO] Количество ICMP pinger-процессов меньше ожидаемого
Смысл: проверить параметр StartPingers в конфигурации Zabbix server. Возможно, количество ICMP pinger-процессов недостаточно для текущего количества ICMP-проверок.
9. Этап 4. Лёгкий blackbox без отправки данных в Zabbix
9.1. Назначение blackbox
Blackbox нужен для независимой фиксации кратковременных ICMP-провалов.
Он не должен быть тяжёлым:
не запускать постоянно mtr;
не запускать постоянно traceroute;
не проверять все коммутаторы филиала;
не делать snmpwalk;
не отправлять значения в Zabbix.
Постоянно выполняется только лёгкая ICMP-проверка 3-5 адресов.
9.2. Установка пакетов
sudo apt update
sudo apt install -y fping mtr-tiny traceroute jq curl
Пакеты:
| Пакет | Зачем нужен |
|---|---|
fping |
Лёгкая пакетная ICMP-проверка нескольких адресов |
mtr-tiny |
Диагностика маршрута по событию |
traceroute |
Разовый трассировщик маршрута по событию |
jq |
Формирование и разбор JSON при обращении к Zabbix API |
curl |
HTTP-запросы к Zabbix API |
9.3. Файл целей blackbox
Файл:
/etc/zabbix/blackbox_targets.conf
Содержимое с подробными комментариями:
# Файл целей для лёгкого blackbox-логгера.
# Формат строки:
# <короткое_имя> <IP-адрес>
#
# Короткое имя используется в логах.
# IP-адрес используется для ICMP-проверки.
#
# Пустые строки и строки, начинающиеся с #, игнорируются.
# Шлюз филиала.
# Главная контрольная точка: если недоступен шлюз филиала,
# то проблема, скорее всего, не в одном конкретном коммутаторе.
branch_gw 10.44.100.1
# Ключевой коммутатор филиала.
# Нужен для сравнения с событиями Zabbix по коммутаторам.
branch_core_sw 10.44.100.10
# Один обычный access-коммутатор филиала.
# Нужен как дополнительная точка, чтобы понять:
# падает один адрес или направление до филиала целиком.
branch_access_sw 10.44.100.20
# Gateway центрального филиала.
# Нужен для проверки, не просела ли локальная сеть центрального филиала.
central_gw 10.x.x.1
# Контрольный адрес другого направления.
# Нужен для проверки, проблема только с этим филиалом или шире.
other_direction 10.y.y.1
9.4. Скрипт blackbox
Файл:
/usr/local/sbin/ping-blackbox.sh
Скрипт с подробными комментариями:
#!/bin/bash
# =====================================================================
# ping-blackbox.sh
# =====================================================================
# Назначение:
# Лёгкий независимый ICMP blackbox-логгер.
# Скрипт постоянно проверяет небольшой список контрольных адресов
# и пишет результат в файловый лог.
#
# Важно:
# - Скрипт НЕ отправляет данные в Zabbix.
# - Скрипт НЕ запускает mtr/traceroute постоянно.
# - Скрипт НЕ проверяет все коммутаторы филиала.
# - Основная задача - зафиксировать кратковременный провал связи.
#
# Рекомендуемый запуск:
# Через systemd service от пользователя zabbix.
# =====================================================================
# Файл со списком целей.
# В каждой рабочей строке ожидается два поля:
# 1) короткое имя цели;
# 2) IP-адрес цели.
TARGETS_FILE="/etc/zabbix/blackbox_targets.conf"
# Каталог для основного лога blackbox.
LOG_DIR="/var/log/zabbix/blackbox"
# Каталог для хранения последнего состояния каждой цели.
# Здесь будут небольшие служебные файлы вида branch_gw.state.
STATE_DIR="/var/lib/zabbix/blackbox-state"
# Основной лог blackbox.
LOG_FILE="${LOG_DIR}/blackbox.log"
# Интервал между циклами проверки всех целей.
# Для минутных отвалов достаточно 5 секунд.
# Если нужно ловить очень короткие провалы, можно уменьшить до 1 секунды.
INTERVAL_SEC=5
# Даже если состояние не меняется, пишем контрольную строку раз в минуту.
# Это помогает понять, что сам blackbox в это время работал.
CONTROL_LOG_EVERY_SEC=60
# Создаём каталоги, если они ещё не существуют.
mkdir -p "$LOG_DIR" "$STATE_DIR"
# Если файла целей нет, пишем ошибку и завершаемся.
# Без списка целей дальнейшая работа бессмысленна.
if [ ! -f "$TARGETS_FILE" ]; then
echo "$(date -Is) ERROR targets file not found: $TARGETS_FILE" >> "$LOG_FILE"
exit 1
fi
# Основной бесконечный цикл.
# Скрипт работает постоянно, пока его не остановит systemd или администратор.
while true; do
# Текущее время в двух форматах:
# NOW_EPOCH нужен для расчёта интервалов;
# NOW_ISO нужен для читаемого лога.
NOW_EPOCH="$(date +%s)"
NOW_ISO="$(date -Is)"
# Читаем файл целей построчно.
while read -r NAME IP; do
# Пропускаем пустые строки.
[[ -z "$NAME" ]] && continue
# Пропускаем комментарии.
[[ "$NAME" =~ ^# ]] && continue
# Если IP не указан, строка считается некорректной и пропускается.
[[ -z "$IP" ]] && continue
# Файл с последним состоянием конкретной цели: OK / FAIL / UNKNOWN.
STATE_FILE="${STATE_DIR}/${NAME}.state"
# Файл с временем последней записи в лог по данной цели.
LAST_LOG_FILE="${STATE_DIR}/${NAME}.lastlog"
# По умолчанию считаем, что предыдущее состояние неизвестно.
PREV_STATE="UNKNOWN"
# Если файл состояния уже есть, читаем предыдущее состояние из него.
[ -f "$STATE_FILE" ] && PREV_STATE="$(cat "$STATE_FILE")"
# По умолчанию считаем, что контрольная строка ещё ни разу не писалась.
LAST_LOG_EPOCH=0
# Если файл времени последней записи есть, читаем значение.
[ -f "$LAST_LOG_FILE" ] && LAST_LOG_EPOCH="$(cat "$LAST_LOG_FILE")"
# Выполняем один ICMP-запрос через fping.
# -C 1 - один запрос к цели;
# -q - тихий режим, итог пишется компактно;
# -t 1000 - timeout 1000 мс.
#
# fping часто пишет результат в stderr, поэтому 2>&1 обязателен.
RESULT="$(fping -C 1 -q -t 1000 "$IP" 2>&1)"
RC=$?
# Если код возврата 0, цель ответила.
if [ "$RC" -eq 0 ]; then
CURRENT_STATE="OK"
# Пример строки fping:
# 10.44.100.1 : 18.40
# Извлекаем значение после двоеточия и убираем пробелы.
RTT_MS="$(echo "$RESULT" | awk -F':' '{gsub(/ /,"",$2); print $2}')"
# Если по какой-то причине RTT не распознан, пишем unknown.
[ -z "$RTT_MS" ] && RTT_MS="unknown"
else
# Любой ненулевой код возврата считаем неуспешной ICMP-проверкой.
CURRENT_STATE="FAIL"
RTT_MS="timeout"
fi
# Флаг необходимости записи в лог.
NEED_LOG=0
# Записываем в лог при изменении состояния.
# Это главное событие: OK -> FAIL или FAIL -> OK.
if [ "$CURRENT_STATE" != "$PREV_STATE" ]; then
NEED_LOG=1
fi
# Рассчитываем, сколько секунд прошло с последней контрольной записи.
AGE=$((NOW_EPOCH - LAST_LOG_EPOCH))
# Даже если состояние не менялось, раз в минуту пишем контрольную строку.
# Это подтверждает, что blackbox в этот период был запущен.
if [ "$AGE" -ge "$CONTROL_LOG_EVERY_SEC" ]; then
NEED_LOG=1
fi
# Если запись нужна, пишем строку в лог и обновляем служебные файлы.
if [ "$NEED_LOG" -eq 1 ]; then
echo "${NOW_ISO} ${NAME} ${IP} ${CURRENT_STATE} rtt_ms=${RTT_MS} prev=${PREV_STATE}" >> "$LOG_FILE"
echo "$CURRENT_STATE" > "$STATE_FILE"
echo "$NOW_EPOCH" > "$LAST_LOG_FILE"
fi
done < "$TARGETS_FILE"
# Пауза до следующего цикла проверки всех целей.
sleep "$INTERVAL_SEC"
done
9.5. Права на blackbox
sudo chmod +x /usr/local/sbin/ping-blackbox.sh
sudo mkdir -p /var/log/zabbix/blackbox /var/lib/zabbix/blackbox-state
sudo chown -R zabbix:zabbix /var/log/zabbix/blackbox /var/lib/zabbix/blackbox-state
10. systemd service для blackbox
Файл:
/etc/systemd/system/ping-blackbox.service
Содержимое с комментариями:
# =====================================================================
# ping-blackbox.service
# =====================================================================
# Назначение:
# Сервис systemd для постоянного запуска лёгкого ICMP blackbox-логгера.
# =====================================================================
[Unit]
# Человекочитаемое описание сервиса.
Description=Lightweight ICMP blackbox logger
# Запускать сервис после появления сети.
After=network-online.target
# Явно просим systemd дождаться network-online.target.
Wants=network-online.target
[Service]
# Тип simple подходит для обычного foreground-скрипта.
Type=simple
# Запускаем от пользователя zabbix, чтобы не использовать root без необходимости.
User=zabbix
# Группа также zabbix, чтобы были права на каталоги /var/log/zabbix и /var/lib/zabbix.
Group=zabbix
# Основной исполняемый файл сервиса.
ExecStart=/usr/local/sbin/ping-blackbox.sh
# Автоматически перезапускать сервис при аварийном завершении.
Restart=always
# Пауза перед перезапуском, чтобы не получить бесконечный быстрый restart-loop.
RestartSec=5
[Install]
# Запускать сервис в обычном multi-user режиме.
WantedBy=multi-user.target
Команды запуска:
sudo systemctl daemon-reload
sudo systemctl enable --now ping-blackbox.service
sudo systemctl status ping-blackbox.service
Проверка лога:
tail -f /var/log/zabbix/blackbox/blackbox.log
11. logrotate для blackbox
Файл:
/etc/logrotate.d/ping-blackbox
Содержимое с комментариями:
# Ротация логов лёгкого blackbox-логгера.
/var/log/zabbix/blackbox/*.log {
# Ротировать каждый день.
daily
# Хранить 30 архивных ротаций.
rotate 30
# Сжимать старые логи.
compress
# Не выдавать ошибку, если лог-файла нет.
missingok
# Не ротировать пустой файл.
notifempty
# Копировать текущий лог в архив и обнулять исходный файл.
# Это удобно для простого bash-скрипта, который постоянно пишет в один файл.
copytruncate
}
Проверка конфигурации logrotate:
sudo logrotate -d /etc/logrotate.d/ping-blackbox
12. Конфигурация доступа к Zabbix API для action-скрипта
Для добавления в action-лог последних значений internal items используется Zabbix API.
Файл:
/etc/zabbix/netdiag_api.conf
Содержимое с комментариями:
# =====================================================================
# netdiag_api.conf
# =====================================================================
# Назначение:
# Конфигурация доступа action-скрипта к Zabbix API.
# Используется только для чтения последних значений internal items.
#
# Важно:
# Файл содержит API token. Нужно ограничить права доступа.
# =====================================================================
# URL API вашего Zabbix frontend.
# Обычно это адрес вида:
# https://zabbix.example.local/api_jsonrpc.php
ZBX_API_URL="https://zabbix.example.local/api_jsonrpc.php"
# API token пользователя Zabbix с минимально необходимыми правами на чтение.
# Рекомендуется отдельный технический пользователь или отдельный API token.
ZBX_API_TOKEN="PASTE_API_TOKEN_HERE"
# Техническое имя host в Zabbix, на котором созданы internal items.
# Важно: использовать именно техническое имя host, а не только visible name.
ZBX_INTERNAL_HOST="Zabbix server"
Права:
sudo chown root:zabbix /etc/zabbix/netdiag_api.conf
sudo chmod 640 /etc/zabbix/netdiag_api.conf
Если API-вставка пока не нужна, этот файл можно не создавать. Action-скрипт должен корректно пропустить блок Zabbix internal state.
13. Action-диагностика по событию
13.1. Когда запускать action-скрипт
Рекомендуемый основной запуск:
Шлюз филиала недоступен по ICMP
Почему лучше запускать по шлюзу:
Если падает весь филиал, не нужно запускать отдельную диагностику по каждому коммутатору.
Дополнительно можно запускать по ключевому коммутатору, если шлюз филиала доступен, но конкретный коммутатор недоступен.
13.2. Action-скрипт
Файл:
/usr/local/sbin/zabbix-netdiag.sh
Скрипт с подробными комментариями:
#!/bin/bash
# =====================================================================
# zabbix-netdiag.sh
# =====================================================================
# Назначение:
# Диагностический скрипт для запуска из Zabbix action.
# Собирает сетевой и системный снимок в момент ICMP-проблемы.
#
# Что собирает:
# - ping до проблемного узла;
# - ping до шлюза филиала;
# - fping по контрольному списку blackbox;
# - traceroute до проблемного узла;
# - mtr до проблемного узла;
# - нагрузку ОС;
# - статистику сетевых интерфейсов;
# - процессы zabbix/fping/mtr/traceroute;
# - последние строки blackbox-лога;
# - последние значения Zabbix internal items через API, если настроен API.
#
# Ожидаемые параметры:
# $1 - Zabbix EVENT.ID
# $2 - Zabbix HOST.NAME
# $3 - IP или DNS проверяемого узла, обычно {HOST.CONN}
# $4 - IP шлюза филиала, обычно {$BRANCH_GW}
#
# Пример запуска из Zabbix:
# /usr/local/sbin/zabbix-netdiag.sh "{EVENT.ID}" "{HOST.NAME}" "{HOST.CONN}" "{$BRANCH_GW}"
# =====================================================================
# Строгий режим не включаем через set -e,
# потому что часть диагностических команд может завершаться с ошибкой.
# Например, ping может вернуть timeout - это ожидаемая диагностическая ситуация.
EVENT_ID="$1"
HOST_NAME="$2"
TARGET_IP="$3"
BRANCH_GW="$4"
# Каталог для диагностических логов.
OUT_DIR="/var/log/zabbix/netdiag"
# Файл со списком целей blackbox.
TARGETS_FILE="/etc/zabbix/blackbox_targets.conf"
# Основной blackbox-лог.
BLACKBOX_LOG="/var/log/zabbix/blackbox/blackbox.log"
# Конфигурация доступа к Zabbix API.
API_CONF="/etc/zabbix/netdiag_api.conf"
# Формируем timestamp для имени файла.
TS="$(date +%Y%m%d_%H%M%S)"
# Безопасно чистим имя host для использования в имени файла.
# Заменяем потенциально проблемные символы на подчёркивание.
SAFE_HOST_NAME="$(echo "$HOST_NAME" | sed -E 's/[^A-Za-z0-9_.-]+/_/g')"
# Итоговый файл диагностики.
OUT_FILE="${OUT_DIR}/${TS}_event_${EVENT_ID}_${SAFE_HOST_NAME}.log"
# Создаём каталог для логов, если он отсутствует.
mkdir -p "$OUT_DIR"
# ---------------------------------------------------------------------
# Функция: section
# Назначение:
# Печатать красивый заголовок раздела в диагностический лог.
# ---------------------------------------------------------------------
section() {
echo
echo "===== $1 ====="
}
# ---------------------------------------------------------------------
# Функция: run_cmd
# Назначение:
# Выполнить диагностическую команду и записать:
# - саму команду;
# - stdout/stderr;
# - код возврата.
#
# Почему это важно:
# Если команда завершилась ошибкой, это тоже диагностическая информация.
# ---------------------------------------------------------------------
run_cmd() {
echo "+ $*"
"$@"
local RC=$?
echo "Command exit code: ${RC}"
echo
}
# ---------------------------------------------------------------------
# Функция: load_api_config
# Назначение:
# Загрузить параметры Zabbix API из /etc/zabbix/netdiag_api.conf.
#
# Если файла нет, API-блок будет пропущен, а остальная диагностика выполнится.
# ---------------------------------------------------------------------
load_api_config() {
if [ -f "$API_CONF" ]; then
# shellcheck disable=SC1090
. "$API_CONF"
return 0
fi
return 1
}
# ---------------------------------------------------------------------
# Функция: zbx_api_call
# Назначение:
# Выполнить JSON-RPC запрос к Zabbix API.
#
# Требования:
# - установлен curl;
# - установлен jq;
# - задан ZBX_API_URL;
# - задан ZBX_API_TOKEN.
# ---------------------------------------------------------------------
zbx_api_call() {
local PAYLOAD="$1"
curl -sk \
-H 'Content-Type: application/json-rpc' \
-d "$PAYLOAD" \
"$ZBX_API_URL"
}
# ---------------------------------------------------------------------
# Функция: zbx_get_hostid
# Назначение:
# Получить hostid по техническому имени host Zabbix.
#
# Важно:
# ZBX_INTERNAL_HOST должен совпадать с техническим именем host,
# на котором созданы internal items.
# ---------------------------------------------------------------------
zbx_get_hostid() {
local PAYLOAD
PAYLOAD="$(jq -n \
--arg token "$ZBX_API_TOKEN" \
--arg host "$ZBX_INTERNAL_HOST" \
'{
jsonrpc: "2.0",
method: "host.get",
params: {
output: ["hostid", "host", "name"],
filter: { host: [$host] }
},
auth: $token,
id: 1
}')"
zbx_api_call "$PAYLOAD" | jq -r '.result[0].hostid // empty'
}
# ---------------------------------------------------------------------
# Функция: zbx_get_item_last_by_key
# Назначение:
# Получить последнее значение item по hostid и точному key_.
#
# На выходе печатается строка:
# <key> = <lastvalue>, lastclock=<timestamp>
# ---------------------------------------------------------------------
zbx_get_item_last_by_key() {
local HOSTID="$1"
local ITEM_KEY="$2"
local PAYLOAD RESPONSE NAME VALUE CLOCK CLOCK_HUMAN
PAYLOAD="$(jq -n \
--arg token "$ZBX_API_TOKEN" \
--arg hostid "$HOSTID" \
--arg key "$ITEM_KEY" \
'{
jsonrpc: "2.0",
method: "item.get",
params: {
output: ["itemid", "name", "key_", "lastvalue", "lastclock"],
hostids: [$hostid],
filter: { key_: [$key] },
limit: 1
},
auth: $token,
id: 1
}')"
RESPONSE="$(zbx_api_call "$PAYLOAD")"
NAME="$(echo "$RESPONSE" | jq -r '.result[0].name // empty')"
VALUE="$(echo "$RESPONSE" | jq -r '.result[0].lastvalue // empty')"
CLOCK="$(echo "$RESPONSE" | jq -r '.result[0].lastclock // empty')"
if [ -n "$CLOCK" ] && [ "$CLOCK" != "0" ]; then
CLOCK_HUMAN="$(date -d "@$CLOCK" -Is 2>/dev/null || echo "$CLOCK")"
else
CLOCK_HUMAN="unknown"
fi
if [ -z "$VALUE" ]; then
echo "${ITEM_KEY} = NOT_FOUND_OR_EMPTY"
else
echo "${ITEM_KEY} = ${VALUE}, lastclock=${CLOCK_HUMAN}, name=${NAME}"
fi
}
# ---------------------------------------------------------------------
# Функция: write_zabbix_internal_state
# Назначение:
# Записать в лог последние значения Zabbix internal items.
#
# Если API не настроен, блок будет аккуратно пропущен.
# ---------------------------------------------------------------------
write_zabbix_internal_state() {
section "ZABBIX INTERNAL STATE"
if ! command -v jq >/dev/null 2>&1; then
echo "jq not installed; skip Zabbix API block"
return
fi
if ! command -v curl >/dev/null 2>&1; then
echo "curl not installed; skip Zabbix API block"
return
fi
if ! load_api_config; then
echo "API config not found: $API_CONF"
echo "Skip Zabbix internal state block"
return
fi
if [ -z "$ZBX_API_URL" ] || [ -z "$ZBX_API_TOKEN" ] || [ -z "$ZBX_INTERNAL_HOST" ]; then
echo "API config is incomplete; skip Zabbix internal state block"
return
fi
local HOSTID
HOSTID="$(zbx_get_hostid)"
if [ -z "$HOSTID" ]; then
echo "Cannot find Zabbix host by technical name: $ZBX_INTERNAL_HOST"
echo "Check ZBX_INTERNAL_HOST in $API_CONF"
return
fi
echo "Zabbix API URL: $ZBX_API_URL"
echo "Zabbix internal host: $ZBX_INTERNAL_HOST"
echo "Zabbix internal hostid: $HOSTID"
echo
zbx_get_item_last_by_key "$HOSTID" 'zabbix[process,"icmp pinger",avg,busy]'
zbx_get_item_last_by_key "$HOSTID" 'zabbix[process,"icmp pinger",max,busy]'
zbx_get_item_last_by_key "$HOSTID" 'zabbix[process,"icmp pinger",count]'
zbx_get_item_last_by_key "$HOSTID" 'zabbix[queue,1m,]'
zbx_get_item_last_by_key "$HOSTID" 'zabbix[queue,5m,]'
zbx_get_item_last_by_key "$HOSTID" 'zabbix[requiredperformance]'
zbx_get_item_last_by_key "$HOSTID" 'zabbix[process,poller,avg,busy]'
zbx_get_item_last_by_key "$HOSTID" 'zabbix[process,poller,max,busy]'
zbx_get_item_last_by_key "$HOSTID" 'zabbix[process,"unreachable poller",avg,busy]'
zbx_get_item_last_by_key "$HOSTID" 'zabbix[process,"unreachable poller",max,busy]'
}
# ---------------------------------------------------------------------
# Основная запись диагностического файла.
# Весь вывод внутри блока перенаправляется в OUT_FILE.
# ---------------------------------------------------------------------
{
echo "===== NETDIAG START ====="
echo "Date: $(date -Is)"
echo "Event ID: ${EVENT_ID}"
echo "Host name: ${HOST_NAME}"
echo "Target IP: ${TARGET_IP}"
echo "Branch gateway: ${BRANCH_GW}"
echo "Output file: ${OUT_FILE}"
section "PING TARGET"
if [ -n "$TARGET_IP" ]; then
run_cmd ping -c 20 -i 0.2 -W 1 "$TARGET_IP"
else
echo "TARGET_IP is empty; skip ping target"
fi
section "PING BRANCH GATEWAY"
if [ -n "$BRANCH_GW" ]; then
run_cmd ping -c 20 -i 0.2 -W 1 "$BRANCH_GW"
else
echo "BRANCH_GW is empty; skip ping branch gateway"
fi
section "FPING CONTROL TARGETS"
if [ -f "$TARGETS_FILE" ]; then
echo "+ fping control targets from $TARGETS_FILE"
awk 'NF >= 2 && $1 !~ /^#/ {print $2}' "$TARGETS_FILE" | fping -C 5 -q -t 1000 2>&1
echo "Command exit code: $?"
else
echo "Targets file not found: $TARGETS_FILE"
fi
section "TRACEROUTE TARGET"
if [ -n "$TARGET_IP" ]; then
run_cmd traceroute -n -w 1 -q 3 "$TARGET_IP"
else
echo "TARGET_IP is empty; skip traceroute"
fi
section "MTR TARGET"
if [ -n "$TARGET_IP" ]; then
run_cmd mtr -r -c 30 -n "$TARGET_IP"
else
echo "TARGET_IP is empty; skip mtr"
fi
section "OS LOAD"
run_cmd uptime
section "MEMORY"
run_cmd free -m
section "TOP SNAPSHOT"
top -b -n 1 | head -n 30
section "NETWORK INTERFACES STATS"
run_cmd ip -s link
section "ZABBIX / FPING PROCESSES"
ps -eo pid,comm,%cpu,%mem,etime,args | grep -E 'zabbix|fping|mtr|traceroute' | grep -v grep
section "BLACKBOX LAST 200 LINES"
if [ -f "$BLACKBOX_LOG" ]; then
tail -n 200 "$BLACKBOX_LOG"
else
echo "Blackbox log not found: $BLACKBOX_LOG"
fi
write_zabbix_internal_state
section "NETDIAG END"
echo "Date: $(date -Is)"
} >> "$OUT_FILE" 2>&1
# Печатаем путь к файлу в stdout.
# Это удобно, если Zabbix action или ручной запуск сохраняет вывод команды.
echo "$OUT_FILE"
13.3. Права на action-скрипт
sudo chmod +x /usr/local/sbin/zabbix-netdiag.sh
sudo mkdir -p /var/log/zabbix/netdiag
sudo chown -R zabbix:zabbix /var/log/zabbix/netdiag
14. Настройка Zabbix action
14.1. Макрос шлюза филиала
На уровне host group филиала или конкретного host создать макрос:
{$BRANCH_GW}=10.44.100.1
14.2. Команда запуска
Пример команды:
/usr/local/sbin/zabbix-netdiag.sh "{EVENT.ID}" "{HOST.NAME}" "{HOST.CONN}" "{$BRANCH_GW}"
14.3. Рекомендуемое условие запуска
Основной вариант:
Trigger name contains: Шлюз филиала недоступен
или через tag:
scope=netdiag
site=<имя_филиала>
14.4. Почему не запускать по каждому коммутатору
Если недоступен весь филиал, Zabbix может создать много событий по коммутаторам. Если на каждое событие запускать mtr, traceroute, ping, это создаст лишнюю нагрузку и шум.
Поэтому рекомендуемая логика:
1. Основной action запускать по недоступности шлюза филиала.
2. По отдельному коммутатору запускать только если шлюз филиала доступен.
3. Для массовых событий использовать зависимости триггеров.
15. Как анализировать результаты
15.1. Вероятная проблема WAN / провайдер / маршрут
Признаки:
Zabbix: шлюз филиала недоступен.
Zabbix: несколько коммутаторов филиала недоступны.
Blackbox: branch_gw FAIL.
Blackbox: central_gw OK.
Blackbox: other_direction OK.
Zabbix icmp pinger max busy низкий.
Zabbix queue 1m = 0.
Zabbix queue 5m = 0.
Syslog коммутаторов филиала чистый.
Вывод:
Наиболее вероятна проблема между центральным филиалом и филиалом:
WAN / провайдер / маршрутизация / firewall.
15.2. Вероятная проблема самого Zabbix
Признаки:
Zabbix: коммутаторы филиала недоступны.
Zabbix: шлюз филиала недоступен.
Blackbox не подтверждает потерю направления.
Zabbix icmp pinger max busy = 90-100%.
Zabbix queue 1m растёт.
Zabbix queue 5m больше 0.
Вывод:
В этот период сам Zabbix находился в деградации.
ICMP-аварию нельзя считать чистым доказательством проблемы канала.
Сначала проверить производительность Zabbix server / кластера.
15.3. Вероятная проблема центрального филиала
Признаки:
Blackbox: branch_gw FAIL.
Blackbox: other_direction FAIL.
Blackbox: central_gw FAIL или нестабилен.
Вывод:
Проблема шире одного филиала.
Проверить сеть центрального филиала, gateway, uplink, маршрутизацию, firewall.
15.4. Вероятная проблема конкретного коммутатора
Признаки:
Zabbix: один коммутатор филиала недоступен.
Zabbix: шлюз филиала доступен.
Другие коммутаторы филиала доступны.
Blackbox: branch_gw OK.
Zabbix pinger не перегружен.
Queue нет.
Вывод:
Проблема ближе к конкретному коммутатору, его адресу, ACL, management VLAN или обработке ICMP на самом устройстве.
16. Рекомендуемая последовательность внедрения
16.1. Минимальный этап
- Создать host
FILIAL-GW. - Добавить ICMP-проверки до шлюза филиала.
- Настроить зависимости: коммутаторы филиала зависят от шлюза филиала.
- Создать internal items:
zabbix[process,"icmp pinger",avg,busy];zabbix[process,"icmp pinger",max,busy];zabbix[process,"icmp pinger",count];zabbix[queue,1m,];zabbix[queue,5m,];zabbix[requiredperformance].
- Создать информационные триггеры по pinger и queue.
16.2. Blackbox
- Установить
fping,mtr-tiny,traceroute,jq,curl. - Создать
/etc/zabbix/blackbox_targets.conf. - Создать
/usr/local/sbin/ping-blackbox.sh. - Создать systemd service.
- Настроить logrotate.
- Проверить, что лог пишется и фиксирует смену состояния.
16.3. Action-диагностика
- Создать
/usr/local/sbin/zabbix-netdiag.sh. - Настроить запуск по триггеру недоступности шлюза филиала.
- Передать в скрипт
EVENT.ID,HOST.NAME,HOST.CONNиBRANCH_GW. - Сохранять диагностические логи в
/var/log/zabbix/netdiag/. - При необходимости настроить
/etc/zabbix/netdiag_api.confдля чтения последних значений internal items через API. - Добавить в уведомление путь к диагностическому файлу или команду для просмотра последних логов.
17. Проверка работоспособности
17.1. Проверить blackbox
sudo systemctl status ping-blackbox.service
sudo tail -f /var/log/zabbix/blackbox/blackbox.log
Ожидаемый пример строк:
2026-05-25T12:10:00+03:00 branch_gw 10.44.100.1 OK rtt_ms=18.4 prev=UNKNOWN
2026-05-25T12:15:25+03:00 branch_gw 10.44.100.1 FAIL rtt_ms=timeout prev=OK
2026-05-25T12:16:03+03:00 branch_gw 10.44.100.1 OK rtt_ms=20.1 prev=FAIL
17.2. Проверить action-скрипт вручную
sudo -u zabbix /usr/local/sbin/zabbix-netdiag.sh "manual-test" "TEST-HOST" "10.44.100.10" "10.44.100.1"
Проверить созданный лог:
ls -lh /var/log/zabbix/netdiag/
tail -n 100 /var/log/zabbix/netdiag/*.log
17.3. Проверить Zabbix API-блок
Проверить, что файл доступен пользователю zabbix:
sudo -u zabbix test -r /etc/zabbix/netdiag_api.conf && echo OK
Проверить ручной запуск action-скрипта и убедиться, что в логе появился раздел:
===== ZABBIX INTERNAL STATE =====
18. Важные замечания по Zabbix HA / кластеру
Если Zabbix работает в HA-режиме, нужно разделять:
1. Zabbix internal items - состояние активного Zabbix server как приложения.
2. OS-level monitoring каждой ноды - CPU/RAM/Disk/Network конкретной VM или сервера.
Рекомендуемая структура:
Zabbix server application
internal items:
- icmp pinger busy
- queue
- requiredperformance
Zabbix node 01
agent items:
- CPU
- RAM
- disk I/O
- network interface
Zabbix node 02
agent items:
- CPU
- RAM
- disk I/O
- network interface
Иначе можно увидеть, что Zabbix-приложение вроде бы работает нормально, но не заметить проблему конкретной ноды, сетевого интерфейса или дисковой подсистемы.
19. Итоговая логика системы
Zabbix ICMP
обнаруживает проблему.
Шлюз филиала
показывает, проблема в одном коммутаторе или во всём направлении.
Dependencies
подавляют вторичные аварии по коммутаторам.
Zabbix internal items
показывают, был ли сам Zabbix способен нормально выполнять ICMP-проверки.
Information triggers
предупреждают о возможной деградации Zabbix.
Blackbox
независимо фиксирует кратковременные провалы в файловом логе.
Action-скрипт
собирает расширенную диагностику при аварии.
Главный результат: после очередного события будет не просто сообщение:
Коммутатор филиала недоступен
а диагностическая картина:
- шлюз филиала был доступен или недоступен;
- несколько коммутаторов филиала падали одновременно или нет;
- центральный gateway был доступен или нет;
- другое направление было доступно или нет;
- ICMP pinger Zabbix был перегружен или нет;
- очередь Zabbix была или нет;
- blackbox подтвердил кратковременный провал или нет;
mtr/tracerouteпоказали проблему на маршруте или нет.
По этой картине можно обоснованно разделить:
коммутатор / филиал / WAN-провайдер / центральный филиал / сам Zabbix.