MAATRIX / Блог / Потолок NAT: сколько клиентов спрячется за одним IP до первых отказов

Потолок NAT: сколько клиентов спрячется за одним IP до первых отказов

MAATRIX

«Посадим офис на один белый IP через NAT, зачем платить за второй» — решение звучит разумно, пока за этим IP не оказывается двести человек, каждый с браузером из полусотни вкладок, мессенджером и видеозвонком одновременно. NAT не откажет красиво: он не пришлёт понятную ошибку «портов больше нет» — он просто начнёт рвать случайные соединения у случайных людей, и разбираться в этом придётся уже постфактум. Разберём, что физически ограничивает число клиентов за одним IP, как посчитать запас заранее и что делать, когда шлюз уже упирается в потолок.

Что на самом деле ограничивает NAT: два разных потолка

У NAT-шлюза не один лимит, а два, и путать их — источник половины неверных диагнозов.

Первый — диапазон портов на один внешний IP. Когда шлюз транслирует адрес клиента в свой внешний IP, он обязан подменить ещё и исходящий порт, иначе не сможет различить, какому внутреннему клиенту вернуть ответный пакет. Портов у одного IP формально 65536, но нижний диапазон (0–1023) обычно зарезервирован под системные нужды и в трансляции не участвует, так что реально доступно порядка 64 тысяч портов на один внешний IP. Это именно диапазон для *трансляций* — не количество клиентов и не количество их устройств, а количество одновременно живых пар «внутренний адрес:порт → внешний порт», которые шлюз способен держать разведёнными.

Второй — таблица трансляций, она же conntrack. Каждое соединение, которое проходит через NAT, шлюз обязан запомнить: кто отправитель, кто получатель, какой порт кому назначен, в каком состоянии TCP-сессия. Эта запись живёт в памяти и стоит реальных мегабайт — не бесконечно. Максимальный размер таблицы задаётся отдельно от диапазона портов и на практике часто оказывается более узким местом, чем сами порты, особенно если приложения держат соединения открытыми подолгу.

Важно: оба лимита — про *шлюз*, а не про отдельного клиента. Клиент не «занимает IP», он занимает несколько записей в таблице и несколько портов на время жизни своих соединений. Как именно NAT сопоставляет внутренние и внешние адреса — отдельная тема, разобранная в статье как работает NAT.

Как посчитать реальную ёмкость NAT-шлюза

Формула для порогового расчёта простая, но в неё нужно подставлять честные числа, а не табличные:

Максимум клиентов ≈ Доступные порты (или лимит conntrack, что меньше)
                     ────────────────────────────────────────────────
                     Среднее число одновременных соединений на клиента
                     × коэффициент запаса (обычно 1.5–2)

Порядок расчёта:

  1. Определите узкое место. Сравните диапазон портов (~64 000 на один внешний IP) с текущим лимитом conntrack-таблицы. Лимит таблицы смотрится так:
sysctl net.netfilter.nf_conntrack_max
cat /proc/sys/net/netfilter/nf_conntrack_count   # текущее заполнение

Значение nf_conntrack_max по умолчанию отличается от дистрибутива к дистрибутиву и часто масштабируется от объёма RAM — не полагайтесь на цифру «из интернета», проверяйте на своей машине.

  1. Оцените среднее число соединений на клиента (следующий раздел) — это самая изменчивая переменная в формуле, и ошибка здесь ломает весь расчёт.
  1. Заложите запас, а не считайте по пиковому теоретическому потолку. Соединения открываются и закрываются неравномерно: пользователи заходят на сайты пачками, TCP-сессии зависают в TIME_WAIT (обычно от одной до нескольких минут после закрытия — конкретное значение задаётся net.ipv4.tcp_fin_timeout и поведением ОС) и продолжают занимать порт, хотя разговор уже закончился. Именно из-за TIME_WAIT реальная ёмкость всегда меньше, чем «порты / клиенты» по прямому делению — иногда заметно меньше. Механику этого эффекта разбирали отдельно в статье про то, как быстро кончаются исходящие порты.
  1. Пересчитывайте при росте. Ёмкость — не константа: новый сервис с постоянными вебсокетами или очередной чат с keep-alive на каждое устройство сдвигает среднее число соединений на клиента вверх, и вчерашний запас в 40% превращается в сегодняшний дефицит.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Сколько соединений в среднем открывает один клиент

Это самое неопределённое число в расчёте, и здесь важно не подставлять красивую цифру из чужой статьи, а прикинуть по своему трафику. Ниже — не измеренные значения, а ориентиры по порядку величины, чтобы было от чего оттолкнуться:

