Jumbo frames включили на половине пути: файлы копировались, база отваливалась
Включили Jumbo Frames между сервером приложения и сервером базы данных во внутренней сети — хотели ускорить передачу больших объёмов данных между своими же машинами. Копирование файлов работало исправно, а вот соединения к базе стали периодически подвисать и рваться непонятным образом. Разбираем, почему так вышло и как это диагностировать за час, а не за два дня.
Содержание
С чего всё началось
Два сервера в одной приватной сети (VLAN на 10.0.x.0/24, без выхода наружу) — один под приложение, второй под PostgreSQL. Между ними гоняются как крупные дампы при бэкапах, так и постоянный поток мелких запросов от приложения к базе. Кто-то посчитал, что раз трафика много и он «толстый», стандартный MTU 1500 — это накладные расходы на заголовки и лишние прерывания сетевой карты, а Jumbo Frames с MTU 9000 сократят число пакетов и разгрузят обе машины.
Изменение внесли только на двух серверах: подняли MTU на сетевых интерфейсах, которые смотрят друг на друга. Команда была примерно такая:
# на сервере приложения и на сервере БД
ip link set dev eth1 mtu 9000
Или через netplan/ifupdown — не суть, суть в том, что тронули только конечные точки. Про виртуальный коммутатор гипервизора (bridge), через который эти два сервера физически связаны на своём хосте, никто не вспомнил — он остался на MTU 1500 по умолчанию.
Первую неделю всё выглядело нормально. Ночной rsync бэкапов дампов базы отрабатывал штатно, копирование крупных файлов между серверами через scp тоже не вызывало нареканий. А вот днём, под обычной нагрузкой, в логах приложения стали появляться разрывы соединений к PostgreSQL — не постоянно, а с частотой в несколько раз за сутки, без явной привязки к времени или нагрузке по CPU/памяти.
Первые подозрения — не туда
Естественная первая реакция в такой ситуации — грешить на саму базу. Проверили:
max_connectionsи текущее число активных соединений — не упирались;statement_timeoutиidle_in_transaction_session_timeout— не настроены агрессивно, разрывы не совпадали с их значениями;- логи PostgreSQL на предмет
could not receive data from clientиunexpected EOF on client connection— эти записи были, но без внятной причины на стороне самой СУБД; - нагрузку по диску и swap — всё в пределах нормы, никакого распухания WAL или долгих чекпоинтов в момент сбоев.
Полдня ушло на пересмотр конфигурации пула соединений в приложении (PgBouncer), на подозрения в сторону keepalive-таймаутов TCP и на попытки поймать проблему через pg_stat_activity. Ничего не сходилось: соединения рвались как будто случайно, но именно в те моменты, когда одновременно с мелкими запросами шёл более крупный ответ от базы — например выборка с сотнями строк или передача результата JOIN на несколько десятков килобайт.
Именно это сочетание — «простые файлы копируются нормально, а конкретно к базе периодически рвётся» — довольно легко списать на что угодно, кроме сети, потому что сеть между своими же серверами в одной стойке интуитively кажется самым надёжным звеном. Обычно подозревают в первую очередь само приложение или саму базу, а сеть проверяют в последнюю очередь именно потому, что она «внутренняя, локальная, чего там могло сломаться».
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверПереключение внимания на сеть
Смена гипотезы произошла, когда заметили конкретную деталь: обрывы совпадали не с общим объёмом трафика, а именно с размером отдельных пакетов, близких к новому MTU. Крупные файлы через scp/rsync передаются протоколом поверх TCP, который сам справляется с фрагментацией и ретрансмиссией достаточно терпеливо — задержка на потерянном сегменте просто продлевает копирование, пользователь этого не замечает. А вот протокол PostgreSQL на конкретных пакетах, которые должны были пройти одним Jumbo-кадром, а фактически спотыкались о более узкое звено, вызывал у клиентской библиотеки таймаут или обрыв соединения — и это было куда заметнее, потому что ломало не передачу файла, а активный запрос приложения.
Стали проверять MTU не «в целом на сервере», а звено за звеном:
# MTU на интерфейсе сервера приложения
ip link show eth1 | grep mtu
# 9000 — ок
# MTU на интерфейсе сервера БД
ip link show eth1 | grep mtu
# 9000 — тоже ок
# а вот на виртуальном мосту гипервизора, через который оба сервера подключены
ip link show vmbr1 | grep mtu
# 1500 — вот и оно
Оба сервера были настроены правильно, но путь между ними физически проходил через виртуальный коммутатор (Linux bridge на гипервизоре KVM), у которого MTU остался стандартным. Часть трафика — заголовки TCP/IP, ACK, мелкие запросы — легко укладывалась в 1500 байт и проходила без проблем. А пакеты, которые отправитель формировал с расчётом на MTU 9000 (то есть близкие к 9000, но не обязательно максимальные — где-то в диапазоне 4000-8000 байт, в зависимости от того, что именно передавала база в ответ), на бридже либо резались непредсказуемо, либо просто дропались, если ядро не успевало корректно их фрагментировать на лету.
Почему рассинхрон MTU вообще возможен
Jumbo Frames — это в чистом виде договорённость на уровне L2 (Ethernet). Кадр большего размера должен одинаково приниматься и передаваться абсолютно каждым устройством на пути: сетевой картой сервера-отправителя, сетевой картой сервера-получателя и всеми промежуточными коммутаторами/мостами/маршрутизаторами между ними. Если хотя бы одно звено настроено на MTU 1500, а остальные — на 9000, получается классический mismatch:
| Звено | MTU до изменения | MTU после изменения | Проблема |
|---|---|---|---|
| eth1 сервера приложения | 1500 | 9000 | настроено |
| eth1 сервера БД | 1500 | 9000 | настроено |
| vmbr1 (мост гипервизора) | 1500 | 1500 | забыли |
| физический коммутатор (если есть) | 1500 | не проверяли | не проверяли |
В отличие от IP-фрагментации на уровне L3, где маршрутизатор в принципе может разбить слишком крупный пакет на несколько (если не выставлен флаг DF — Don't Fragment), на уровне L2 кадр большего размера, чем разрешает конкретный порт коммутатора или моста, обычно просто отбрасывается как невалидный (превышение MTU интерфейса), либо — в случае программных мостов на Linux — поведение зависит от реализации и может приводить к молчаливому дропу без внятной диагностики на стороне отправителя. TCP это видит как потерю сегмента и запускает свой механизм ретрансмиссии, но если это происходит систематически именно на пакетах определённого размера, соединение начинает деградировать вплоть до разрыва по таймауту — с точки зрения приложения выглядит как «база иногда не отвечает».
Диагностика: тест ping с DF-флагом на каждом звене
После обнаружения расхождения MTU на бридже стало ясно, как правильно тестировать подобные вещи впредь — не полагаться на «настроили — значит работает», а прогонять целевой размер пакета с флагом «не фрагментировать» именно между реальными эндпоинтами через реальный путь:
# с сервера приложения на сервер БД, полезная нагрузка 8972 байта
# (8972 + 28 байт заголовков ICMP/IP = 9000 байт — весь кадр)
ping -M do -s 8972 10.0.1.20
Если путь действительно пропускает Jumbo Frames целиком, ответ будет обычным, без ошибок. Если хотя бы одно звено настроено на меньший MTU, получим:
ping: local error: message too long, mtu=1500
Именно эта команда с MTU-целевым размером и флагом DF сразу же указала на бридж — тест с сервера приложения напрямую на сервер БД (по факту через мост) падал с ошибкой mtu=1500, хотя оба конечных интерфейса были выставлены на 9000. То есть проблема была не в конечных точках, а в промежуточном звене, о котором просто забыли при внесении изменения.
Полезно гонять такой тест не только между двумя серверами напрямую, но и пошагово, если есть промежуточные узлы:
# от сервера приложения до самого моста (если у моста есть свой IP)
ping -M do -s 8972 <ip-моста>
# от сервера БД до того же моста
ping -M do -s 8972 <ip-моста>
Это позволяет локализовать, какое именно звено не пропускает increased MTU, не гадая, а глядя на конкретный ответ команды.
Исправление и что проверяли после
Исправление было простым по сути и заняло меньше времени, чем поиск причины: подняли MTU на виртуальном мосту гипервизора до 9000, синхронно с обоими серверами.
ip link set dev vmbr1 mtu 9000
Если мост создаётся через netplan или конфиг libvirt/Proxmox — правили в соответствующем месте, чтобы настройка пережила перезагрузку хоста, а не только руками через ip link, которая слетает при рестарте сетевой службы.
После правки повторно прогнали ping -M do -s 8972 на каждом сегменте пути — все звенья ответили без ошибок. Дополнительно проверили стабильность под нагрузкой: несколько часов мониторили логи PostgreSQL на предмет обрывов и параллельно гоняли повторяющийся тест передачи файлов вперемешку с обычной нагрузкой приложения на базу. Обрывы прекратились полностью.
Отдельно стоит сказать: если в сети есть ещё и физический коммутатор между хостами гипервизоров (не только виртуальный мост внутри одного хоста), проверка должна включать и его — большинство управляемых коммутаторов позволяют посмотреть настроенный MTU на конкретном порту через свой веб-интерфейс или CLI, и если он не поддерживает Jumbo Frames вовсе (бывает на бюджетных моделях) или настроен на меньшее значение — вся цепочка ограничена самым узким звеном, сколько бы серверов ни настроили правильно.
Практические выводы на будущее
Из этого случая вынесли простое правило, которое теперь применяют при любом изменении MTU:
- Jumbo Frames — это настройка всего пути, а не конечных точек. Прежде чем менять MTU на серверах, составить список всех промежуточных звеньев: сетевые карты обоих серверов, виртуальные мосты/коммутаторы гипервизоров, физические коммутаторы, если они есть в сегменте. Каждое из них нужно проверить и, если требуется, изменить — заранее, а не по факту сбоя.
- Тестировать целевым размером пакета с DF, а не просто пингом по умолчанию. Обычный
pingбез флагов ничего не покажет — маленькие ICMP-пакеты пройдут через любое звено независимо от MTU. Показательна только проверка размером, близким к новому MTU, с флагом «не фрагментировать». - Не полагаться на «настроили — значит везде». Изменение конфига на двух серверах не гарантирует, что промежуточное сетевое оборудование (особенно виртуальное, создаваемое автоматически при разворачивании ВМ) подхватило то же значение — виртуальные мосты часто создаются с MTU по умолчанию и не синхронизируются с интерфейсами гостевых машин автоматически.
- Проверять эффект на реальной смешанной нагрузке, а не только большими файлами. В этом случае именно устойчивость файлового протокола к потерям и ретрансмиссиям маскировала проблему — стоит специально гонять тест с профилем нагрузки, похожим на то, что реально идёт по этому каналу (мелкие запросы вперемешку со средними и крупными пакетами), а не только однократную передачу большого файла.
- Логировать и мониторить именно сетевой уровень при подозрении на подобные обрывы, не ограничиваясь логами приложения и базы — счётчики ошибок и отброшенных пакетов на интерфейсах (
ip -s link show) часто показывают ростdropped/errorsдаже до того, как это долетит до логов приложения в виде понятной записи.
Если раскатываете Jumbo Frames на собственной инфраструктуре — будь то физические серверы в одной стойке или виртуальные машины на одном или нескольких гипервизорах — не забудьте, что сетевая карта сервера это только один конец. Смежная тема с похожим по природе, но другим по масштабу источником проблем — фрагментация MTU уже не внутри своей сети, а между клиентом и сервером через интернет — разобрана в статье MTU и фрагментация: откуда загадочные обрывы. Там же, но с фокусом на то, как аналогичный симптом («дело не в приложении») привёл к правильному диагнозу, — в статье Полдня искали в приложении, а дело было в MTU.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Можно ли включить Jumbo Frames только на одном из двух серверов, а не на обоих?
Нет смысла — MTU согласуется по наименьшему значению среди участников соединения, так что если один интерфейс настроен на 9000, а другой на 1500, реально будет использоваться 1500 на этом плече, просто без явной ошибки, пока размер пакета не превысит меньшее значение.
Как быстро проверить, что весь путь между двумя серверами действительно поддерживает Jumbo Frames, не меняя ничего в конфигурации?
Прогнать ping -M do -s 8972 <адрес> (Linux) или ping -f -l 8972 <адрес> (Windows, флаг -f соответствует DF) — при отсутствии ошибок весь путь пропускает кадры нужного размера, при ошибке message too long или таймауте — где-то в цепочке узкое звено.
Нужно ли включать Jumbo Frames на управляющей сети (management), если она отделена от сети передачи данных?
Как правило нет смысла — Jumbo Frames имеет смысл там, где реально гоняются крупные объёмы данных пакетами, близкими к максимальному размеру (репликация БД, бэкапы, сторедж-трафик); на management-сети с редкими мелкими пакетами эффект не будет заметен, а риск рассинхрона MTU добавится без выгоды.
Что делать, если физический коммутатор в цепочке не поддерживает Jumbo Frames, а поменять его сейчас нельзя?
Тогда стоит либо отказаться от увеличенного MTU на этом сегменте вовсе, либо, если коммутатор используется только как транзит между двумя определёнными хостами, изолировать эту пару в отдельный VLAN/сегмент, где Jumbo Frames настроены только там, где реально поддерживаются всеми участниками, не трогая остальную сеть.
Как понять, что текущие обрывы соединений к базе — это именно MTU, а не что-то другое?
Первый признак — обрывы совпадают по времени именно с передачей более крупных пакетов (не самых маленьких запросов), а не с общей нагрузкой по CPU/памяти/диску; второй — тест ping -M do с размером, близким к настроенному MTU, между теми же хостами воспроизводит ошибку стабильно.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →