Сокет с ReusePort: соединения распределяются неравномерно
Параметр повторного использования порта позволяет нескольким процессам слушать один адрес: ядро распределяет соединения между ними. Это удобно для масштабирования, но распределение не идеально равномерное, и при разном числе экземпляров получается перекос.
Вероятные причины
По порядку: сверху то, что встречается чаще.
-
Экземпляры запущены не одновременно
Ядро распределяет новые соединения между текущими слушателями. Экземпляр, поднявшийся позже, получает меньше, пока не установятся новые соединения.
-
Соединения долгоживущие
Распределение работает при установке соединения. Постоянные соединения закрепляются за одним экземпляром надолго.
-
Параметр не поддерживается программой
Программа должна открывать сокет с нужным флагом. При сокет-активации это делает systemd параметром сокета.
Диагностика
Команды идут в том порядке, в котором их стоит выполнять: каждая следующая проверяет то, что осталось после предыдущей.
Сколько процессов слушает адрес.
sudo ss -tlnp | grep портОбщее число соединений на порту.
sudo ss -tan | grep порт | wc -lРешение
Решение своё для каждой причины. Сначала определите, какая из них ваша, — иначе правки наложатся друг на друга.
- Почему происходит
- Ядро распределяет новые соединения между текущими слушателями. Экземпляр, поднявшийся позже, получает меньше, пока не установятся новые соединения.
- Как проверить
-
Посмотрите число соединений по процессам.
sudo ss -tlnp | grep :8080 sudo ss -tan | grep :8080 | wc -l
- Как исправить
- Для долгоживущих соединений перекос сохраняется. Балансировщик впереди даёт более предсказуемое распределение, чем повторное использование порта.
- Почему происходит
- Распределение работает при установке соединения. Постоянные соединения закрепляются за одним экземпляром надолго.
- Как проверить
-
Посмотрите время жизни соединений.
sudo ss -tno | grep :8080 | head
- Как исправить
- Ограничьте время жизни соединений или поставьте перед службой балансировщик, который умеет их перераспределять.
- Почему происходит
- Программа должна открывать сокет с нужным флагом. При сокет-активации это делает systemd параметром сокета.
- Как проверить
-
Посмотрите параметр сокета.
systemctl show myapp.socket -p ReusePort 2>/dev/null
- Как исправить
-
Задайте
ReusePort=yesв описании сокета либо включите соответствующую настройку в самой программе.
Пример вывода
Два экземпляра на одном порту, распределение неравномерно. Пример показательный: он собран на тестовой машине специально для этой страницы, а не взят из чужого журнала.
$ sudo ss -tlnp | grep :8080
LISTEN 0 4096 *:8080 *:* users:(("myapp",pid=1200,fd=6))
LISTEN 0 4096 *:8080 *:* users:(("myapp",pid=1340,fd=6))
$ sudo ss -tan | grep -c "1200"
412
$ sudo ss -tan | grep -c "1340"
88
$ systemctl show myapp.socket -p ReusePort
ReusePort=yes
Связанные ошибки
- Address already in use при запуске службы Порт или адрес уже занят: bind() failed (98: Address already in use). Как найти владельца порта и что делать с остатками прежнего процесса.
- Служба не получает сокет при сокет-активации Сокет активен, служба запускается, но не находит переданный дескриптор. Разбор: Accept, FileDescriptorName, соответствие имён.
- HAProxy: все серверы группы недоступны Балансировщик отдаёт 503: проверки состояния не проходят. Разбор проверок и таймаутов.
- Сокет отклоняет соединения: достигнут MaxConnections При Accept=yes число одновременных экземпляров ограничено. Как поднять предел и когда режим по соединению не подходит.
- Сокет с Accept=yes: на каждое соединение свой экземпляр службы Служба порождает десятки экземпляров: при приёме соединений сокетом на каждое создаётся отдельный процесс.
- Bad file descriptor в журнале службы Ошибка 9 при операции с файлом: дескриптор закрыт или не тот. Разбор для служб и сокет-активации.
- File exists при создании файла или сокета Ошибка 17: объект уже существует. Разбор для сокетов, файлов блокировки и каталогов службы.
- Gunicorn: WORKER TIMEOUT в журнале службы Рабочие процессы Gunicorn убиваются по таймауту: медленные запросы, блокирующие вызовы, неверный тип обработчика.
Где встречается чаще всего
Источники
-
systemd.socket(5)
ReusePort= и распределение соединений. -
Воспроизведено на тестовой машине, systemd 255 (Ubuntu 24.04)
Проверено двумя экземплярами службы с повторным использованием порта.