Тип нагрузкиПримерное число одновременных соединений на клиентаКомментарий
Обычный веб-сёрфинг (несколько вкладок)единицы–десяткибраузеры держат пул соединений на домен, HTTP/2 и HTTP/3 мультиплексируют трафик в меньшее число TCP/QUIC-сессий
Активный день с видеозвонками, стримингом, синхронизацией облакадесяткивидеозвонок, мессенджер, почта, облачный синк — каждый держит своё долгоживущее соединение
IoT-устройство с постоянным keep-aliveединицыобычно одно-два долгоживущих соединения, но их может быть *много устройств* на одного «клиента»-домохозяйство
Торрент-клиент, P2P-приложениесотни и вышездесь один клиент реально способен исчерпать заметную долю таблицы в одиночку
Сервер приложений/прокси за тем же NATможет быть неограниченноне «клиент» в бытовом смысле, требует отдельного учёта

Ключевой практический вывод: расчёт «на клиента» ломается, как только за NAT оказывается хоть одно устройство или приложение, которое ведёт себя не как обычный пользователь. Реальный разбор именно такого случая — когда одно-единственное приложение открыло десятки тысяч соединений и не закрывало их — есть в статье про инцидент с NAT-шлюзом: полезно прочитать, чтобы понимать, что «средним по больнице» такие случаи не описываются, и закладывать в расчёт не только среднее, но и максимум на клиента.

Симптомы приближения к потолку

Проблема с NAT почти никогда не выглядит как явная ошибка. Она выглядит как раздражающая нестабильность, которую сложно воспроизвести на заказ:

  • Случайные обрывы у части пользователей, не у всех сразу. Кому-то из офиса видео зависает, кто-то без проблем работает часами — потому что в таблице банально не осталось свободного слота именно в момент, когда чей-то клиент пытался открыть новое соединение.
  • Проблема усиливается в часы пик и почти исчезает ночью — прямой признак, что дело в заполненности таблицы или занятости портов, а не в самом приложении.
  • Новые соединения не устанавливаются, старые продолжают работать. Если у вас уже открыт SSH или видеозвонок — он живёт. А вот попытка открыть что-то новое (обновить страницу, перезайти после разрыва Wi-Fi) может зависать или падать по таймауту.
  • Ошибки без единой закономерности по приложению. Рвётся то видео, то VPN, то просто загрузка сайта — потому что NAT не разбирает, какой протокол внутри пакета, ему всё равно, чей это трафик.
  • На самом шлюзе всё выглядит спокойно: CPU не нагружен, память формально свободна, диск не при делах. Классическая ловушка — искать причину в производительности сервера, хотя дело в конкретном счётчике заполненности таблицы или в занятости диапазона портов.

Если картина совпадает — прежде чем чинить приложение или менять провайдера, проверьте именно эти два счётчика.

Диагностика: смотрим на цифры, а не гадаем

Прежде чем что-то менять, стоит убедиться, что дело действительно в NAT, а не в чём-то ещё. Несколько команд, которые быстро дают ответ (на Linux-шлюзе с netfilter):

# Заполненность таблицы трансляций и её лимит
cat /proc/sys/net/netfilter/nf_conntrack_count
sysctl net.netfilter.nf_conntrack_max

# Сколько записей в таблице занимает конкретный внутренний IP
conntrack -L -s 192.168.1.50 | wc -l

# Занятость портов на внешнем интерфейсе по состояниям TCP
ss -tan | awk '{print $1}' | sort | uniq -c | sort -rn

# Живая статистика по conntrack: сколько записей создаётся/удаляется в секунду
watch -n1 'cat /proc/sys/net/netfilter/nf_conntrack_count'

# Логирование отброшенных из-за переполнения таблицы пакетов (если включено)
dmesg | grep -i "nf_conntrack: table full"

Строка nf_conntrack: table full, dropping packet в dmesg — это прямое подтверждение диагноза: шлюз действительно упёрся в лимит таблицы и начал молча отбрасывать пакеты вместо того, чтобы транслировать их. Если такой строки нет, а обрывы всё равно есть — вероятнее исчерпание портов на конкретный внешний IP при большом числе соединений к одному и тому же адресу назначения (у SNAT есть дополнительное ограничение: пара «внешний IP + внешний порт» должна быть уникальна на *каждый адрес назначения отдельно*, что иногда даёт неожиданные упоры даже при формально свободной таблице). Подробный разбор именно такого переполнения — с симптомами и логами — есть в статье про переполнение conntrack.

Решения: как раздвинуть потолок

Когда расчёт или диагностика показали, что текущей ёмкости не хватает, вариантов несколько — и они не взаимоисключающие:

1. Несколько внешних IP вместо одного. Самое прямое решение: если у шлюза два или три внешних адреса и трафик распределяется между ними (round-robin по клиентам, по подсетям или через hash-балансировку), доступный пул портов умножается кратно числу IP. Технически это делается правилами SNAT/MASQUERADE с несколькими диапазонами адресов или через несколько независимых NAT-инстансов. Плюс — просто и предсказуемо. Минус — нужно выделять и оплачивать дополнительные IP, а если распределение клиентов по IP кривое (например, привязка по хэшу от внутреннего адреса), часть ёмкости может простаивать, пока другая перегружена.

