Linux-сервер

Тюнинг sysctl и ulimit под высокую нагрузку: TCP в Linux и «nf_conntrack: table full»

6 шагов 17 мин чтения 26 просмотров 0
Содержание

Когда нагрузка на сайт или API растёт, первые ошибки часто не связаны ни с CPU, ни с RAM: Too many open files, nf_conntrack: table full, dropping packet, соединения, зависающие в состоянии SYN, или переполненная очередь listen. Это лимиты ядра и процессов, значения которых по умолчанию рассчитаны на небольшую машину, а не на сервер с десятками тысяч соединений. В статье: как определить, в какой лимит вы упираетесь, как поднять лимит файловых дескрипторов (ulimit), настроить TCP-стек через sysctl, задать размер таблицы отслеживания соединений и согласовать с этим конфигурацию Nginx.

Меняйте по одному параметру и замеряйте результат. Эти настройки помогают, только если вы действительно упираетесь в лимит; медленное приложение быстрее они не сделают. На KVM VPS и выделенных серверах можно менять все эти параметры, а внутри контейнеров (LXC/OpenVZ) многие параметры ядра доступны только для чтения.

Шаг 1: Поиск лимита, в который упирается сервер

# Kernel messages about dropped packets or full tables
dmesg -T | grep -iE 'conntrack|too many|possible SYN flooding' | tail

# Socket summary and listen queues (Recv-Q close to Send-Q = backlog is full)
ss -s
ss -lnt

# Listen queue overflows and dropped SYNs since boot
nstat -az | grep -iE 'ListenOverflows|ListenDrops|TCPReqQFull'

# Connection tracking usage vs maximum
cat /proc/sys/net/netfilter/nf_conntrack_count /proc/sys/net/netfilter/nf_conntrack_max 2>/dev/null

# Open file limit of the shell and of a running service (Nginx worker example)
ulimit -n
grep 'Max open files' /proc/$(pgrep -f 'nginx: worker' | head -1)/limits

Шаг 2: Увеличение лимита файловых дескрипторов

Каждое соединение, файл лога и сокет занимают файловый дескриптор. Распространённое значение по умолчанию — 1024 на процесс — слишком мало для нагруженного прокси или базы данных. Поднимите общесистемный максимум, пользовательские лимиты для сеансов входа и, главное, лимит самих служб systemd, потому что службы игнорируют /etc/security/limits.conf.

# System-wide maximum
cat > /etc/sysctl.d/90-files.conf <<'EOF2'
fs.file-max = 2097152
fs.nr_open = 2097152
EOF2

# Login sessions (takes effect at the next login)
cat > /etc/security/limits.d/90-nofile.conf <<'EOF2'
*    soft nofile 1048576
*    hard nofile 1048576
root soft nofile 1048576
root hard nofile 1048576
EOF2

# A specific service, e.g. Nginx (repeat for mysqld, php-fpm, your app)
mkdir -p /etc/systemd/system/nginx.service.d
cat > /etc/systemd/system/nginx.service.d/limits.conf <<'EOF2'
[Service]
LimitNOFILE=1048576
EOF2
systemctl daemon-reload && systemctl restart nginx

Шаг 3: Настройка TCP-стека через sysctl

Приведённые ниже значения — разумная отправная точка для нагруженного веб-, прокси- или API-сервера. Вынесите их в отдельный файл, чтобы изменения было легко увидеть и откатить.

cat > /etc/sysctl.d/91-network-tuning.conf <<'EOF2'
# Longer accept and SYN queues for bursts of new connections
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.core.netdev_max_backlog = 16384

# More local ports for outgoing connections (proxies, crawlers, API clients)
net.ipv4.ip_local_port_range = 1024 65000
# Reuse TIME_WAIT sockets for new outgoing connections (safe; tcp_tw_recycle no longer exists)
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15

# Keepalive: detect dead peers after about 10 minutes instead of 2 hours
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 5

# Keep SYN cookies on as a basic protection against SYN floods
net.ipv4.tcp_syncookies = 1

