АйТи Фреш
Главная / Статьи / DevOps и автоматизация
DevOps и автоматизация

Поставил maxdelay в logrt Zabbix 7.4 — алерты пошли быстрее, а часть строк исчезла. Агент их не дочитает

Автор: Семёнов Евгений Сергеевич, директор ООО «АйТи-Фреш» · · ~21 мин чтения
Поставил maxdelay в logrt Zabbix 7.4 — алерты пошли быстрее, а часть строк исчезла. Агент их не дочитает
Иллюстрация к статье «Поставил maxdelay в logrt Zabbix 7.4 — алерты пошли быстрее, а часть строк исчезла. Агент их не дочитает».

Ситуация узнаваемая: элемент logrt[] отдавал ошибки с опозданием на полчаса, вы дописали в ключ maxdelay, и алерты полетели почти мгновенно. Радость длится до первого разбора инцидента, когда выясняется, что в истории нет ровно той строки, ради которой всё и затевалось. Отвечаю сразу на вопрос из заголовка: нет, агент их не дочитает — никогда. Дальше разбираю механику прыжка, показываю, как за две минуты отличить намеренный пропуск от кривой регулярки, привожу разбор случая в компании на 34 рабочих места, где отставание в 40 минут лечилось вообще другими параметрами, и даю чек-лист: что крутить в первую очередь, а на что можно забить.

maxdelay — это не ускорение, это разрешение не читать

Начну с главного, чтобы дальше читалось спокойно. maxdelay в ключах log[] и logrt[] работает не по принципу «читай быстрее», а по принципу «если не успеваешь — не читай». Значение по умолчанию 0 пропуск запрещает: агент дочитает журнал целиком, пусть даже с отставанием в час. Любое значение больше нуля — это ваша письменная санкция на потерю строк. В документации Zabbix это написано прямым текстом, отдельным предупреждением: указание maxdelay > 0 может привести к игнорированию важных записей журнала и пропущенным оповещениям.

Механика такая. На каждой проверке агент запоминает три величины: сколько байт он обработал, сколько байт осталось до конца файла и сколько секунд на обработку ушло. Из них считается оценочная задержка — примерное число секунд, которое понадобится, чтобы разобрать весь хвост. Если оценка укладывается в maxdelay, агент читает файл обычным порядком. Если не укладывается — он вычисляет новую позицию в файле, такую, чтобы остаток разобрался за отведённое время, и переставляет туда свой указатель. Потом сдвигается вперёд до начала ближайшей целой строки, чтобы не отхватить пол-записи.

И вот ключевой момент, из-за которого и возникает вопрос «а он их потом дочитает?». Пропущенный кусок не попадает ни в какой буфер. Это не очередь и не отложенная задача. Агент физически не читает эти байты — он только двигает offset. Догонять нечего: с точки зрения агента этих строк не существовало. Ни через минуту, ни после перезапуска службы, ни после zabbix_agentd -R log_level_increase. Единственное место, где они остались, — сам файл на диске.

Порядок параметров стоит держать перед глазами, потому что maxdelay сидит седьмым и его легко воткнуть не в ту позицию (а лишняя запятая молча превращает элемент в неподдерживаемый):

log[file,<regexp>,<encoding>,<maxlines>,<mode>,<output>,<maxdelay>,<options>,<persistent dir>]
logrt[file regexp,<regexp>,<encoding>,<maxlines>,<mode>,<output>,<maxdelay>,<options>,<persistent dir>]
log.count[file,<regexp>,<encoding>,<maxproclines>,<mode>,<maxdelay>,<options>,<persistent dir>]
logrt.count[file regexp,<regexp>,<encoding>,<maxproclines>,<mode>,<maxdelay>,<options>,<persistent dir>]

Обратите внимание: у счётных ключей .count нет параметра output, и четвёртый параметр называется maxproclines — это не «сколько отправить», а «сколько разобрать». Путаница между maxlines и maxproclines — вторая по частоте причина того, что «счётчик занижает».