2. Увеличить лимит таблицы conntrack. Если ёмкости по портам достаточно, а упирается именно таблица — лимит поднимается через sysctl:

sysctl -w net.netfilter.nf_conntrack_max=<новое_значение>
sysctl -w net.netfilter.nf_conntrack_buckets=<новое_значение>/4

Здесь есть потолок здравого смысла: каждая запись в таблице стоит памяти, и бездумно задранный лимит на слабой машине приведёт к тому, что таблица переживёт NAT, но убьёт сервер нехваткой RAM. Поднимайте лимит постепенно, проверяя фактическое потребление памяти, и закрепляйте изменение в /etc/sysctl.conf или /etc/sysctl.d/, иначе оно слетит при перезагрузке.

3. Сократить время жизни неиспользуемых записей. Часть таблицы обычно занята соединениями, которые формально ещё не закрыты, но фактически неактивны (зависшие TCP-сессии, недокрытые TIME_WAIT). Уменьшение таймаутов — net.netfilter.nf_conntrack_tcp_timeout_time_wait и соседних параметров — освобождает записи быстрее и повышает эффективную ёмкость без увеличения лимита. Делать это нужно осторожно: слишком агрессивное укорачивание таймаутов может закрывать реально ещё нужные соединения на нестабильных каналах.

4. Carrier-grade NAT (CGNAT) с большим пулом портов. Если клиентов десятки и сотни тысяч (характерно для мобильных операторов и крупных провайдеров, которые сажают много абонентов за ограниченный пул публичных IPv4), одиночным NAT-шлюзом задачу не решить в принципе — нужна архитектура с распределённым пулом внешних IP и портов, где каждому абоненту детерминированно (или динамически) выделяется свой диапазон портов на конкретном внешнем адресе. Это снимает часть проблем с диагностикой (известно, какой абонент на каком порту), но добавляет сложности с логированием для соответствия требованиям к хранению данных о подключениях — это уже отдельная тема на стыке сети и комплаенса.

5. Развести нагрузку по нескольким шлюзам. Если один физический или виртуальный NAT-инстанс упирается в CPU при обработке большого числа трансляций (а не только в память или порты), может помочь горизонтальное разделение — часть клиентов маршрутизируется через второй шлюз с собственным внешним IP и собственной таблицей. По сути это комбинация решений 1 и 4 в меньшем масштабе, доступная и для офисной, и для провайдерской инфраструктуры.

Универсального «правильного» решения нет — выбор зависит от того, что именно уперлось: порты, память под таблицу или CPU на обработку пакетов. Диагностика из предыдущего раздела как раз для того, чтобы не гадать, а увидеть это по счётчикам.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Сколько клиентов реально помещается за одним IP?

Единого числа нет — зависит от того, сколько одновременных соединений держит каждый клиент, и от лимита таблицы conntrack на конкретном шлюзе. Для обычного офисного трафика (веб, почта, мессенджеры) ориентировочно можно закладывать от нескольких сотен до пары тысяч активных пользователей на один внешний IP при типичном лимите таблицы, но это грубая прикидка, а не гарантия — считайте по формуле из статьи под свою нагрузку.

Что закончится раньше — порты или таблица conntrack?

Зависит от профиля трафика. При коротких соединениях с быстрым закрытием обычно первой упирается таблица (много кратковременных записей одновременно). При долгоживущих соединениях к разным адресам чаще упираются именно порты на конкретный внешний IP. Проверяйте оба счётчика — не полагайтесь на предположение, какой из них «обычно» узкий.

IPv6 решает эту проблему?

Да, принципиально: при полноценном IPv6 у каждого устройства может быть собственный глобальный адрес без трансляции, и вопрос ёмкости NAT снимается сам собой. На практике переход растягивается годами из-за смешанной инфраструктуры и провайдеров, которые до сих пор не отдают IPv6 клиентам, так что NAT на IPv4 остаётся практической реальностью для большинства сетей ещё надолго.

Можно ли увеличить лимит conntrack до бесконечности и забыть про проблему?

Нет — лимит ограничен памятью сервера, и слишком большая таблица не столько «решает» проблему, сколько переносит её из сети в нехватку RAM. Кроме того, на некоторых шлюзах поиск в очень большой таблице начинает заметно нагружать CPU. Увеличивать лимит нужно вместе с ресурсами сервера, а не вместо них.

NAT на домашнем роутере и NAT на серверном шлюзе — это одна и та же проблема?

Механика та же (порты + таблица трансляций), но масштаб и настройки разные. У бытовых роутеров таблица обычно совсем небольшая и негде её увеличить через sysctl — там при переполнении помогает либо более производительное железо, либо перенос NAT-функции на Linux-шлюз с полным контролем над параметрами ядра.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →