К основному содержанию

Кейс

Логи переживают недоступность ELK: disk-assisted очередь rsyslog

Центральный rsyslog пересылал логи сетевого парка в ELK по UDP и не замечал недоступность приёмника. Переходим на TCP с disk-assisted очередью: во время сбоя строки копятся в пределах выделенного места, а после восстановления досылаются в порядке поступления.

5 ГБлимит disk-assisted буфера на время недоступности ELK
Пересылка логов с дисковым буфером

Схема читается сверху вниз: сначала обычный ход, затем то, ради чего очередь и заводится.

01Обычный ход

УстройстваРоутеры, файрволы, точки доступа шлют syslog по UDP и TCP 514
rsyslogЦентральный коллектор: приём от парка и пересылка дальше
ELKLogstash разбирает строки и кладёт в индекс логов

Строки идут по TCP, очередь на коллекторе почти пуста

02Приёмник недоступен

ELK не отвечаетОбновление, перезагрузка или обрыв связи между площадками
Дисковая очередьСтроки копятся на диске коллектора в пределах выделенного места
Досылка по порядкуСвязь вернулась — накопленное уходит в порядке поступления

Очередь пережидает и штатную остановку коллектора: то, что было в памяти, сохраняется на диск

Весь сетевой парк — роутеры, файрволы, точки доступа — шлёт syslog на один центральный rsyslog-коллектор. Он складывает логи локально по хостам и заодно пересылает всё в ELK, где их удобно искать и строить дашборды. Пересылка была настроена одной привычной строкой:

*.* @elk-host:PORT

Один символ @ — это UDP. И именно он тихо ломал всю затею с наблюдаемостью: стоило ELK или сети на пути моргнуть, как логи за это время просто исчезали. Не «задерживались», а терялись без следа. Задача звучала так: сделать буфер, чтобы при недоступности ELK строки копились, а после восстановления досылались.

Почему UDP не сообщает, что ELK недоступен

Первый инстинкт — «добавить очередь к текущей пересылке». Но с UDP это не сработает, и причина фундаментальная. UDP — протокол без соединения: sendto() отдаёт пакет в сеть и почти всегда возвращает успех, даже если на том конце никто не слушает. Надёжного подтверждения доставки отправитель не получает.

А очередь rsyslog наполняется только тогда, когда действие переходит в состояние suspended — то есть когда доставка осознанно не удалась. Обычная UDP-пересылка не получает от приёмника такого сигнала: с точки зрения rsyslog строка отправлена. Значит resumeRetryCount и action queue не защищают именно от падения ELK — для отправителя по-прежнему «всё хорошо».

Вывод, который определил решение: буфер требует TCP. Только на TCP обрыв соединения — это явная ошибка доставки, после которой очередь начинает работать.

Параметр, который включает диск

На TCP всё встаёт на места. Заменяем legacy-синтаксис на полноценное действие с очередью:

*.* action(
    type="omfwd"
    target="elk-host"
    port="PORT"
    protocol="tcp"

    queue.type="LinkedList"
    queue.filename="fwd_to_elk"      # включает disk-assisted режим
    queue.spoolDirectory="/var/spool/rsyslog"
    queue.maxDiskSpace="5g"          # предел места для файлов очереди
    queue.saveOnShutdown="on"        # сохранить память при штатной остановке
    queue.size="100000"              # размер первичной очереди, в сообщениях
    queue.highWatermark="90000"      # с этого порога включается запись на диск
    queue.lowWatermark="70000"       # здесь очередь вернётся в memory-only

    action.resumeRetryCount="-1"     # не прекращать попытки соединения
    action.resumeInterval="10"       # пауза между попытками, сек
)

Ключевой здесь — queue.filename. Это не «имя лог-файла», а переключатель: очередь LinkedList становится disk-assisted. В нормальном режиме она работает в памяти, а при достижении queue.highWatermark начинает выгружать сообщения на диск. При снижении до queue.lowWatermark снова остаётся только в памяти.

За штатный рестарт отвечает отдельный параметр queue.saveOnShutdown="on": перед остановкой rsyslog сохраняет на диск и ту часть очереди, которая ещё оставалась в памяти. Это не защита от SIGKILL, OOM, сбоя ОС или отключения питания. Если нужно, чтобы каждое принятое сообщение переживало такие события, требуется pure disk queue с отдельно настроенными checkpoint и синхронизацией файлов.

queue.maxDiskSpace — обязательный предохранитель от заполнения всего раздела, но это не команда «удалять самые старые строки». Когда выделенное место заканчивается, результат зависит от источников, queue.timeoutEnqueue и явно заданных queue.discardMark/queue.discardSeverity: часть источников получит backpressure, а новые сообщения после тайм-аута могут быть отброшены. Поэтому вместе с лимитом нужны наблюдение за самой очередью и заранее выбранная политика перегрузки.

Как это ведёт себя вживую

Вся логика сводится к трём состояниям, и переключение между ними — автоматическое:

Такая схема закрывает обычную недоступность приёмника, пока хватает буфера. Но plain TCP syslog не даёт end-to-end exactly-once: при разрыве соединения остаётся окно неопределённости для последнего сообщения. Если приёмник должен подтверждать каждое событие, вместо обычного TCP нужен RELP.

Грабли, которые стоили времени

Проверка, а не вера

После рестарта — три подтверждения. Первое: на коллекторе видно установленное TCP-соединение к приёмнику (ss -tn), и оно держится, а не рвётся каждую секунду. Второе, решающее — маркер:

logger -t elk-buffer-test "проверка $(date)"

Если это событие находится в ELK разобранным по полям — сквозная цепочка подтверждена: TCP, парсинг, индексация. Если TCP-сессия живёт, а события в индексе нет — значит на порту не тот вход, и это разговор уже с командой ELK, а не с конфигом rsyslog.

Третье подтверждение — состояние очереди через impstats: следим за size, full, discarded.full и discarded.nf. Без этих счётчиков лимит 5 ГБ лишь ограничивает размер файлов, но не предупреждает, что запас уже заканчивается.


Урок здесь не про rsyslog как таковой, а про разницу между «отправили» и «доставили». UDP-пересылка красиво работает на демонстрации и молча теряет данные ровно тогда, когда они нужнее всего — в момент инцидента, когда что-то на пути и падает. Практичная защита от таких провалов начинается с TCP, отдельной очереди, лимита и мониторинга её заполнения. Пока хватает выделенного места, логи могут подождать на диске и уехать после восстановления ELK. Наблюдаемость, которая учитывает собственные сбои, — это не только дашборд, но и эта неприметная очередь под ним.

Все кейсы