maxdelay > 0 — это осознанный размен полноты на свежесть. Если вы не готовы вслух произнести фразу «часть ошибок мы согласны не увидеть», оставляйте 0 и чините отставание другими способами.

Как за две минуты отличить прыжок от кривой регулярки

Когда строки «исчезают», гипотез обычно две: агент прыгнул через кусок файла или регулярное выражение просто не матчит. Разводятся они по логу самого агента, и это первое, что я открываю, а не веб-интерфейс Zabbix. Прыжок агент фиксирует явным сообщением примерно такого вида: item:"logrt[...]" logfile:"..." skipping N bytes (from byte X to byte Y) to meet maxdelay. Формулировка «to meet maxdelay» — то, что нужно грепать.

# Linux
grep -n 'to meet maxdelay' /var/log/zabbix/zabbix_agentd.log | tail -n 20

# Windows (агент пишет в C:\Program Files\Zabbix Agent\zabbix_agentd.log)
Select-String -Path 'C:\Program Files\Zabbix Agent\zabbix_agentd.log' -Pattern 'to meet maxdelay' | Select-Object -Last 20

Если такие строки есть — вопрос закрыт, это не баг, это ваша конфигурация. Дальше остаётся посчитать, сколько байт суммарно улетело в никуда: сложите N по всем сообщениям за сутки и сопоставьте с размером журнала. Я обычно делаю это один раз, чтобы показать заказчику цифру. Когда человек видит, что мониторинг официально не посмотрел 40 % журнала, дискуссия про «зато быстро» заканчивается сама.

Если строк про maxdelay нет вообще, а данных всё равно не видно — причина другая, и maxdelay тут ни при чём. Уровня логирования DebugLevel=3 хватает, чтобы увидеть сам факт прыжка; поднимать до 4 или 5 имеет смысл только когда нужно видеть, где именно агент стоит в файле и как он трекает ротацию. Делается это на лету, без перезапуска службы: zabbix_agentd -R log_level_increase, и обязательно верните обратно через log_level_decrease — на четвёрке шумный агент способен за ночь налить гигабайт собственного лога, и я такое чинил дважды.

Неподдерживаемый элемент Zabbix не молчит — он пишет причину прямо в информации об элементе данных. Примерно половина обращений «мониторинг ничего не видит» закрывается чтением этой одной строки.
Поставил maxdelay в logrt Zabbix 7.4 — алерты пошли быстрее, а часть строк исчезла. Агент их не дочитает — схема
Схема к статье. Открыть схему в полном размере

Разбор: «ФитоГрад», озеленение офисов, 34 рабочих места

Условный клиент — компания по озеленению офисов «ФитоГрад», 34 рабочих места: менеджеры, выездные флористы с планшетами, склад растений и небольшая бухгалтерия. Серверов немного: два узла на Debian — интернет-витрина с калькулятором фитостен и сервис обмена заказами между сайтом, 1С и банк-клиентом. Zabbix 7.4, агенты работают в активном режиме и ходят прямо на сервер, прокси нет. Прикладной журнал сервиса обмена — один файл в сутки по маске app-YYYYMMDD.log, порядка 450 тыс. строк за сутки: сервис пишет каждую синхронизацию остатков и каждый опрос банка. Жалоба звучала так: «сообщение о падении обмена с банком приходит через 40–60 минут, к этому моменту оплаты от клиентов уже не разнесены, а флористы едут на объекты без подтверждённых заказов». Приходящий админ нашёл maxdelay, поставил 60 — и стало приходить за минуту. Все выдохнули. Через три недели обмен упал снова, а в истории элемента не оказалось ни одной строки об этом.

