sysctl для ноды: BBR, буферы и очереди
Один файл, который превращает дефолтное ядро в ноду, способную переварить трафик. С объяснением, что каждая строка делает.
Дефолтные настройки ядра рассчитаны на ноутбук, а не на ноду, через которую ходят сотни клиентов. Хорошая новость: чинится одним файлом и одной командой. Плохая: половина советов в интернете копируется с 2014 года и либо бесполезна, либо вредна.
Проверь, что BBR вообще есть
BBR живёт в ядре начиная с 4.9. В любом современном дистрибутиве он есть, но модуль может быть не загружен:
sysctl net.ipv4.tcp_available_congestion_controlЕсли в списке нет bbr:
modprobe tcp_bbr && echo tcp_bbr > /etc/modules-load.d/bbr.confФайл целиком
Кладём в отдельный файл, а не в /etc/sysctl.conf, чтобы обновления пакетов его не трогали:
# Очередь fq + BBR: меньше задержка, выше скорость на плохих каналах
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
# Буферы сокетов до 64 МБ: нужно для гигабита на дальних маршрутах
net.core.rmem_max = 67108864
net.core.wmem_max = 67108864
# min / default / max: первые два числа — дефолты ядра, поднимаем только max
net.ipv4.tcp_rmem = 4096 131072 67108864
net.ipv4.tcp_wmem = 4096 16384 67108864
# Очереди соединений: чтобы всплеск клиентов не упирался в дефолтный somaxconn = 4096
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.core.netdev_max_backlog = 250000
# Больше исходящих портов (дефолт 32768–60999) и переиспользование TIME-WAIT
# для исходящих соединений (дефолт tcp_tw_reuse = 2 — только для loopback)
net.ipv4.ip_local_port_range = 10000 65535
net.ipv4.tcp_tw_reuse = 1
# сколько осиротевшее соединение висит в FIN_WAIT_2 (дефолт 60 с)
net.ipv4.tcp_fin_timeout = 15
# Не сбрасывать окно после простоя (дефолт 1 — сбрасывать)
net.ipv4.tcp_slow_start_after_idle = 0
# Искать рабочий MTU сам, когда обнаружена ICMP-чёрная дыра (дефолт 0 — выключено)
net.ipv4.tcp_mtu_probing = 1
# TCP Fast Open для клиента и сервера (дефолт 1 — только клиент).
# Серверная часть работает, только если приложение включает TFO на своём сокете
net.ipv4.tcp_fastopen = 3
# Файловые дескрипторы: каждое соединение — это файл.
# file-max — на всю систему, nr_open — потолок на процесс (дефолт 1048576)
fs.file-max = 2097152
fs.nr_open = 2097152Применяем без перезагрузки:
sysctl --systemИ проверяем, что BBR взлетел:
sysctl net.ipv4.tcp_congestion_control net.core.default_qdiscЛимиты процессов
fs.file-max — это потолок на всю систему. У процесса свой лимит RLIMIT_NOFILE, и под systemd он по умолчанию 1024:524288: мягкий 1024, жёсткий 524288. Программа, которая сама не поднимает мягкий лимит, упрётся в too many open files примерно на тысяче соединений.
Xray и другие программы на Go (с Go 1.19) сами поднимают мягкий лимит до жёсткого, а юнит из официального установщика XTLS/Xray-install уже содержит LimitNOFILE=1000000. Свой лимит нужен, если сервис запускается своим юнитом или это не Go-программа. Для сервисов под systemd:
[Service]
LimitNOFILE=1048576systemctl daemon-reload && systemctl restart xrayДля Docker лимит задаётся в compose через ulimits: одним числом (nofile: 1048576) или парой soft/hard. LimitNOFILE не может быть больше fs.nr_open.
Conntrack
Если на ноде есть NAT, Docker или iptables с ct state, ядро помнит каждое соединение в таблице conntrack. Переполнится — новые соединения начнут молча отваливаться, а в dmesg появится nf_conntrack: table full, dropping packet.
# дефолт равен nf_conntrack_buckets и зависит от объёма памяти
net.netfilter.nf_conntrack_max = 1048576
# дефолт 432000 (5 суток): мёртвые established-записи долго занимают таблицу
net.netfilter.nf_conntrack_tcp_timeout_established = 7200Заметка
Параметры nf_conntrack появляются, только когда модуль загружен. Если sysctl --system ругается на них при загрузке, добавь nf_conntrack в /etc/modules-load.d/.
Чего не делать
Не делай так
net.ipv4.tcp_tw_recycle = 1. Этот параметр ломал клиентов за NAT и был удалён из ядра в 4.12. На современном ядре такого ключа просто нет, и sysctl ругается на него. Если он есть в твоём «оптимизационном скрипте», скрипт писали очень давно.
- Не раздувай
rmem_default/wmem_defaultиtcp_mem.rmem_defaultиwmem_default— стартовый буфер каждого сокета, и тысяча клиентов умножит его на тысячу.tcp_mem— общий лимит памяти TCP в страницах (не в байтах): ядро считает его при загрузке от объёма RAM, и если его задрать, TCP перестанет себя ограничивать, пока память не кончится. - Не отключай IPv6 через sysctl «на всякий случай». Часть софта ожидает
::1и потом ломается загадочно. Если IPv6 не нужен, просто не слушай на нём. - Не копируй наборы целиком, не читая. Сравнить с тем, что используют в сообществе, можно в базе: sysctl, BBR, MTU.
Проверка до и после
Разницу видно на дальних маршрутах. Прогони iperf3 до сервера в другой стране до и после применения:
iperf3 -c <сервер> -t 20 -P 4Если скорость не изменилась, узкое место не в ядре: смотри на канал хостера и CPU steal. Как это проверить — в чек-листе нового VPS.
Источники
- ip-sysctl.rst:
tcp_congestion_control,tcp_available_congestion_control,tcp_rmem,tcp_wmem,tcp_mem,somaxconn,tcp_max_syn_backlog,ip_local_port_range,tcp_tw_reuse,tcp_fin_timeout,tcp_slow_start_after_idle,tcp_mtu_probing,tcp_fastopen - sysctl/net.rst:
default_qdisc,rmem_max,wmem_max,rmem_default,wmem_default,netdev_max_backlog - sysctl/fs.rst:
file-max,nr_open - nf_conntrack-sysctl.rst:
nf_conntrack_max,nf_conntrack_buckets,nf_conntrack_tcp_timeout_established - Linux 4.9 (kernelnewbies): BBR; Linux 4.12 (kernelnewbies) и коммит «tcp: remove tcp_tw_recycle»
- systemd.exec(5):
LimitNOFILE=; systemd-system.conf(5):DefaultLimitNOFILE=1024:524288 - Go 1.19 release notes: автоматический подъём
RLIMIT_NOFILE; XTLS/Xray-install:LimitNOFILE=1000000в юните - Compose file reference: ulimits
- iperf3(1):
-c,-t,-P