Проблема с MTU на reg.ru и ее решение через iptables
Опубликовано 05.08.2025
Введение: Скрытая проблема с сетью
Разработчики и системные администраторы, использующие серверы на платформе OpenStack (например, тарифные линейки C*-M*-D* у хостинг-провайдера reg.ru), иногда сталкиваются с загадочными сетевыми проблемами. Кажется, что интернет работает, но при попытке передать большие объемы данных или установить соединение с определенными сервисами, запросы могут зависать или обрываться по таймауту.
Провайдер объясняет эту проблему особенностями своей инфраструктуры:
Серверы на платформе OpenStack используют технологию VxLAN, которая резервирует 50 байт для служебной информации. Из-за этого максимальный размер единицы передачи данных (MTU) на основном сетевом интерфейсе сервера (
ens3) составляет 1450 байт.В то же время, Docker по умолчанию настраивает сетевые интерфейсы контейнеров с MTU 1500 байт. Это приводит к тому, что пакеты, отправленные из контейнера, превышают допустимый размер и не могут быть переданы в глобальную сеть.
Официальные решения и их ограничения
Решение, предложенное провайдером, заключается в ручном изменении MTU для Docker-контейнеров:
- В
docker-compose.ymlдля каждой сети нужно добавить параметрcom.docker.network.driver.mtu: 1450. - Для
docker runиdocker buildнеобходимо отредактировать или создать файл/etc/docker/daemon.json, указав в нём"mtu": 1450.
Эти методы, безусловно, решают проблему, но имеют существенные недостатки:
- Не глобальны: Требуют ручного изменения конфигурации для каждого проекта (
docker-compose) или для каждой новой установки Docker. - Не решают проблему для существующих контейнеров: Требуется перезапуск и пересоздание всех контейнеров, что может быть неудобно.
- Легко забыть: Разработчик, переносящий проект на сервер с такой особенностью, может не сразу вспомнить о необходимости изменить MTU, что приведёт к потере времени на отладку.
Глобальное и элегантное решение с помощью iptables
Вместо того чтобы вручную менять настройки каждого контейнера, можно решить эту проблему один раз и навсегда на уровне самого сервера, используя iptables.
Суть решения заключается в следующем: мы не будем менять MTU в Docker, а вместо этого воспользуемся возможностью iptables автоматически изменять специальное значение в TCP-пакетах — MSS (Maximum Segment Size). MSS — это максимальный размер полезной нагрузки в TCP-пакете. Он на 40 байт меньше, чем MTU (20 байт для заголовка IP и 20 байт для заголовка TCP).
Правило iptables заставит TCP-пакеты, исходящие с нашего сервера, “сообщать” о корректном MSS, основанном на MTU исходящего интерфейса. Таким образом, удалённый хост будет отправлять нам пакеты меньшего размера, и проблема с MTU будет решена на уровне TCP-соединения.
Инструкция по применению iptables
Чтобы применить это правило, нам нужно добавить одну строку в iptables.
Определите имя вашего основного сетевого интерфейса. Обычно это
ens3илиeth0. Вы можете проверить это с помощью командыip a. Затем внесите имя интерфеса в переменную IFACE_NAME.export IFACE_NAME=ens3Выполните следующую команду:
sudo iptables -t mangle -A POSTROUTING \
-p tcp --tcp-flags SYN,RST SYN -o $IFACE_NAME \
-j TCPMSS --clamp-mss-to-pmtu
Что делает эта команда?
sudo iptables: Выполняет командуiptablesс правами администратора.-t mangle: Указывает, что мы работаем с таблицейmangle, которая используется для изменения пакетов.-A POSTROUTING: Добавляет правило в цепочкуPOSTROUTING. Эта цепочка обрабатывает пакеты, которые только что покинули локальную сеть и готовы отправиться в глобальную.-p tcp --tcp-flags SYN,RST SYN: Фильтрует только первый пакет в TCP-соединении (SYN), который сообщает о максимальном размере сегмента.-o $IFACE_NAME: Указывает, что правило применяется только к пакетам, исходящим через основной сетевой интерфейс сервера (в нашем примере этоens3).-j TCPMSS --clamp-mss-to-pmtu: Цель правила —TCPMSS. Эта цель автоматически вычисляет максимальный MSS на основе MTU исходящего интерфейса и устанавливает его. Это более универсальное решение, чем установка фиксированного значения.
- Сделаем правило постоянным
Правила iptables по умолчанию временные и исчезнут после перезагрузки сервера. Чтобы сохранить правило, воспользуйтесь системными утилитами. В большинстве современных дистрибутивов Linux (Ubuntu, Debian) можно использовать netfilter-persistent.
Для Ubuntu/Debian:
sudo apt-get install netfilter-persistent
sudo netfilter-persistent save
sudo systemctl enable netfilter-persistent
Заключение
Решение с iptables является более предпочтительным, чем ручное изменение конфигурации Docker. Оно позволяет:
- Исправить проблему глобально: Теперь все контейнеры, независимо от того, как они были созданы, будут работать корректно.
- Сэкономить время: Больше не нужно вспоминать о необходимости вносить изменения в
docker-compose.ymlилиdaemon.jsonдля каждого нового проекта. - Избежать простоев: Не требуется пересоздавать уже запущенные контейнеры.
- Быть универсальным: Использование флага
--clamp-mss-to-pmtuделает решение не зависящим от конкретного значения MTU.
Это элегантное и надёжное решение позволяет сосредоточиться на разработке, а не на проблемах инфраструктуры, и идеально подходит для разработчиков, которые ценят своё время.