Считаем арифметику, из-за которой всё и поехало. Интервал обновления элемента стоял 1m — «чтобы не грузить сервер». MaxLinesPerSecond в конфиге агента не трогали, то есть работал дефолт 20. По документации агент для поиска нужных строк анализирует вдесятеро больше, чем разрешено отправить, — при дефолтах это около 200 записей за одну проверку. Поток приложения — 450 тыс. строк в сутки, это ~312 строк в минуту в среднем, а в утренний пик синхронизации складских остатков и вдвое больше. Итог: агент разбирает 200 строк в минуту при поступлении 312. Отставание росло в среднем на 110 строк каждую минуту и никогда бы не рассосалось само. Сорок минут задержки — это просто накопленный с утреннего пика хвост.

Лечение заняло полчаса и maxdelay в нём не участвовал. Интервал элемента поставил 1s — да, именно так, это не опечатка и не нагрузка: агент начинает читать мелкими порциями и перестаёт устраивать всплески раз в минуту. MaxLinesPerSecond поднял до 100. Элемент разбил на два: узкий по ERROR|FATAL с отправкой строк и счётный logrt.count по WARN, чтобы видеть динамику без перекачки текста. Плюс persistent_dir: позицию в файле сервер и так хранит и отдаёт агенту при старте, а постоянные файлы закрывают случай, когда агент перезапустился, не успев отправить буфер, — без них в истории появляются повторы или дыра.

# /etc/zabbix/zabbix_agentd.conf
ServerActive=10.20.0.11
MaxLinesPerSecond=100
BufferSize=1000     # у zabbix_agentd по умолчанию 100, у agent 2 уже 1000
BufferSend=1
Timeout=10
logrt["/var/log/fitograd/app-[0-9]{8}\.log","(ERROR|FATAL)",,1000,skip,,0,,/var/lib/zabbix/logpos]
logrt.count["/var/log/fitograd/app-[0-9]{8}\.log","WARN",,10000,skip,0,,/var/lib/zabbix/logpos]

Результат: при интервале 1s потолок разбора по основному элементу — до 10 000 строк за секундную проверку (maxlines=1000, умноженное на десять), против поступающих в пик 10–12 строк в секунду; запас многократный, отставание после разгона хвоста стабильно держится в пределах 3–5 секунд. maxdelay вернули в 0. Нагрузка на сервер Zabbix, вопреки опасениям, не выросла: вместо минутных пачек пошёл ровный тонкий поток. Одну вещь я не отдал: заказчик хотел собирать в Zabbix ещё и INFO «на всякий случай». Отговорил. Zabbix — не syslog-сервер и не хранилище логов, для этого рядом ставится нормальный сборщик, а в мониторинг тянутся только события, на которые есть реакция.

Отставание в мониторинге логов почти никогда не про «сервер слабый». В девяти случаях из десяти это интервал 1m и дефолтный MaxLinesPerSecond=20, упирающиеся в поток приложения.
Цифры и версии: Разбор: «ФитоГрад», озеленение офисов, 34 рабочих места — схема
Цифры и версии: Разбор: «ФитоГрад», озеленение офисов, 34 рабочих места. Открыть схему в полном размере

Настоящие узкие места: где агент действительно тормозит

Первое и главное — потолок разбора. MaxLinesPerSecond в конфиге агента по умолчанию 20, и это ограничение на отправку. Разбирает агент вдесятеро больше — в документации это сформулировано прямо: при дефолтах не более 200 проанализированных и не более 20 отправленных записей за проверку. Для log.count[] и logrt.count[] свой параметр maxproclines: по умолчанию 10×MaxLinesPerSecond, максимум 10 000. Параметр maxlines в самом ключе переопределяет MaxLinesPerSecond для конкретного элемента — это удобнее, чем крутить глобальный конфиг, потому что поднимать потолок обычно нужно для одного шумного журнала, а не для всех.

