Флаг: English English

Проблема с 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.

Эти методы, безусловно, решают проблему, но имеют существенные недостатки:

  1. Не глобальны: Требуют ручного изменения конфигурации для каждого проекта (docker-compose) или для каждой новой установки Docker.
  2. Не решают проблему для существующих контейнеров: Требуется перезапуск и пересоздание всех контейнеров, что может быть неудобно.
  3. Легко забыть: Разработчик, переносящий проект на сервер с такой особенностью, может не сразу вспомнить о необходимости изменить 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.

  1. Определите имя вашего основного сетевого интерфейса. Обычно это ens3 или eth0. Вы можете проверить это с помощью команды ip a. Затем внесите имя интерфеса в переменную IFACE_NAME.

    export IFACE_NAME=ens3
    
  2. Выполните следующую команду:

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 исходящего интерфейса и устанавливает его. Это более универсальное решение, чем установка фиксированного значения.
  1. Сделаем правило постоянным

Правила 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.

Это элегантное и надёжное решение позволяет сосредоточиться на разработке, а не на проблемах инфраструктуры, и идеально подходит для разработчиков, которые ценят своё время.

Нужна помощь?

Свяжись со мной и я помогу решить проблему

Похожие посты