Netfilter, conntrack -E, ENOBUFS и использование оперативной памяти

Вопрос или проблема

Я пытаюсь записывать события conntrack для NAT-шлюза. У меня есть скрипт на Perl, который анализирует вывод, и он работает при средней нагрузке. Однако при высокой нагрузке я получаю ENOBUFS:

Feb 27 09:35:43 localhost conntrack-parse[4107269]: WARNING: Мы достигли ENOBUFS! Мы теряем события.
Feb 27 09:35:43 localhost conntrack-parse[4107269]: Это сообщение означает, что текущий размер буфера сокета netlink слишком мал.
Feb 27 09:35:43 localhost conntrack-parse[4107269]: Пожалуйста, проверьте --buffer-size в man-странице conntrack(8).
Feb 27 09:35:43 localhost conntrack-parse[4107269]: WARNING: Мы достигли ENOBUFS! Мы теряем события.
Feb 27 09:35:43 localhost conntrack-parse[4107269]: Это сообщение означает, что текущий размер буфера сокета netlink слишком мал.
Feb 27 09:35:43 localhost conntrack-parse[4107269]: Пожалуйста, проверьте --buffer-size в man-странице conntrack(8).
Feb 27 09:35:49 localhost conntrack-parse[4107269]: WARNING: Мы достигли ENOBUFS! Мы теряем события.
Feb 27 09:35:49 localhost conntrack-parse[4107269]: Это сообщение означает, что текущий размер буфера сокета netlink слишком мал.
Feb 27 09:35:49 localhost conntrack-parse[4107269]: Пожалуйста, проверьте --buffer-size в man-странице conntrack(8).

Сообщение об ошибке кажется достаточно ясным, я могу установить размер буфера. Однако, мой буфер составляет 8G:

● conntrack-parse.service - Парсинг логов conntrack в более удобный для чтения формат
     Loaded: загружено (/lib/systemd/system/conntrack-parse.service; включено; preset: включено)
     Active: активно (работает) с Чт 2025-02-27 00:27:54 GMT; 9 ч. назад
   Main PID: 4107264 (conntrack-parse)
      Tasks: 2 (limit: 154045)
     Memory: 6.6G
        CPU: 1ч 15мин 3.033с
     CGroup: /system.slice/conntrack-parse.service
             ├─4107264 /usr/bin/perl /usr/local/bin/conntrack-parse
             └─4107269 /usr/sbin/conntrack -E -eNEW,DESTROY --src-nat -otimestamp,extended --buffer-size=8589934592

Обычно я просто продолжал бы увеличивать это число, пока оно не начало работать, но использование памяти в системе в настоящее время значительно ниже 8G, поэтому я не думаю, что дальнейшее увеличение будет иметь какой-либо эффект, и мне нужно искать решение в другом месте.

# free -m
               total        used        free      shared  buff/cache   available
Mem:          128412        4771       63058           6       61811      123641
Swap:            979           3         976

Для подтверждения, команда conntrack говорит, что буфер сокета Netlink установлен:

Feb 27 09:38:37 localhost conntrack-parse[16004]: NOTICE: Размер буфера сокета Netlink был установлен на 8589936896 байт.                                                                                                                                                           

Итак, мой главный вопрос: в чем здесь узкое место? Сообщение об ошибке, кажется, дает четкое руководство, но это не работает для моего случая.

Информация о хосте, Dell PowerEdge с сетевыми адаптерами Intel, использующими драйвер i40e:

# uname -a 
Linux 6.1.0-31-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.128-1 (2025-02-07) x86_64 GNU/Linux

# sysctl -a | grep conntrack
net.netfilter.nf_conntrack_acct = 0
net.netfilter.nf_conntrack_buckets = 2097152
net.netfilter.nf_conntrack_checksum = 1
net.netfilter.nf_conntrack_count = 444961
net.netfilter.nf_conntrack_dccp_loose = 1
net.netfilter.nf_conntrack_dccp_timeout_closereq = 64
net.netfilter.nf_conntrack_dccp_timeout_closing = 64
net.netfilter.nf_conntrack_dccp_timeout_open = 43200
net.netfilter.nf_conntrack_dccp_timeout_partopen = 480
net.netfilter.nf_conntrack_dccp_timeout_request = 240
net.netfilter.nf_conntrack_dccp_timeout_respond = 480
net.netfilter.nf_conntrack_dccp_timeout_timewait = 240
net.netfilter.nf_conntrack_events = 2
net.netfilter.nf_conntrack_expect_max = 32768
net.netfilter.nf_conntrack_frag6_high_thresh = 4194304
net.netfilter.nf_conntrack_frag6_low_thresh = 3145728
net.netfilter.nf_conntrack_frag6_timeout = 60
net.netfilter.nf_conntrack_generic_timeout = 600
net.netfilter.nf_conntrack_gre_timeout = 30
net.netfilter.nf_conntrack_gre_timeout_stream = 180
net.netfilter.nf_conntrack_icmp_timeout = 30
net.netfilter.nf_conntrack_icmpv6_timeout = 30
net.netfilter.nf_conntrack_log_invalid = 0
net.netfilter.nf_conntrack_max = 16777216
net.netfilter.nf_conntrack_sctp_timeout_closed = 10
net.netfilter.nf_conntrack_sctp_timeout_cookie_echoed = 3
net.netfilter.nf_conntrack_sctp_timeout_cookie_wait = 3
net.netfilter.nf_conntrack_sctp_timeout_established = 210
net.netfilter.nf_conntrack_sctp_timeout_heartbeat_sent = 30
net.netfilter.nf_conntrack_sctp_timeout_shutdown_ack_sent = 3
net.netfilter.nf_conntrack_sctp_timeout_shutdown_recd = 3
net.netfilter.nf_conntrack_sctp_timeout_shutdown_sent = 3
net.netfilter.nf_conntrack_tcp_be_liberal = 0
net.netfilter.nf_conntrack_tcp_ignore_invalid_rst = 0
net.netfilter.nf_conntrack_tcp_loose = 1
net.netfilter.nf_conntrack_tcp_max_retrans = 3
net.netfilter.nf_conntrack_tcp_timeout_close = 10
net.netfilter.nf_conntrack_tcp_timeout_close_wait = 60
net.netfilter.nf_conntrack_tcp_timeout_established = 1200
net.netfilter.nf_conntrack_tcp_timeout_fin_wait = 120
net.netfilter.nf_conntrack_tcp_timeout_last_ack = 30
net.netfilter.nf_conntrack_tcp_timeout_max_retrans = 300
net.netfilter.nf_conntrack_tcp_timeout_syn_recv = 60
net.netfilter.nf_conntrack_tcp_timeout_syn_sent = 120
net.netfilter.nf_conntrack_tcp_timeout_time_wait = 120
net.netfilter.nf_conntrack_tcp_timeout_unacknowledged = 300
net.netfilter.nf_conntrack_timestamp = 0
net.netfilter.nf_conntrack_udp_timeout = 30
net.netfilter.nf_conntrack_udp_timeout_stream = 120
net.nf_conntrack_max = 16777216

