Балансировка нагрузки TCP и UDP

  • Михаил
  • 12 мин. на прочтение
  • 48
  • 30 Jun 2022
  • 30 Jun 2022

В этой главе описывается, как использовать NGINX Plus и NGINX с открытым исходным кодом для проксирования и балансировки нагрузки трафика TCP и UDP.

 

Введение

Балансировка нагрузки относится к эффективному распределению сетевого трафика между несколькими внутренними серверами.

В NGINX Plus Release 5 и более поздних версиях NGINX Plus может проксировать и балансировать трафик протокола управления передачей (TCP). TCP — это протокол для многих популярных приложений и служб, таких как LDAP, MySQL и RTMP.

В NGINX Plus Release 9 и более поздних версиях NGINX Plus может проксировать и балансировать нагрузку UDP-трафика. UDP (протокол пользовательских дейтаграмм) — это протокол для многих популярных нетранзакционных приложений, таких как DNS, системный журнал и RADIUS.

Чтобы сбалансировать нагрузку HTTP-трафика, обратитесь к статье Балансировка нагрузки HTTP .

 

Предпосылки

  • Последний NGINX Plus (дополнительные этапы сборки не требуются) или последний NGINX с открытым исходным кодом , созданный с --with-streamфлагом конфигурации
  • Приложение, база данных или служба, которые взаимодействуют через TCP или UDP.
  • Серверы восходящего потока, на каждом из которых работает один и тот же экземпляр приложения, базы данных или службы.

 

Настройка обратного прокси

Во-первых, вам нужно настроить обратный прокси -сервер , чтобы NGINX Plus или NGINX с открытым исходным кодом могли пересылать TCP-соединения или дейтаграммы UDP от клиентов в вышестоящую группу или на прокси-сервер.

Откройте файл конфигурации NGINX и выполните следующие действия:

Создайте блок верхнего уровня stream {}:

stream {
    # ...
}

Определите один или несколько блоков конфигурации для каждого виртуального сервера в контексте server {}верхнего уровня .stream {}

В server {}блоке конфигурации для каждого сервера включите listenдирективу для определения IP-адреса и/или порта , который прослушивает сервер.

Для трафика UDP также включите этот udpпараметр. Поскольку TCP является протоколом по умолчанию для контекста, в директиве streamнет tcpпараметра :listen

stream {

    server {
        listen 12345;
        # ...
    }

    server {
        listen 53 udp;
        # ...
    }
    # ...
}

Включите proxy_passдирективу для определения прокси-сервера или вышестоящей группы, на которую сервер перенаправляет трафик:

stream {

    server {
        listen     12345;
        #TCP traffic will be forwarded to the "stream_backend" upstream group
        proxy_pass stream_backend;
    }

    server {
        listen     12346;
        #TCP traffic will be forwarded to the specified server
        proxy_pass backend.example.com:12346;
    }

    server {
        listen     53 udp;
        #UDP traffic will be forwarded to the "dns_servers" upstream group
        proxy_pass dns_servers;
    }
    # ...
}

Если прокси-сервер имеет несколько сетевых интерфейсов, вы можете дополнительно настроить NGINX на использование определенного исходного IP-адреса при подключении к вышестоящему серверу. Это может быть полезно, если прокси-сервер за NGINX настроен на прием соединений из определенных IP-сетей или диапазонов IP-адресов.

Включите proxy_bindдирективу и IP-адрес соответствующего сетевого интерфейса:

stream {
    # ...
    server {
        listen     127.0.0.1:12345;
        proxy_pass backend.example.com:12345;
        proxy_bind 127.0.0.1:12345;
    }
}

При желании вы можете настроить размер двух буферов в памяти, куда NGINX может помещать данные как от клиентских, так и от вышестоящих подключений. При небольшом объеме данных буферы можно уменьшить, что может сэкономить ресурсы памяти. Если имеется большой объем данных, размер буфера можно увеличить, чтобы уменьшить количество операций чтения/записи сокета. Как только данные получены по одному соединению, NGINX считывает их и пересылает по другому соединению. Буферы управляются proxy_buffer_sizeдирективой:

stream {
    # ...
    server {
        listen            127.0.0.1:12345;
        proxy_pass        backend.example.com:12345;
        proxy_buffer_size 16k;
    }
}

 

Настройка балансировки нагрузки TCP или UDP

Чтобы настроить балансировку нагрузки:

Создайте группу серверов или восходящую группу , трафик которой будет сбалансирован по нагрузке. Определите один или несколько upstream {}блоков конфигурации в контексте верхнего уровня stream {}и задайте имя для вышестоящей группы, например, stream_backendдля серверов TCP и dns_serversдля серверов UDP:

stream {

    upstream stream_backend {
        # ...
    }

    upstream dns_servers {
        # ...
    }

    # ...
}

Убедитесь, что на имя вышестоящей группы ссылается proxy_passдиректива, подобная настроенной выше для обратного прокси.

Заполните восходящую группу вышестоящими серверами . Внутри upstream {}блока добавьте serverдирективу для каждого вышестоящего сервера, указав его IP-адрес или имя хоста (которое может разрешаться в несколько IP-адресов) и обязательный номер порта. Обратите внимание, что вы не определяете протокол для каждого сервера, потому что он определяется для всей восходящей группы параметром, который вы включаете в listenдирективу в serverблоке, который вы создали ранее .

stream {

    upstream stream_backend {
        server backend1.example.com:12345;
        server backend2.example.com:12345;
        server backend3.example.com:12346;
        # ...
    }

    upstream dns_servers {
        server 192.168.136.130:53;
        server 192.168.136.131:53;
        # ...
    }

    # ...
}

Настройте метод балансировки нагрузки, используемый вышестоящей группой. Вы можете указать один из следующих методов:

Round Robin — по умолчанию NGINX использует алгоритм Round Robin для балансировки нагрузки трафика, последовательно направляя его на серверы в настроенной вышестоящей группе. Поскольку это метод по умолчанию, round?robinдирективы нет; просто создайте блок конфигурации в контексте upstream {}верхнего уровня и добавьте директивы, как описано в предыдущем шаге.stream {}server

Наименьшее количество подключений — NGINX выбирает сервер с наименьшим количеством текущих активных подключений.

Наименьшее время (только NGINX Plus) — NGINX Plus выбирает сервер с наименьшей средней задержкой и наименьшим количеством активных подключений. Метод, используемый для расчета наименьшей средней задержки, зависит от того, какой из следующих параметров включен в least_timeдирективу:

  • connect – Время подключения к вышестоящему серверу
  • first_byte– Время приема первого байта данных
  • last_byte – Время получения полного ответа от сервера

Хэш — NGINX выбирает сервер на основе заданного пользователем ключа, например, исходного IP-адреса ( $remote_addr):

upstream stream_backend {
    hash $remote_addr;
    server backend1.example.com:12345;
    server backend2.example.com:12345;
    server backend3.example.com:12346;
}

Метод Hashбалансировки нагрузки также используется для настройки сохраняемости сеанса . Поскольку хеш-функция основана на IP-адресе клиента, соединения от данного клиента всегда передаются на один и тот же сервер, если только сервер не отключен или недоступен по другой причине. Укажите необязательный consistentпараметр, чтобы применить метод последовательного хеширования ketama :

hash $remote_addr consistent;

Случайный — каждое соединение будет передаваться на случайно выбранный сервер. Если twoпараметр указан, то сначала NGINX случайным образом выбирает два сервера с учетом весов серверов, а затем выбирает один из этих серверов указанным методом:

  • least_conn– Наименьшее количество активных подключений
  • least_time=connect(NGINX Plus) — время подключения к вышестоящему серверу ( $upstream_connect_time)
  • least_time=first_byte(NGINX Plus) — наименьшее среднее время получения первого байта данных с сервера ( $upstream_first_byte_time)
  • least_time=last_byte(NGINX Plus) — наименьшее среднее время получения последнего байта данных с сервера ( $upstream_session_time)

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

