Прокси-сервер NGINX#

Обратным прокси-сервером называется веб-сервер, который является оконечной точкой соединения с клиентом и открывает новое соединение с проксируемым сервером от имени клиента.

Проксируемый сервер определяется как сервер, с которым NGINX устанавливает соединение для выполнения запроса клиента. Проксируемые серверы могут принимать разные формы, и для каждой NGINX можно настроить по-разному.

В силу самой природы обратного прокси-сервера проксируемый сервер не получает информацию непосредственно от клиента. Часть этой информации, например истинный IP-адрес клиента, важна для отладки и для отслеживания. Ее можно передать проксируемому серверу в виде заголовков.

HTTP-сервер – это программа, основная задача которой состоит в доставке веб-страниц клиентам в ответ на их запросы. Происхождение веб-страницы может быть каким угодно – от простого HTML-файла на диске до многокомпонентного каркаса, генерирующего зависящее от пользователя содержимое, которое динамически обновляется с помощью AJAX или WebSocket. NGINX – модульная программа, способная обслуживать HTTP-запросы любым необходимым способом.

NGINX состоит из одного главного и нескольких рабочих процессов. Все они однопоточные и способны одновременно обслуживать тысячи соединений. Большая часть работы производится рабочим процессом, поскольку именно он обрабатывает запросы клиентов. Чтобы как можно быстрее реагировать на запросы, NGINX использует встроенный в операционную систему механизм событий

Запрос пользователя можно проанализировать, исходя из URI-адреса, параметров клиента или др., чтобы лучше обработать его. Любая часть исходного URL-запроса при прохождении через обратный прокси-сервер может быть преобразована.

С точки зрения проксирования, наиболее важна директива proxy_pass . Она принимает один параметр – URI-адрес, на который следует передать запрос. Использование proxy_pass с указанием URI заменяет request_uri этой частью.

Так, в следующем примере /uri будет заменено на /newuri при передаче запроса проксируемому серверу:

location /uri {
        proxy_pass http://localhost:8080/newuri;
}

Модуль proxy#

В следующей таблице перечислены некоторые наиболее употребительные директивы модуля proxy .

  • proxy_connect_timeout - Максимальное время ожидания соединения с проксируемым сервером

  • proxy_pass - Задает URL проксируемого сервера, которому передается запрос

  • proxy_pass_header - Отменяет сокрытие заголовков, определенных в директиве proxy_hide_header, разрешая передавать их клиенту

  • proxy_redirect - Перезаписывает заголовки Location и Refresh, полученные от проксируемого сервера; полезно для обхода допущений, принятых каркасом разработки приложений

  • proxy_set_header - Перезаписывает заголовки, отправляемые проксируемому серверу. Может также применяться для подавления некоторых заголовков (если в качестве значения указать

пустую строку)

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

proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

В директиве proxy_redirect задано значение off , потому что переписывать заголовок Location в большинстве случаев не нужно.

Заголовок Host устанавливается так, чтобы проксируемый сервер мог сопоставить запрос с виртуальным сервером или как-то иначе использовать часть переданного клиентом URL-адреса, содержащую имя хоста.

Заголовки X-Real-IP и X-Forwarded-For служат аналогичным целям – передать проксируемому серверу информацию об IP-адресе подключившегося клиента. * Переменная $remote_addr , подставляемая в заголовок X-Real-IP, содержит IP-адрес клиента, определенный NGINX. * Переменная $proxy_add_x_forwarded_for содержит значение заголовка X-Forwarded-For из запроса клиента, за которым следует значение переменной $remote_addr .

Модуль upstream#

Директива upstream начинает новый контекст, в котором определяется группа проксируемых серверов. Этим серверам можно приписать разные веса (чем выше вес, тем больше соединений будет передано конкретному проксируемому серверу), они могут иметь разные типы (TCP или в домене UNIX). Их даже можно пометить как остановленные на техническое обслуживание.

Директивы, допустимые в контексте upstream .

  • ip_hash - Обеспечивает равномерное распределение клиентских соединений по всем серверам за счет хэширования IP-адреса по его сети класса C

  • keepalive - Количество соединений с проксируемыми серверами, кэшируемых в одном рабочем процессе. При использовании с HTTP-соединениями значение proxy_http_version должно быть равно 1.1, а значение proxy_set_header – Connection “

  • least_conn - Активирует алгоритм балансировки нагрузки, согласно которому очередной запрос передается серверу с наименьшим числом активных соединений

  • server - Определяет адрес (доменное имя, или IP-адрес с необязательным номером TCP-порта, или путь к сокету в домене UNIX) и необязательные параметры проксируемого сервера, а именно:

    • weight: относительный вес сервера;

    • max_fails: максимальное число неудачных попыток установления связи с сервером в течение времени fail_timeout, после которого сервер помечается как неработоспособный;

    • fail_timeout: время, в течение которого сервер должен ответить на запрос, и время, в течение которого сервер считается неработоспособным;

    • backup: такой сервер получает запрос, только если все остальные неработоспособны;

    • down: помечает сервер как непригодный для обработки запросов

