База знаний

Настройки ядра для большого числа открытых соединений

Значения sysctl и лимиты systemd, которые имеют значение, когда машина держит десятки тысяч сокетов, и те, что скопированы из старых блогов и ничего не делают.

Сначала измеряйте

ss -s
cat /proc/sys/fs/file-nr
nstat -az TcpExtListenOverflows TcpExtListenDrops

ListenOverflows растёт — значит, очередь приёма заполнена, и ядро отбрасывает завершённые рукопожатия. Это конкретная проблема с конкретным решением. Большинство других симптомов вообще не лечатся sysctl, а настройка машины, которая не находится под нагрузкой, лишь переносит eventual failure в более труднодоступное место.

Настройки, которые оправдывают себя

# /etc/sysctl.d/90-connections.conf
fs.file-max = 2097152
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 32768
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.ip_local_port_range = 10240 65535
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_slow_start_after_idle = 0
net.ipv4.tcp_mtu_probing = 1
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
sysctl --system
sysctl net.ipv4.tcp_congestion_control

Что делает каждая из них:

  • somaxconn ограничивает очередь прослушивания, и ваше приложение всё равно должно явно запросить её. В nginx это listen ... backlog=65535; в большинстве языков — второй аргумент listen().
  • ip_local_port_range важен на стороне клиента. Прокси, открывающий исходящие соединения к одному адресату, исчерпывает эфемерные порты примерно на двадцати восьми тысячах при диапазоне по умолчанию.
  • tcp_tw_reuse позволяет ядру повторно использовать сокеты TIMEWAIT для новых исходящих соединений. Безопасно. Его родственник `tcptw_recycle` был удалён из Linux в 4.12, и любой гайд, который всё ещё рекомендует его, был написан до 2017 года.
  • fq вместе с bbr — реальное улучшение на длинных или потерянных путях и бесполезно на коротких чистых. Оба есть в штатных ядрах.

Отслеживание соединений

Stateful файрвол даёт каждому соединению запись conntrack, и у таблицы есть предел. При его достижении пакеты отбрасываются, а в журнале ядра пишется nf_conntrack: table full.

sysctl net.netfilter.nf_conntrack_max net.netfilter.nf_conntrack_count

Поднимите его, если есть память — каждая запись стоит пару сотен байт:

net.netfilter.nf_conntrack_max = 1048576

Где нагрузка — stateless публичный сервис, лучший ответ — не отслеживать его:

table inet raw {
  chain prerouting {
    type filter hook prerouting priority raw;
    tcp dport 443 notrack
  }
}

Лимиты, которые игнорирует systemd

/etc/security/limits.conf применяется к входам через PAM. Он никак не влияет на сервис, запущенный systemd, поэтому повышение nofile там и перезапуск nginx не меняет ровным счётом ничего. Установите его в юните:

mkdir -p /etc/systemd/system/nginx.service.d
cat > /etc/systemd/system/nginx.service.d/limits.conf <<EOF
[Service]
LimitNOFILE=1048576
EOF
systemctl daemon-reload
systemctl restart nginx

Проверяйте по работающему сервису, а не по только что записанному файлу:

systemctl show nginx -p LimitNOFILE

Три вещи, которые делать не стоит

  • Не вставляйте файл sysctl из пятидесяти строк с форума. Половина из них рассчитана на ядро, которое не выходило десять лет, и одна строка тихо сломает обнаружение path MTU.
  • Не повышайте tcp_rmem и tcp_wmem без длинного толстого пути, который это оправдывает. На пути в одну миллисекунду буферы большего размера дают только задержку и давление на память.
  • Не отключайте tcp_timestamps. Вы потеряете оценку RTT и PAWS и получите лишь слух о производительности.

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

Готовы, когда вы готовы

Выберите город. Выберите размер. Оплатите монетой.

Никаких форм о том, кто вы, никакого ожидания одобрения человеком, никаких звонков для проверки. Счёт оплачен — и учётные данные приходят на почту.