# sysctl -a | grep rmem
net.core.rmem_default = 212992
net.core.rmem_max = 16777216
net.ipv4.tcp_rmem = 4096        131072  6291456
net.ipv4.udp_rmem_min = 4096

Ответ или решение

Теория

Проблема, с которой вы столкнулись, связана с использованием Netfilter и прослушиванием событий conntrack. При анализе сетевого трафика NAT-шлюза возникает ошибка ENOBUFS, указывающая на то, что буфер сокета netlink недостаточно велик для обработки всех входящих данных, что приводит к потере событий. Эта ошибка часто проявляется под высокой нагрузкой сети, когда объем обрабатываемых данных существенно увеличивается.

Netfilter — это фреймворк в ядре Linux, который управляет сетевой фильтрацией и мониторингом пакетов. Одним из его компонентов является conntrack, который отслеживает соединения, включая их состояния и изменения. Команда conntrack -E используется для мониторинга событий связей, таких как создание и разрушение соединений, с возможностью использования опций для детального контроля потока информации, например, источника NAT или меток времени.

Ошибка ENOBUFS указывает на нехватку буферного пространства, что может произойти даже в системах с большим объемом оперативной памяти, если буфер сокета netlink заполнен данными быстрее, чем они обрабатываются. В этом случае увеличение размера буфера может быть эффективным, но лишь до определенного предела.

Пример

Имея уже установленный buffer-size до 8 ГБ, вы столкнулись с тем, что и этого размера оказывается недостаточно. Ваши конфигурации показывают:

  1. Устройство: Dell PowerEdge с сетевыми картами на чипах Intel и драйвером i40e.
  2. Система: Debian со пользовательским ядром Linux 6.1.
  3. Загрузка системы: свободно 123 ГБ из общего объема памяти (128 ГБ).
  4. Текущие настройки: netlink socket buffer установлен до 8 ГБ, и это все равно приводит к ошибкам ENOBUFS.

Ваши системные параметры показывают, что даже с увеличенной памятью в 8 ГБ, недостаточность все равно проявляется. Это может указывать на более глубокую проблему, связанную с производительностью системы при высокой нагрузке.

Применение

  1. Оптимизация обработки событий

    Проверьте свой код на наличие возможных препятствий в обработке событий. Если ваше приложение написано на Perl, убедитесь, что парсинг данных и их обработка максимально оптимизированы. Медленный парсинг и использование блокирующих операций могут стать узким местом при высокой нагрузке.

  2. Используйте многопоточность

    Задействуйте многопоточность или параллелизм в вашем скрипте для эффективной обработки большого количества событий. Возможно, вам стоит переработать сценарий на языках, поддерживающих более высокую степень параллелизма (таких как Python с библиотекой asyncio, Golang или Rust).

  3. Мониторинг сетевой инфраструктуры

    Используйте инструменты мониторинга, чтобы определить, когда пропускная способность соединения достигает предела. Это возможно, что именно сетевые драйвера или сами сетевые карты становятся узким местом (например, i40e), особенно при интенсивной нагрузке.

  4. Коррекция системных параметров

    Убедитесь, что ваши параметры net.core.rmem_max достаточны для обеспечения достаточной пропускной способности данных через socket. Вы можете попробовать увеличить этот параметр, чтобы проверить улучшится ли ситуация. Но помните, что увеличение этого параметра может привести к ограничению остальных процессов в системе.

  5. Разделение нагрузки

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

  6. Логирование и диагностика

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

Заключение:

Ваша ситуация требует углубленного подхода к пониманию того, где находятся ограничения в вашей текущей архитектуре и наладке системы. Очевидно, что один только наращивание буфера не всегда будет адекватным решением и требуется полный аудит системы и возможные изменения в программном обеспечении и сетевой инфраструктуре.

Оцените материал
Добавить комментарий

Капча загружается...