upstream stream_backend {
    hash   $remote_addr consistent;
    server backend1.example.com:12345 weight=5;
    server backend2.example.com:12345;
    server backend3.example.com:12346 max_conns=3;
}
upstream dns_servers {
    least_conn;
    server 192.168.136.130:53;
    server 192.168.136.131:53;
    # ...
}

Альтернативный подход заключается в том, чтобы проксировать трафик на один сервер, а не на вышестоящую группу. Если вы идентифицируете сервер по имени хоста и настроите имя хоста для разрешения нескольких IP-адресов, то NGINX распределяет трафик по IP-адресам с помощью Round Robinалгоритма. В этом случае вы должны указать номер порта сервера в proxy_passдирективе и не должны указывать протокол перед IP-адресом или именем хоста:

stream {
    # ...
    server {
        listen     12345;
        proxy_pass backend.example.com:12345;
    }
}

 

Настройка проверок работоспособности

NGINX может постоянно тестировать вышестоящие TCP- или UDP-серверы, избегать отказавших серверов и корректно добавлять восстановленные серверы в группу с балансировкой нагрузки.

Инструкции по настройке проверок работоспособности для TCP см. в разделе Проверки работоспособности TCP.

Инструкции по настройке проверок работоспособности для UDP см. в разделе Проверки работоспособности UDP.

 

Конфигурация «на лету»

Группы вышестоящих серверов можно легко перенастроить «на лету» с помощью NGINX Plus REST API. Используя этот интерфейс, вы можете просматривать все серверы в вышестоящей группе или отдельном сервере, изменять параметры сервера, а также добавлять или удалять вышестоящие серверы.

Чтобы включить настройку «на лету»:

Создайте блок верхнего уровня http {}или убедитесь, что он присутствует в вашей конфигурации:

http {
    # ...
}

Создайте место для запросов конфигурации, например, api :

http {
    server {
        location /api {
            # ...
        }
    }
}

В этом месте укажите apiдирективу:

http {
    server {
        location /api {
            api;
            # ...
        }
    }
}

По умолчанию API NGINX Plus предоставляет доступ к данным только для чтения. Параметр write=onразрешает доступ для чтения/записи, чтобы можно было вносить изменения в восходящие потоки:

http {
    server {
        location /api {
            api  write=on;
            # ...
        }
    }
}

Ограничьте доступ к этому местоположению с помощью директив allowи :deny

http {
    server {
        location /api {
            api   write=on;
            allow 127.0.0.1; # permit access from localhost
            deny  all;       # deny access from everywhere else
        }
    }
}

Когда API включен в режиме записи, рекомендуется ограничить доступ к PATCH, POSTи DELETEметодам для определенных пользователей. Это можно сделать, внедрив базовую аутентификацию HTTP :

http {
    server {
        location /api {
            limit_except GET {
                auth_basic "NGINX Plus API";
                auth_basic_user_file /path/to/passwd/file;
            }
            api   write=on;
            allow 127.0.0.1;
            deny  all;
        }
    }
}

Создайте зону общей памяти для группы вышестоящих серверов, чтобы все рабочие процессы могли использовать одну и ту же конфигурацию. Для этого в stream {}блоке верхнего уровня находим целевую группу upsteam, добавляем zoneдирективу в группу upstream server и указываем имя зоны (здесь stream_backend ) и объем памяти (64 КБ):

stream {
    upstream stream_backend {
        zone backend 64k;
        # ...
    }
}

 

Пример конфигурации «на лету»