Второе — интервал. Здесь у людей стойкая интуиция наоборот: «поставлю пореже, чтобы не грузить». На мониторинге логов это работает ровно наоборот, и рекомендация ставить активным log-элементам интервал 1 секунда звучит и в материалах самих разработчиков Zabbix. При редком интервале агент вынужден за одну проверку вычитывать всё, что накопилось, и упирается в потолок строк; при секундном — идёт равномерно и потолка не касается вовсе.

Третье — буферы, и тут есть неочевидная разница между классическим агентом и агентом 2. У zabbix_agentd BufferSize по умолчанию 100 значений, у агента 2 — 1000; BufferSend у обоих 5 секунд. В агенте 2 и лимит строк называется иначе — Plugins.Log.MaxLinesPerSecond. Классический zabbix_agentd умеет выгружать данные прямо во время сбора и освобождать буфер по ходу дела. Агент 2, как сказано в документации, останавливает сбор журнала, пока данные не будут выгружены и буфер не освободится, а выгрузка идёт асинхронно. На спокойных логах разницы не заметите, на шумных — агент 2 начинает пилообразно отставать, и это лечится увеличением BufferSize и уменьшением BufferSend, а не maxdelay. Ещё одна деталь: persistent_dir поддерживается только классическим zabbix_agentd на Unix-системах, агентом 2 — нет.

Четвёртое — регулярки и ротация. Жадные выражения вида .*(ERROR|FATAL).* на длинных строках стоят лишнего CPU: убирайте крайние .*, они ничего не добавляют, потому что поиск и так идёт по вхождению. Строки длиннее 256 КБ агент при сопоставлении с регуляркой всё равно обрезает до первых 256 КБ. Про ротацию: на Unix агент опознаёт файл по inode и MD5 первых 512 байт, в Windows на NTFS — по 64-битному индексу файла, на ReFS — по 128-битному ID. Документация отдельно просит не менять время модификации журнала и не подменять его копированием — иначе Zabbix посчитает файл новым и начнёт с начала. Классическая картина: раз в сутки в 03:00 в истории всплеск старых событий. Для logrt[] и logrt.count[] схему ротации можно указать в options: rotate (по умолчанию — файл переименовывают, пишут новый) или copytruncate (logrotate копирует файл и усекает оригинал). Ограничение из документации: copytruncate нельзя сочетать с maxdelay > 0 и с persistent_dir. Встречаются ещё mtime-reread и mtime-noreread — они управляли реакцией на смену времени модификации, но mtime-noreread в документации помечен устаревшим с 5.0.2, и в новых элементах я на эти значения не опираюсь. Мой выбор по умолчанию — ротация через create/rename, а copytruncate только там, где приложение не умеет переоткрывать файл.

Прежде чем разрешать пропуски, пройдитесь по этому списку. Я ни разу не встречал случая, когда после интервала 1s, поднятого maxlines и правильной ротации отставание оставалось значимым.

Что я делаю вместо maxdelay

Мой порядок действий, ровно в этой последовательности — сверху то, что даёт эффект сразу и почти бесплатно, снизу то, до чего доходят единицы. Первое: фильтровать на источнике. Если приложение сыплет отладкой в прод, вопрос не к Zabbix. Снизьте уровень логирования, разведите потоки по разным файлам, вынесите access-лог отдельно от error-лога. Одно это решение обычно снимает 80 % объёма и вместе с ним всю проблему.

Второе: сузить регулярку в самом ключе, чтобы агент отбрасывал строки как можно раньше. Третье: разделить один жирный элемент на несколько узких — по критике отдельный log[] со строками, по массовым событиям logrt.count[], который отдаёт только число и не таскает текст через прокси. Четвёртое: интервал 1s и maxlines под реальный поток. Пятое: persistent_dir на Unix-агентах, чтобы рестарт или обновление агента с неотправленным буфером не давали повторов или дыры в истории. Шестое: mode=skip на новых элементах, чтобы при первом запуске не втянуть в историю весь архив.