# Bigger socket buffers for fast long-distance transfers
net.core.rmem_max = 33554432
net.core.wmem_max = 33554432
net.ipv4.tcp_rmem = 4096 87380 33554432
net.ipv4.tcp_wmem = 4096 65536 33554432

# BBR congestion control (kernel 4.9+)
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
EOF2

sysctl --system
sysctl net.core.somaxconn net.ipv4.tcp_congestion_control

Подробнее о BBR — в статье включение BBR в Linux. Помните, что приложения передают собственный backlog в listen(); например, Nginx нужна директива listen 80 backlog=65535;, чтобы использовать увеличенную очередь.

Шаг 4: Размер таблицы отслеживания соединений (conntrack)

Если вы используете firewalld, ufw, NAT в iptables или Docker, ядро отслеживает каждое соединение в таблице conntrack. Когда она заполняется, новые пакеты отбрасываются, и сайт становится «случайно» недоступным. Максимум по умолчанию зависит от объёма RAM и часто составляет всего 65536 или 262144.

# Load the module so the keys exist, and raise the limits
modprobe nf_conntrack
cat > /etc/sysctl.d/92-conntrack.conf <<'EOF2'
net.netfilter.nf_conntrack_max = 1048576
# Forget idle established connections after 2 hours instead of 5 days
net.netfilter.nf_conntrack_tcp_timeout_established = 7200
net.netfilter.nf_conntrack_tcp_timeout_time_wait = 30
EOF2
sysctl --system

# Hash table size (about max / 4), applied at module load
echo 'options nf_conntrack hashsize=262144' > /etc/modprobe.d/nf_conntrack.conf
echo 262144 > /sys/module/nf_conntrack/parameters/hashsize

# Load the module at boot so the sysctl values are applied
echo nf_conntrack > /etc/modules-load.d/nf_conntrack.conf

Каждая запись conntrack занимает примерно 300 байт памяти ядра, так что миллион записей обойдётся примерно в 300 МБ. На чистом веб-сервере без NAT можно вообще отключить отслеживание для портов 80/443 правилом notrack, но увеличить таблицу проще.

Шаг 5: Согласование конфигурации Nginx

# /etc/nginx/nginx.conf (main context and events block)
worker_processes auto;
worker_rlimit_nofile 1048576;

events {
    worker_connections 65535;
    multi_accept on;
}

http {
    keepalive_timeout 30;
    keepalive_requests 1000;
    # ...
}

# In each server block, let the listen socket use the bigger backlog
#   listen 80 backlog=65535;
#   listen 443 ssl backlog=65535;
nginx -t && systemctl reload nginx
grep 'Max open files' /proc/$(pgrep -f 'nginx: worker' | head -1)/limits

Шаг 6: Проверка под нагрузкой

Повторите команды из шага 1 в часы пиковой нагрузки. Счётчик ListenOverflows должен перестать расти, число записей conntrack — оставаться заметно ниже максимума, а ошибки «too many open files» — исчезнуть из логов приложения. О постоянном мониторинге CPU, памяти, диска и трафика читайте в нашем руководстве по мониторингу сервера.

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

Какой параметр важнее всего?

Для прокси и API это обычно лимит файловых дескрипторов службы и таблица conntrack; для сайтов с резкими всплесками посетителей — очередь listen (somaxconn плюс backlog в Nginx). В какой именно лимит упираетесь вы, покажет шаг 1.

sysctl пишет «cannot stat /proc/sys/net/netfilter/nf_conntrack_max»

Модуль conntrack ещё не загружен. Выполните modprobe nf_conntrack и добавьте его в /etc/modules-load.d/, как показано в шаге 4.

Нужны ли эти настройки, если я использую Cloudflare или другой CDN?

CDN уменьшает число соединений, доходящих до сервера, но origin-серверу всё равно нужно достаточно файловых дескрипторов и backlog для соединений, которые открывает CDN, а также для некэшируемого трафика API и WebSocket.

Если проблема не решена, создайте тикет — техническая поддержка IMIDC работает 24/7. Укажите IP сервера, версию ОС, сообщения об ошибках и вывод команд из шага 1, чтобы мы быстрее нашли причину.

Помог ли вам данный ответ?

Похожие руководства