Этот урок дает ту самую карту. Мы пройдем путь одного пакета от приложения до провода и обратно, разберем, где именно растет latency и теряются пакеты, какие команды смотреть в первую очередь и почему на 2026 год часть старой диагностики уже вытесняется eBPF. Это концептуальная основа (часть знаменитой карты Брендана Грегга), на которую дальше лягут конкретные инструменты. Цель простая: чтобы ты, глядя на проблему, сразу понимал, в какой слой тыкать.
Сетевой стек Linux: из чего он собран
Представь сетевой стек linux как многоэтажку, по которой данные едут на лифте сверху вниз (отправка) и снизу вверх (прием). На каждом этаже свой контроль и своя очередь, и пакет может застрять на любом.
Путь на отправку (TX), сверху вниз:
- Приложение - твой код вызывает send()/write() в сокет.
- Сокет и его буфер - данные копируются в буфер отправки (wmem). Если приложение пишет быстрее, чем сеть успевает отдавать, буфер растет.
- TCP/UDP (L4) - TCP режет данные на сегменты, нумерует, ждет подтверждений (ACK), управляет окном. UDP просто заворачивает и отдает дальше, без гарантий.
- IP (L3) - выбирает маршрут, ставит адреса, при необходимости фрагментирует.
- qdisc (сетевая очередь) - дисциплина очередей перед отправкой в драйвер. Здесь живут шейпинг, приоритеты и pacing. На современном Linux с systemd по умолчанию это fq_codel (так с ядра 4.12); на нагруженных TCP-серверах под 10G и выше часто ставят sch_fq, особенно вместе с congestion control BBR.
- Драйвер NIC и ring buffer - кольцевой буфер карты. Драйвер кладет туда дескрипторы, карта забирает данные через DMA.
- Провод - дальше уже физика и провайдер.
Ключевые понятия, без которых дальше никак. Throughput - это сколько байт в секунду протекает (ширина трубы). Latency - сколько ждет один пакет (длина трубы). Это разные вещи: канал на 10 Гбит/с с пингом 200 мс - широкий, но медленный на отклик. RTT (round-trip time) - время "туда и обратно", от отправки до прихода ACK. Окно TCP - сколько данных разрешено держать "в полете" без подтверждения; congestion window (cwnd) ядро само сужает при потерях и осторожно наращивает обратно. Практическое правило: максимальный throughput одного TCP-потока ограничен сверху как (окно в байтах) / RTT. Поэтому на "толстом длинном" канале (большой BDP - bandwidth-delay product) маленькое окно или редкие потери режут скорость в разы, и это не лечится "более широким каналом".

