34. Наблюдаемость сети. tcpdump, Wireshark, netstat, ss
Введение в сетевую наблюдаемость
В диагностике сложных распределенных систем существует старое медицинское правило: когда пациенту плохо, хороший врач не гадает по симптомам, а слушает стетоскопом, берет анализ крови и делает томографию.
В высоконагруженных Go-системах узким местом редко становится чистая вычислительная мощность процессора. Ваше приложение в утилите top может выглядеть абсолютно безмятежно — потреблять скромные 5% CPU и 200 МБ памяти, — но внешние клиенты при этом будут захлебываться от ошибок 504 Gateway Timeout. Чаще всего деградация вызвана сетевыми задержками, утечками файловых дескрипторов, неоптимальным использованием пулов соединений или блокировками на уровне ядра. Инспекция трафика и состояния сокетов позволяет отделить проблемы рантайма Go от проблем инфраструктуры и сетевого стека.
Инструменты сетевой наблюдаемости (tcpdump, Wireshark, ss) — это и есть стетоскоп, рентген и кардиограф бэкенд-инженера.
tcpdump: Сырой трафик и BPF-фильтры
tcpdump — классический инструмент для захвата пакетов. В современных Linux он использует AF_PACKET сокеты и библиотеку libpcap.
Когда сетевая карта принимает кадр Ethernet, драйвер помещает его в кольцевой буфер приема (Rx ring). Специальный хук ядра dev_add_pack перехватывает пакет и дублирует его для сокета AF_PACKET, не дожидаясь, пока стек TCP/IP разберет заголовки.
flowchart TD
classDef process fill:#1e293b,stroke:#38bdf8,stroke-width:1px,color:#f8fafc
classDef success fill:#064e3b,stroke:#34d399,stroke-width:1px,color:#f8fafc
classDef error fill:#7f1d1d,stroke:#f87171,stroke-width:1px,color:#f8fafc
classDef warning fill:#78350f,stroke:#fbbf24,stroke-width:1px,color:#f8fafc
classDef entry fill:#1e3a5f,stroke:#38bdf8,stroke-width:2px,color:#f8fafc
classDef aux fill:#334155,stroke:#64748b,stroke-width:1px,color:#f8fafc
classDef system fill:#312e81,stroke:#a78bfa,stroke-width:1px,color:#f8fafc
classDef data fill:#132d29,stroke:#2dd4bf,stroke-width:1px,color:#f8fafc
classDef network fill:#881337,stroke:#fb7185,stroke-width:1px,color:#f8fafc
classDef accent fill:#1e1b4b,stroke:#818cf8,stroke-width:1px,color:#f8fafc
NIC["Сетевая карта (NIC / DMA)"]:::system --> RxRing["Кольцевой буфер Rx ядра Linux"]
RxRing --> Tap{"AF_PACKET Hook"}
Tap -->|"Классический путь"| Netfilter["Netfilter / TCP/IP Stack"]
Netfilter --> SocketBuf["Socket Receive Buffer"]
SocketBuf --> GoApp["Go Runtime (netpoller / epoll)"]
Tap -->|"Дублирование пакета"| BPF["BPF Filter (In-Kernel)"]
BPF -- "Совпало" --> PcapBuf["libpcap Ring Buffer"]
BPF -- "Не совпало" --> Drop["Мгновенный сброс без оверхеда"]
PcapBuf --> Tcpdump["tcpdump (Userspace)"]
Ключевая особенность — BPF (Berkeley Packet Filter). Фильтры компилируются в байткод, который выполняется внутри ядра до передачи пакета в userspace. Это радикально снижает нагрузку на CPU и количество контекстных переключений, так как отбрасываются нецелевые пакеты на раннем этапе.
Для Go-разработчика критично понимать, как фильтровать трафик без деградации продакшена:
# Захват только TCP-пакетов для конкретного сервиса (порт 8080), без DNS-развертки
sudo tcpdump -i eth0 -n -s 0 'tcp port 8080' -w capture.pcap
Wireshark: Глубокий анализ протоколов
Wireshark — GUI-фронтенд для libpcap. Его сила — в встроенных диссекторах (разборщиках протоколов). Для Go-бэкенда он незаменим при отладке:
- TLS/HTTPS: Визуализация
TLS Handshake, проверка сертификатов, анализClientHello/ServerHello. - HTTP/2 & gRPC: Разбор фреймов, приоритетов потоков,
HEADERS,DATA,GOAWAY. Позволяет увидетьHEAD-of-Lineблокировки или асимметрию вPushPromise. - Результаты: Экспорт в
tsharkдля CI/CD или скриптов автоматизации.
netstat vs ss: Архитектура и производительность
Оба инструмента показывают состояние сокетов, но их архитектура кардинально различается.
flowchart TD
classDef process fill:#1e293b,stroke:#38bdf8,stroke-width:1px,color:#f8fafc
classDef success fill:#064e3b,stroke:#34d399,stroke-width:1px,color:#f8fafc
classDef error fill:#7f1d1d,stroke:#f87171,stroke-width:1px,color:#f8fafc
classDef warning fill:#78350f,stroke:#fbbf24,stroke-width:1px,color:#f8fafc
classDef entry fill:#1e3a5f,stroke:#38bdf8,stroke-width:2px,color:#f8fafc
classDef aux fill:#334155,stroke:#64748b,stroke-width:1px,color:#f8fafc
classDef system fill:#312e81,stroke:#a78bfa,stroke-width:1px,color:#f8fafc
classDef data fill:#132d29,stroke:#2dd4bf,stroke-width:1px,color:#f8fafc
classDef network fill:#881337,stroke:#fb7185,stroke-width:1px,color:#f8fafc
classDef accent fill:#1e1b4b,stroke:#818cf8,stroke-width:1px,color:#f8fafc
subgraph NetstatPath ["Устаревший netstat"]
NProc["Чтение файлов procfs:<br/>/proc/net/tcp, /proc/net/sockstat"]:::network
NProc --> NParse["Форматирование и парсинг текста в userspace<br/>(Блокировка таблиц сокетов ядра!)"]
NParse --> NOut["netstat: Высокий iowait / O(N) латентность"]
end
subgraph SSPath ["Современный ss"]
SKernel["Ядро Linux (TCP/IP стек)"]
SKernel -->|"Syscall netlink (NETLINK_INET_DIAG)"| NL["Прямой бинарный трансфер без парсинга текста"]
NL --> SOut["ss: Мгновенный O(1) дамп миллионов сокетов"]
end
| Характеристика | netstat |
ss |
|---|---|---|
| Источник данных | /proc/net/tcp, /proc/net/sockstat (procfs) |
netlink сокеты (прямой запрос к ядру) |
| Производительность | Очереди, парсинг текстовых файлов | getsockopt/netlink запросы, мгновенно |
| Поддержка | Старые TCP-состояния | QUIC, BBR, send/recv очереди, PID процессов |
Go-специфика: Связь инструментов с netpoller и рантаймом
Когда вы отлаживаете Go-сервис через ss или tcpdump, важно помнить о модели G-M-P.
stateDiagram-v2
classDef process fill:#1e293b,stroke:#38bdf8,stroke-width:1px,color:#f8fafc
classDef success fill:#064e3b,stroke:#34d399,stroke-width:1px,color:#f8fafc
classDef error fill:#7f1d1d,stroke:#f87171,stroke-width:1px,color:#f8fafc
classDef warning fill:#78350f,stroke:#fbbf24,stroke-width:1px,color:#f8fafc
classDef entry fill:#1e3a5f,stroke:#38bdf8,stroke-width:2px,color:#f8fafc
classDef aux fill:#334155,stroke:#64748b,stroke-width:1px,color:#f8fafc
classDef system fill:#312e81,stroke:#a78bfa,stroke-width:1px,color:#f8fafc
classDef data fill:#132d29,stroke:#2dd4bf,stroke-width:1px,color:#f8fafc
classDef network fill:#881337,stroke:#fb7185,stroke-width:1px,color:#f8fafc
classDef accent fill:#1e1b4b,stroke:#818cf8,stroke-width:1px,color:#f8fafc
[*] --> SYN_RECEIVED: Входящий SYN
SYN_RECEIVED --> ESTABLISHED: Тройное рукопожатие завершено
state ESTABLISHED {
[*] --> ActiveReadWrite: Обработка в Go goroutine
ActiveReadWrite --> RemoteFIN: Клиент прислал FIN
}
RemoteFIN --> CLOSE_WAIT: Ядро ответило ACK
note right of CLOSE_WAIT
ЛОВУШКА В GO:
Если в коде забыли resp.Body.Close()
или conn.Close(), сокет навсегда
застревает в CLOSE_WAIT!
end note
CLOSE_WAIT --> LAST_ACK: Go вызвал conn.Close()
LAST_ACK --> [*]: Получен финальный ACK
- Утечки соединений (
CLOSE_WAIT): Еслиss -t state close-waitпоказывает множество соединений от вашего PID, это почти всегда означает, что в Go коде не вызванresp.Body.Close()илиconn.Close(). Удаленная сторона отправилаFIN, ядро перевело сокет вCLOSE_WAIT, но прикладной поток не закрыл дескриптор. TIME_WAITи истощение портов: При высокой скорости создания/завершения соединений (например, в гравитационных тестах или при плохой настройкеhttp.Transport) возникает 39. TIME_WAIT, Port Exhaustion и другие проблемы TCP-сервисов.ss -t state time-wait \| wc -lпокажет нагрузку. Go не управляетtcp_tw_reuseнапрямую, но может минимизировать проблему через пул соединений (MaxIdleConns,IdleConnTimeout).- Блокировки и
epoll:ss -pпоказывает PID процесса, владеющего сокетом. Если вы видите, что сокет принадлежит PID Go-рантайма, но трафик не идет (tcpdumpпоказывает пустоту), проверьте, не заблокирован лиnetpollerили не исчерпан лиepollлимит (fs.epoll.max_user_watches).
Практический чек-лист отладки для Go-бэкенда
- Сервис не отвечает:
ss -t state established \| grep <pid>->tcpdump -i any port <port> -n-> проверкаHEADERS/DATAв Wireshark. - Высокая latency:
ss -t -i(показываетssthresh,cwnd,rtt,bbrпараметры) -> анализtcpdumpна предмет retransmission. - Утечка памяти/файловых дескрипторов:
ss -p \| grep <pid> \| wc -lvslsof -p <pid> \| grep sock. Еслиssпоказывает меньше сокетов, чемlsof, возможно, дескрипторы утекли вsyscallили не закрыты вdefer. - Проблемы с TLS/gRPC: Захват трафика Wireshark -> фильтр
tls.handshake.type == 1(Client Hello) -> проверка сертификатов и cipher suites.
Итог
Сетевая наблюдаемость в Go требует понимания границы между рантаймом и ядром. tcpdump и libpcap дают доступ к сырым пакетам с минимальными издержками благодаря BPF. ss вытеснил netstat благодаря netlink и производительности. Wireshark незаменим для протокольного уровня (TLS, HTTP/2, gRPC). Понимание того, как netpoller взаимодействует с epoll, и как состояния сокетов отражаются в Go-коде, позволяет быстро находить утечки, блокировки и узкие места в высоконагруженных системах.
В следующей статье мы разберем инструменты диагностики и устранения проблем: 35. Диагностика сети. ping, traceroute, dig, curl, mtr, чтобы научиться быстро находить разрывы в цепочке доставки пакетов.