На что можно забить. Не гонитесь за отдельным прокси ради логов — на потоке, характерном для офиса до 50 рабочих мест, он не нужен. Не переписывайте всё на агент 2 только ради логов: там, где важна равномерность на шумных журналах, классический агент ведёт себя предсказуемее, и persistent_dir у него есть. И не пытайтесь сделать из Zabbix хранилище логов с полнотекстовым поиском — это разные инструменты, а попытка совместить заканчивается раздутым history_log и жалобами на тормоза фронтенда.

Правильный порядок — сначала уменьшить поток, потом увеличить пропускную способность агента, и только если и это не помогло, обсуждать осознанные потери.
Памятка: Что я делаю вместо maxdelay — схема
Памятка: Что я делаю вместо maxdelay. Открыть схему в полном размере

Если maxdelay всё-таки нужен — ставьте его вместе со сторожем

Честно: сценарии, где maxdelay оправдан, существуют. Самый реальный — приложение, которое иногда уходит в цикл и за минуту наливает гигабайт однотипных строк. В такой ситуации важнее знать «что происходит прямо сейчас», чем «что было полтора часа назад», и полнота вам всё равно недоступна: сотни тысяч одинаковых записей никто читать не будет. Второй сценарий — временный отладочный журнал, который вы подключили на неделю и точно снимете. Всё остальное, что я видел, — это попытка параметром прикрыть неправильно настроенный интервал.

Если ставите — ставьте не меньше двух-трёх интервалов проверки, иначе агент будет прыгать почти на каждом цикле, и заведите отдельный элемент, который следит за самим фактом прыжка. Логика простая: агент честно пишет о пропуске в свой лог, значит, этот лог тоже надо мониторить. Это тридцать секунд работы и полностью снимает эффект «мы думали, всё видим».

logrt.count["/var/log/zabbix/zabbix_agentd\.log.*","to meet maxdelay",,1000,skip,0]

Маска zabbix_agentd\.log.* нужна, чтобы не терять сообщения при встроенной ротации лога агента (LogFileSize переименовывает файл в .old). И триггер на любое ненулевое значение, с текстом, который через полгода поймёт дежурный, а не только автор: «Агент на {HOST.NAME} пропустил часть журнала из-за maxdelay — данные за период неполные».

Дальше по вкусу: вывести этот счётчик на дашборд рядом с самим элементом логов и раз в месяц смотреть, растёт он или нет. Если растёт — значит, поток приложения увеличился и пора возвращаться к разделу выше. Если стабильно ноль — maxdelay можно смело убирать в 0, он вам больше не нужен.

Правило, которое я ставлю в регламент: элемент с maxdelay > 0 не имеет права существовать без парного элемента, считающего пропуски. Иначе через полгода никто уже не вспомнит, что мониторинг здесь неполный.

Короткий чек-лист

Если вы дочитали до сюда с открытым конфигом, вот сжатая последовательность. Сначала грепните лог агента на «to meet maxdelay» — это ответ на вопрос, теряете вы строки или нет, и он занимает десять секунд. Потом посмотрите статус элемента данных: неподдерживаемый элемент напишет причину сам. Затем посчитайте свой поток: строк в сутки делим на 1440, получаем строки в минуту и сравниваем с 200 (дефолтный потолок разбора за проверку при интервале 1m). Если ваш поток больше — вы нашли причину, и maxdelay тут не нужен.

После этого меняйте интервал на 1s, поднимайте maxlines в ключе, разделяйте элементы на критику и счётчики, добавляйте persistent_dir. И возвращайте maxdelay в 0 — с чистой совестью, потому что отставание вы к этому моменту уже убрали по-настоящему, а не спрятали. Если после всего этого агент всё равно не успевает — проблема не в мониторинге, а в приложении, которое пишет столько, что его журнал не читается в реальном времени. Это отдельный разговор с разработчиком, и он полезнее любых параметров.

