Зачем трогать ядро
Дефолтные параметры ядра Linux рассчитаны на типовой web-сервер: мало коннектов, небольшие буферы. На VPN-ноде с 1000+ ESTABLISHED-соединений эти дефолты становятся горлом: softirq не успевает, TCP-буферы малы, очереди переполняются, CPU тратится впустую. Тюнинг ниже применялся на бюджетных 2-ядерных VPS и снижал LoadAvg на 58–69% без смены железа и без правки xray-конфигурации.
Корневые проблемы:
- один RX-queue на интерфейс (типично для бюджетных VPS) — весь входящий трафик обрабатывает одно ядро,
ksoftirqdзабит на 100% при свободных остальных; netdev_budget = 300— ядро берёт 300 пакетов за цикл softirq, остальное в очередь, на пиках лаги;- маленькие TCP-буферы (
rmem_maxоколо 200 КБ) — ядро дробит крупные пакеты и тратит CPU; - дефолтный congestion control (cubic) заметно медленнее BBR на каналах с потерями и большим RTT.
Шаг 1. RPS: размазать softirq по ядрам
Receive Packet Steering распределяет обработку входящих пакетов по нескольким CPU. Применимо, когда ядер больше одного, а у сетевухи одна RX-очередь:
# проверить: 0 = RPS не настроен
cat /sys/class/net/<iface>/queues/rx-0/rps_cpus
# маска битовая по числу ядер: 2 ядра → 3, 4 ядра → f
echo 3 > /sys/class/net/<iface>/queues/rx-0/rps_cpus
echo 32768 > /proc/sys/net/core/rps_sock_flow_entries
echo 32768 > /sys/class/net/<iface>/queues/rx-0/rps_flow_cnt
sysctl не умеет писать в /sys/class/net/*, поэтому за персистентность отвечает oneshot systemd-unit (RemainAfterExit=yes), который повторяет эти echo после network.target.
Шаг 2. Sysctl-профиль
/etc/sysctl.d/99-vpn-tuning.conf:
net.core.netdev_budget = 600
net.core.netdev_budget_usecs = 8000
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
net.ipv4.tcp_fastopen = 3
net.netfilter.nf_conntrack_max = 524288
net.core.somaxconn = 8192
net.ipv4.tcp_max_syn_backlog = 8192
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15
Замеры на 2-ядерном VPS: LoadAvg за минуту упал с 3.80 до 1.16 (−69%), rmem_max вырос в 80 раз, нагрузка ksoftirqd распределилась между ядрами.
Кроме буферов и BBR профиль закрывает лимиты, в которые прокси упирается раньше, чем в CPU: conntrack (таблица netfilter переполняется на всплеске — в разобранной утечке рост с 12k до 37k записей, дальше новые соединения отбрасываются), backlog очереди и nofile — лимит файловых дескрипторов процесса задаётся отдельно через LimitNOFILE в unit-файле; референс здорового потолка — порядка миллиона на процесс. Упор в любой из этих лимитов снаружи выглядит как «медленная сеть».
BBR молча слетает в cubic
Самая коварная деталь всего тюнинга. В конфигурации явно прописан tcp_congestion_control = bbr, а рантайм показывает cubic:
sysctl -n net.ipv4.tcp_congestion_control net.core.default_qdisc
Причина — порядок инициализации при загрузке. Если модуль tcp_bbr не добавлен в /etc/modules-load.d/, то при буте systemd-sysctl применяет настройки раньше, чем модуль подгрузится. Запись значения bbr молча проваливается — алгоритм ещё не зарегистрирован в ядре — и сервер остаётся на cubic. На части серверов модуль встаёт вовремя, поэтому дефект выборочный и легко теряется среди «здоровых» соседей.
Фикс:
modprobe tcp_bbr
echo tcp_bbr > /etc/modules-load.d/bbr.conf
sysctl --system
Разница cubic против bbr — кратная просадка пропускной на канале с потерями и большим RTT, то есть у типичного мобильного клиента. Синтетика внутри дата-центра этого может не показать: магистраль чистая. Проверяйте рантайм на каждом сервере парка, а не только на «жалобном».
Подводные камни
- Steal time тюнингом не лечится — это overcommit виртуалки у хостера, соседи воруют CPU. Решается только сменой тарифа или хостера.
- Multi-queue NIC на бюджетных KVM недоступен —
ethtool -L … combined Nупирается вCombined: 1у virtio-net. RPS это и компенсирует. busy_poll/busy_readдают минус 5–10% latency, но плюс CPU — включать только при запасе ядер.- Снимайте BEFORE-снапшот значений на каждой ноде до правки: параметры и интерфейсы у нод разные.
- Держите эталонный sysctl-профиль единым для всего парка и раскатывайте одинаково — выборочные отличия между нодами потом стоят часов диагностики.
Вывод. RPS плюс расширенные TCP-буферы плюс BBR/fq снимают львиную долю паразитной softirq-нагрузки и режут LoadAvg вдвое без апгрейда железа. Но тюнинг ядра не лечит overcommit хостера (steal) и не спасает, если tcp_bbr не закреплён в modules-load.d и слетает после обновления ядра. «Настройка записана в конфигурацию» не равно «настройка применилась» — проверяйте фактический рантайм.