Где растут задержки и теряются пакеты
Главная мысль: задержка и потери - это почти всегда переполненная очередь. Где очередь переполнилась, там и проблема. Идем по слоям сверху вниз.
- ring buffer на приеме переполнился - CPU не успел выгрести пакеты (один поток softirq уперся в 100% ядра). Эти дропы НЕ видны в обычных RX-ошибках ip -s, их показывает только ethtool -S как rx_missed_errors (в ip -s это колонка missed). Лечится увеличением кольца (ethtool -G) или раскидыванием прерываний по ядрам (RSS/RPS, балансировка IRQ).
- буфер сокета мал или приложение медленно читает - данные не влезают, отправитель тормозится. На приеме видно как растущий Recv-Q.
- qdisc/txqueuelen переполнилась на отправке - пакеты дропаются перед уходом в провод (видно как dropped на TX и в tc -s qdisc).
- backlog очередь на listen-сокете переполнилась - новые TCP-соединения отбрасываются (классическое "сервер не принимает коннекты под нагрузкой"). Сюда же SYN backlog: счетчик переполнений видно в nstat -az как TcpExtListenOverflows и TcpExtListenDrops.
- retransmit - TCP не дождался ACK и шлет сегмент заново. Каждый ретрансмит - это потерянный где-то по пути пакет linux плюс лишний RTT задержки. Растущие ретрансмиты - почти верный признак потерь на участке.
Выбор инструмента диктуется слоем. Сокеты и буферы - ss. Сводные счетчики стека - nstat. Счетчики карты - ip -s и ethtool. Сырые пакеты на проводе - tcpdump. А первопричину дропов и ретрансмитов в 2026-м чаще всего быстрее найти через eBPF, не трогая tcpdump. Начнем сверху.
ss - заглянуть внутрь сокетов и TCP.
Код: Выделить всё
sudo ss -tinp state established
Код: Выделить всё
State Recv-Q Send-Q Local Address:Port Peer Address:Port
ESTAB 0 4320 10.0.0.5:443 10.0.0.9:51544
cubic wscale:7,7 rto:312 rtt:48.5/12.1 mss:1448 cwnd:10 ssthresh:7
bytes_sent:982340 bytes_retrans:14480 delivery_rate:6.1Mbps
retrans:0/9 reord:3 rcv_rtt:49.0 send 2.4Mbps pacing_rate 4.8Mbps
ip -s link и nstat - счетчики карты и стека.
Код: Выделить всё
ip -s link show eth0
Код: Выделить всё
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 ...
RX: bytes packets errors dropped missed mcast
8123456 91022 0 37 12 0
TX: bytes packets errors dropped carrier collsns
4501233 60110 0 0 0 0
Код: Выделить всё
sudo ethtool -S eth0 | grep -E 'drop|miss|err|no_buf|fifo'
Код: Выделить всё
nstat -az | grep -E 'Retrans|ListenDrops|ListenOverflows|OutOfWindow'
tcpdump - посмотреть сами пакеты на проводе.
Код: Выделить всё
sudo tcpdump -ni eth0 -tt 'host 10.0.0.9 and tcp port 443'
eBPF - актуальный на 2026 способ ловить причину. Когда ss показал, что ретрансмиты есть, а tcpdump на 10G захлебывается, спускайся в ядро напрямую. Инструменты из bcc и bpftrace получают данные из ядра без захвата и фильтрации трафика, поэтому накладные расходы минимальны:
Код: Выделить всё
sudo tcpretrans # каждый ретрансмит: кто, куда, в каком TCP-состоянии
sudo tcpdrop # где именно ядро дропнуло сегмент, со стеком вызовов
sudo tcplife # сводка по каждому закрытому соединению (длительность, байты)
sudo tcptracer # connect/accept/close в реальном времени
Типичные грабли и заблуждения
- "ping хороший, значит сеть ок". ping меряет ICMP RTT, но не видит потери на TCP-уровне, переполнение буферов сокета и ретрансмиты. Низкий пинг и тормозящее приложение - обычная комбинация.
- "раз есть дропы - надо крутить буферы". Сначала пойми, ГДЕ дроп: missed на карте (ring), dropped в backlog и переполнение qdisc лечатся по-разному. Слепое увеличение rmem/wmem часто только маскирует проблему и съедает память, а на современных ядрах размеры буферов и так автотюнятся.
- Запуск tcpdump без -n на нагруженном хосте. Резолвинг имен сам генерит трафик и искажает картину, а на 10G еще и роняет производительность. Почти всегда нужен -n, а лучше -w в файл.
- Путать throughput и latency. "Скорость хорошая" (throughput) не отменяет того, что каждый запрос ждет лишний RTT из-за маленького cwnd или потерь.
- Recv-Q путают с проблемой сети. Большой Recv-Q - это почти всегда твое приложение медленно читает из сокета, сеть тут ни при чем.
- "netstat покажет". netstat и ifconfig устарели и местами врут на счетчиках дропов. На 2026 правильный набор - ss, ip, nstat, ethtool, и eBPF для глубины.
Мини-лаба: проверь руками прямо сейчас
- Запусти sudo ss -tinp state established и найди соединение с ненулевым Send-Q или retrans. Прочитай его rtt, cwnd и delivery_rate, прикинь, упирается ли throughput в окно (вспомни формулу окно/RTT).
- Сделай ip -s link show <твой интерфейс> два раза с паузой и посмотри, растут ли dropped/missed/errors. Растут - запиши, какой именно счетчик, и проверь его драйверного двойника через sudo ethtool -S <iface> | grep -E "drop|miss".
- Запусти nstat -az два раза с паузой под нагрузкой и найди, растут ли TcpRetransSegs и TcpExtListenDrops.
- В одном терминале запусти sudo tcpdump -ni <iface> -tt "tcp port 443", в другом дерни любой https-сайт через curl. Найди в дампе SYN, SYN-ACK, ACK (рукопожатие) и прикинь RTT по меткам времени.
- Если есть bpfcc-tools: запусти sudo tcpretrans под нагрузкой и сравни, что он ловит, с числом ретрансмитов из ss и nstat.
- Чем отличается throughput от latency и почему канал может быть "широким", но "медленным"?
- Что означают Recv-Q и Send-Q в выводе ss, и о чем говорит стабильно большой Recv-Q?
- Зачем нужен NAPI и что произойдет, если CPU не успевает выгребать ring buffer (как это будет видно в top и в ip -s)?
- Какой инструмент в 2026-м выберешь, чтобы с минимальным оверхедом увидеть, где именно ядро дропает TCP-сегмент, и почему не tcpdump?
Пакет едет лифтом через слои: приложение - сокет - TCP/UDP - IP - qdisc - драйвер с ring buffer - провод, и обратно через прерывания и NAPI. Задержки и потери - это всегда переполненная сетевая очередь, и весь фокус в том, чтобы найти, КАКАЯ именно переполнилась. Сокеты и буферы смотришь через ss, сводные счетчики стека - через nstat, счетчики карты - через ip -s и ethtool, сами пакеты - через tcpdump, а первопричину дропов и ретрансмитов в 2026-м быстрее всего ловить через eBPF (tcpretrans/tcpdrop). Сначала карта, потом инструмент - в обратном порядке только теряешь время.