stream {
    # ...
    # Configuration of an upstream server group
    upstream appservers {
        zone appservers 64k;
        server appserv1.example.com:12345 weight=5;
        server appserv2.example.com:12345 fail_timeout=5s;
        server backup1.example.com:12345 backup;
        server backup2.example.com:12345 backup;
    }
    
    server {
        # Server that proxies connections to the upstream group
        proxy_pass appservers;
        health_check;
    }
}
http {
    # ...
    server {
        # Location for API requests
        location /api {
            limit_except GET {
                auth_basic "NGINX Plus API";
                auth_basic_user_file /path/to/passwd/file;
            }
            api write=on;
            allow 127.0.0.1;
            deny  all;
        }
    }
}

Здесь доступ к местоположению разрешен только с адреса локального хоста ( 127.0.0.1). Доступ со всех других IP-адресов запрещен.

Чтобы передать конфигурационную команду в NGINX, отправьте API-команду любым способом, например, с помощью curl.

Например, чтобы добавить новый сервер в группу серверов, отправьте POSTзапрос:

curl -X POST -d '{ \ 
   "server": "appserv3.example.com:12345", \ 
   "weight": 4 \ 
 }' -s 'http://127.0.0.1/api/6/stream/upstreams/appservers/servers'

Чтобы удалить сервер из группы серверов, отправьте DELETEзапрос:

curl -X DELETE -s 'http://127.0.0.1/api/6/stream/upstreams/appservers/servers/0'

Чтобы изменить параметр для конкретного сервера, отправьте PATCHзапрос:

curl -X PATCH -d '{ "down": true }' -s 'http://127.0.0.1/api/6/http/upstreams/appservers/servers/0'

 

Пример конфигурации балансировки нагрузки TCP и UDP

Это пример конфигурации балансировки нагрузки TCP и UDP с NGINX:

stream {
    upstream stream_backend {
        least_conn;
        server backend1.example.com:12345 weight=5;
        server backend2.example.com:12345 max_fails=2 fail_timeout=30s;
        server backend3.example.com:12345 max_conns=3;
    }
    
    upstream dns_servers {
        least_conn;
        server 192.168.136.130:53;
        server 192.168.136.131:53;
        server 192.168.136.132:53;
    }
    
    server {
        listen        12345;
        proxy_pass    stream_backend;
        proxy_timeout 3s;
        proxy_connect_timeout 1s;
    }
    
    server {
        listen     53 udp;
        proxy_pass dns_servers;
    }
    
    server {
        listen     12346;
        proxy_pass backend4.example.com:12346;
    }
}

В этом примере вся функциональность, связанная с прокси-сервером TCP и UDP, настраивается внутри streamблока точно так же, как в блоке настраиваются параметры HTTP-запросов http.

Есть два именованных upstreamблока, каждый из которых содержит три сервера, на которых размещается одинаковый контент. Для serverкаждого сервера за именем сервера следует обязательный номер порта. Подключения распределяются между серверами по методу балансировки нагрузки Least Connections : подключение идет к серверу с наименьшим количеством активных подключений.

Три serverблока определяют три виртуальных сервера:

Первый сервер прослушивает порт 12345 и проксирует все TCP-подключения к группе stream_backend вышестоящих серверов. Обратите внимание, что proxy_passдиректива, определенная в контексте streamмодуля, не должна содержать протокол.

Задаются два необязательных параметра таймаута: proxy_connect_timeoutдиректива задает таймаут, необходимый для установления соединения с сервером в группе stream_backend . Директива proxy_timeoutустанавливает тайм-аут, используемый после запуска проксирования на один из серверов в группе stream_backend .

Второй сервер прослушивает порт 53 и передает все дейтаграммы UDP ( udpпараметр listenдирективы) вышестоящей группе с именем dns_servers . Если udpпараметр не указан, сокет прослушивает соединения TCP.

Третий виртуальный сервер прослушивает порт 12346 и проксирует TCP-подключения к backend4.example.com , которые могут разрешаться в несколько IP-адресов с балансировкой нагрузки с помощью метода Round Robin.