Алгоритмы балансировки нагрузки#

Для выбора проксируемого сервера, которому передается очередной запрос, модуль upstream может применять один из трех алгоритмов балансировки нагрузки: циклический, по хэш-коду IP-адреса или с наименьшим количеством соединений. По умолчанию подразумевается циклический (round-robin) алгоритм , для его активации никакой директивы не нужно. В этом случае выбирается сервер, следующий за тем, который был выбран для обслуживания предыдущего запроса, – с учетом следования серверов в конфигурационном блоке и их весов. Циклический алгоритм пытается обеспечить справедливое распределение трафика, основываясь на понятии очередности.

Алгоритм хэширования IP-адреса , активируемый директивой ip_hash , основан на предположении, что запросы от клиентов с некоторыми IP-адресами должны попадать одному и тому же проксируемому серверу. В качестве ключа хэширования NGINX берет первые три октета IPv4-адреса или весь IPv6-адрес. Таким образом, множеству близких IP-адресов всегда сопоставляется один и тот же проксируемый сервер. Цель этого механизма – обеспечить не справедливое распределение, а постоянство связи между клиентом и обслуживающим его сервером.

Алгоритм балансировки нагрузки, с наименьшим количеством соединений , выбирается директивой least_conn . Он ставит целью равномерное распределение нагрузки между проксируемыми серверами путем выбора того, у которого количество активных соединений наименьшее. Различия в вычислительной мощности проксируемых серверов можно учесть с помощью параметра weight директивы server. При выборе сервера с наименьшим количеством соединений алгоритм принимает во внимание вес.

Типы проксируемых серверов#

Проксируемым называется сервер, которому NGINX передает запрос на соединение. Он может находиться на другой физической или виртуальной машине, но это необязательно. Проксируемый сервер может быть демоном, прослушивающим сокет в домене UNIX на локальной машине, или одним из многих демонов, прослушивающих порты TCP на другой машине. Это может быть сервер Apache с модулями для обработки запросов различных типов или сервер промежуточного уровня Rack, предоставляющий HTTP-интерфейс к приложениям, написанным на Ruby. В любом случае NGINX можно настроить как прокси-сервер.

Единственный проксируемый сервер#

Веб-сервер Apache часто применяется для обслуживания как статических файлов, так и интерпретируемых скриптов разных типов.

server {
        location / {
                proxy_pass http://localhost:8080;
        }
}

Это самая простая из всех возможных конфигураций прокси-сервера. NGINX служит оконечной точкой для всех клиентских соединений и проксирует запросы на порт 8080 на локальном компьютере. Предполагается, что Apache настроен на прослушивание порта localhost:8080 .

Следующая конфигурация позволяет NGINX самостоятельно обслуживать запросы на статические файлы, а остальные передавать Apache:

server {
        location / {
                try_files $uri @apache;
        }

        location @apache {
                proxy_pass http://127.0.0.1:8080;
        }
}

Директива try_files по очереди проверяет файлы, пока не найдет совпадение. Так, в показанном выше примере NGINX сам доставит файлы, которые соответствуют URI в запросе клиента и находятся в его корневом каталоге. Если файл не найден, то NGINX передаст запрос Apache для дальнейшей обработки.

Несколько проксируемых серверов#

В NGINX можно передавать запросы нескольким проксируемым серверам. Для этого следует объявить контекст upstream, определить в нем несколько серверов и сослаться на этот контекст в директиве proxy_pass:

upstream app{
        server 127.0.0.1:9000;

        server 127.0.0.1:9001;

        server 127.0.0.1:9002;
}

server {
        location / {
                proxy_pass http://app;
        }
}

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

asda

Если некоторый клиент должен всегда попадать на один и тот же проксируемый сервер, то есть требуется обеспечить некое слабое подобие липких сеансов, то следует воспользоваться директивой ip_hash . В случае когда распределение запросов характеризуется широким разбросом времени обработки одного запроса, то лучше выбрать алгоритм **least_conn*. Подразумеваемый по умолчанию циклический алгоритм хорош для общего случая, когда не требуется учитывать особенности клиента или проксируемого сервера.