Свежесть и полнота в мониторинге логов — два разных требования. maxdelay покупает первое за счёт второго; всё остальное из этой статьи даёт оба сразу.
Порядок действий: Короткий чек-лист — схема
Порядок действий: Короткий чек-лист. Открыть схему в полном размере

Частые вопросы

Агент дочитает пропущенные строки позже, когда нагрузка спадёт?

Нет. При срабатывании maxdelay агент не читает пропущенные байты вообще — он только переставляет позицию в файле. Эти строки не попадают в буфер и не образуют очередь, поэтому дочитывать нечего ни через минуту, ни после перезапуска агента. Единственный способ их увидеть — открыть сам файл на диске.

Как понять, что строки пропали именно из-за maxdelay, а не из-за регулярки?

Грепните лог агента на подстроку «to meet maxdelay»: при прыжке агент пишет сообщение с именем элемента, именем файла и числом пропущенных байт (skipping N bytes from byte X to byte Y). Если таких записей нет, причина другая — смотрите статус элемента данных, права на файл, кодировку и схему ротации.

Какое значение maxdelay безопасно?

Безопасное значение только одно — 0, оно же по умолчанию. Любое положительное значение по определению разрешает потери. Если вы всё-таки его ставите, берите не меньше двух-трёх интервалов проверки и обязательно заводите парный элемент, который считает сообщения о прыжках в логе агента.

Почему интервал 1 секунда не грузит сервер сильнее, чем 1 минута?

Потому что объём данных задаёт приложение, а не интервал опроса. При интервале 1 минута агент раз в минуту вычитывает накопившуюся пачку и упирается в потолок разбора (около 200 строк при MaxLinesPerSecond=20), а при 1 секунде читает тонким ровным потоком без всплесков. На практике нагрузка на прокси при переходе на 1s обычно снижается.

Чем отличается maxlines от maxproclines?

maxlines в ключах log[] и logrt[] ограничивает число строк, отправляемых на сервер за секунду, и переопределяет MaxLinesPerSecond для конкретного элемента. У счётных ключей log.count[] и logrt.count[] четвёртый параметр называется maxproclines и ограничивает число обрабатываемых строк — именно поэтому при дефолтах счётчик может занижать реальное количество событий.

Работает ли всё это одинаково в Zabbix agent и agent 2?

Не совсем. Классический агент умеет выгружать данные во время сбора, а агент 2, по документации, останавливает сбор журнала, пока данные не выгружены и буфер не освобождён. У агента 2 другие дефолты и имена: BufferSize по умолчанию 1000 вместо 100, лимит строк задаётся как Plugins.Log.MaxLinesPerSecond. persistent_dir поддерживается только классическим агентом на Unix. На шумных журналах я предпочитаю классический zabbix_agentd.

Можно ли включить copytruncate и maxdelay в одном элементе?

Нет. В документации Zabbix прямо сказано, что options=copytruncate нельзя использовать вместе с maxdelay > 0 и с persistent_dir. Если logrotate настроен на copytruncate, либо переведите ротацию на create/rename, либо откажитесь от maxdelay и постоянных файлов в этом элементе.

Столкнулись с похожей задачей? Обращайтесь — решим

Если у вас происходит что-то из описанного в этой статье — или любая другая проблема с ИТ-инфраструктурой, — обращайтесь в любое время. Мои специалисты и я лично разберём ситуацию, найдём настоящую причину и доведём до решения.

Возьмёмся и за разовую задачу, и за постоянное обслуживание. Первичная консультация — бесплатно и без обязательств.

📞 +7 903 729-62-41 💬 MAX: +7 903 729-62-41 ✈ Telegram @ITfresh_Boss

С уважением, Семёнов Евгений Сергеевич, директор «АйТи Фреш» — IT-аутсорсинг для компаний до 50 рабочих мест, 15+ лет практики

Источники

© ООО «АйТи-Фреш» · Москва · Все статьи