Прокси-сервер 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 взаимодействие с клиентами, чтобы ни один из серверов приложений не был перегружен. Подобная конфигурация изображена на следующем рисунке:
Если некоторый клиент должен всегда попадать на один и тот же проксируемый сервер, то есть требуется обеспечить некое слабое подобие липких сеансов, то следует воспользоваться директивой ip_hash . В случае когда распределение запросов характеризуется широким разбросом времени обработки одного запроса, то лучше выбрать алгоритм **least_conn*. Подразумеваемый по умолчанию циклический алгоритм хорош для общего случая, когда не требуется учитывать особенности клиента или проксируемого сервера.