Proxy pass что означает
Перейти к содержимому

Proxy pass что означает

  • автор:

Как избежать 10 частых ошибок в настройке NGINX

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

  1. Недостаточное количество файловых дескрипторов;
  2. Директива error_log off;
  3. Отсутствие keepalive-соединения с вышестоящими серверами;
  4. Упущение механизмов наследования директив;
  5. Директива proxy_buffering;
  6. Неправильное использование директивы if;
  7. Чрезмерные проверки работоспособности;
  8. Незащищенный доступ к метрикам;
  9. Использование ip_hash, когда весь трафик поступает из одного и того же блока /24 CIDR;
  10. Игнорирование преимуществ вышестоящих групп.

Ошибка №1: Недостаточное количество файловых дескрипторов на воркера

Директива worker_connections задает максимальное количество одновременных подключений, которое может быть открыто для рабочего процесса NGINX (значение по умолчанию 512). Здесь учитываются не только клиентские, но и все типы подключений (например, соединения с прокси-серверами). Важно помнить, что существует еще одно ограничение на количество одновременных подключений, которые могут быть открыты воркером: ограничение операционной системы на максимальное количество файловых дескрипторов (FDS), выделенных для каждого процесса. В современных дистрибутивах UNIX ограничение по умолчанию равно 1024.

Для всех развертываний NGINX, кроме самых маленьких, ограничение в 512 подключений на воркера может быть недостаточным. На самом же деле, дефолтный файл nginx.conf, который мы распространяем с двоичными файлами NGINX с открытым исходным кодом и NGINX Plus, увеличивает это значение до 1024.

Распространенной ошибкой при конфигурации является неувеличение количества FDs как минимум вдвое по сравнению со значением worker_connections. Исправить ситуацию можно, установив это значение с помощью директивы worker_rlimit_nofile в основном контексте конфигурации.

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

NGINX также использует FD на каждый файл журнала и пару FD для связи с мастер-процессом, но обычно эти цифры невелики по сравнению с количеством файловых дескрипторов, используемых для подключений и файлов.

UNIX предлагает несколько способов задать необходимое количество FDs для каждого процесса:

  • Команда ulimit, если вы запускаете NGINX из shell;
  • Переменные манифеста init script или systemd если вы запускаете NGINX как сервис;
  • Файл /etc/security/limits.conf.

Стоит отметить, что данный метод зависит от того, как вы запускаете NGINX, тогда как worker_rlimit_nofile работает независимо от этого.

Плюс ко всему, с помощью команды операционной системы sysctl fs.file-max вы можете установить общесистемное ограничение на количество FDs. Общего объема должно хватить, но стоит проверить, что максимальное количество файловых дескрипторов, задействованных в рабочих процессах NGINX (*worker_rlimit_nofile worker_connections), значительно ниже, чем fs.file‑max**. В случае если вдруг NGINX станет использовать все доступные ресурсы (например, во время DoS-атаки), даже возможность выполнить вход в систему для устранения проблем станет невозможна.

Ошибка №2: Директива error_log off

Распространенная ошибка заключается в убежденности, что директива error_log off отключает ведение журнала. Фактически, в отличие от директивы access_log, error_log не включает режим off. Если вы включаете директиву error_log off в конфигурацию, NGINX создает файл лога ошибок с именем off в каталоге по умолчанию для файлов конфигурации NGINX (обычно это /etc/nginx).

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

error_log /dev/null emerg;

Обратите внимание, что эта директива работает только после проверки данной конфигурации со стороны NGINX. Таким образом, каждый раз при запуске NGINX или перезагрузке конфигурации, она будет по умолчанию зарегистрирована в журнале ошибок (обычно это /var/log /nginx/error.log) до тех пор, пока конфигурация не будет проверена. Чтобы изменить каталог журнала, включите параметр -e в команде nginx.

Ошибка №3: Отсутствие keepalive-соединения с вышестоящими серверами

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

При больших объемах трафика открытие нового соединения для каждого запроса может значительно истощить системные ресурсы и сделать невозможным открытие соединений. Причина заключается в следующем: для каждого соединения кортеж из адреса источника, порта источника, адреса назначения и порта назначения должен быть уникальным. Для подключений из NGINX к вышестоящему серверу три элемента (первый, третий и четвертый) являются фиксированными, оставляя в качестве переменной только порт источника. Когда соединение закрывается, сокет Linux находится в состоянии TIME-WAIT в течение двух минут, что при больших объемах трафика увеличивает вероятность исчерпания пула доступных исходных портов. Если это произойдет, NGINX не сможет открывать новые подключения к вышестоящим серверам.

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

Чтобы включить keepalive-соединения:

  • Включите директиву keepalive в каждый блок upstream<>, чтобы задать количество ожидающих соединений keepalive с вышестоящими серверами, сохраненных в кэше каждого рабочего процесса.

Обратите внимание, что директива keepalive не ограничивает общее количество подключений к вышестоящим серверам, которые может открыть рабочий процесс NGINX – это распространенное заблуждение. Таким образом параметр keepalive не обязательно должен быть большим, как может показаться на первый взгляд.

Мы рекомендуем установить значение, в два раза превышающее количество серверов, перечисленных в блоке upstream<>. Этого хватит для того, чтобы NGINX поддерживал непрерывные соединения со всеми серверами и вышестоящие серверы могли обрабатывать новые входящие соединения.

Также обратите внимание, что когда вы указываете алгоритм балансировки нагрузки в блоке upstream<> ‑ с помощью директивы hash, ip_hash, least_conn, least_time или random – директива должна отображаться над директивой keepalive. Это одно из редких исключений из общего правила, согласно которому порядок директив в конфигурации NGINX не имеет значения.

  • В блоке location<>, который перенаправляет запросы в вышестоящую группу, включите следующие директивы вместе с proxy_pass:
proxy_http_version 1.1; proxy_set_header "Connection" "";

По умолчанию NGINX использует HTTP/1.0 для подключений к вышестоящим серверам и соответственно добавляет заголовок Connection: close к запросам, которые он пересылает на серверы. В результате, каждое соединение закрывается по завершении запроса, несмотря на наличие директивы keepalive в блоке upstream<>.

Директива proxy_http_version говорит NGINX использовать HTTP/1.1 вместо дефолтного параметра, а директива proxy_set_header удаляет значение close из заголовка Connection.

Ошибка №4: Упущение механизмов наследования директив

Директивы NGINX наследуются с предыдущего уровня конфигурации, или “сверху-вниз”: дочерний контекст, будучи вложенным в другой контекст (родительский), наследует настройки директив, входящих в родительский уровень. Например, все блоки server<> и location<> в контексте http<> наследуют значение директив, входящих в уровень http, а директива в блоке server<> наследуется всеми дочерними блоками location<>. Однако, когда одна и та же директива включена как в родительский контекст, так и в его дочерний контекст, значения не суммируются – вместо этого значение в дочернем контексте перекрывает родительское значение.

Ошибочным будет забыть о “правиле перекрытия” для директив массива, которые могут быть включены не только в нескольких контекстах, но и несколько раз в пределах данного контекста. Данные примеры включают proxy_set_header и add_header – наличие “add” в имени второго позволяет особенно легко забыть о правиле перекрытия.

Рассмотрим, как работает наследование на примере add_header:

http < add_header X-HTTP-LEVEL-HEADER 1; add_header X-ANOTHER-HTTP-LEVEL-HEADER 1; server < listen 8080; location / < return 200 "OK"; >> server < listen 8081; add_header X-SERVER-LEVEL-HEADER 1; location / < return 200 "OK"; >location /test < add_header X-LOCATION-LEVEL-HEADER 1; return 200 "OK"; >location /correct < add_header X-HTTP-LEVEL-HEADER 1; add_header X-ANOTHER-HTTP-LEVEL-HEADER 1; add_header X-SERVER-LEVEL-HEADER 1; add_header X-LOCATION-LEVEL-HEADER 1; return 200 "OK"; >> >

Для сервера, прослушивающего порт 8080, нет директив add_header ни в блоках server<>, ни в location<>. Таким образом, наследование происходит напрямую, и мы видим два заголовка в контексте http<>:

% curl -is localhost:8080 HTTP/1.1 200 OK Server: nginx/1.21.5 Date: Mon, 21 Feb 2022 10:12:15 GMT Content-Type: text/plain Content-Length: 2 Connection: keep-alive X-HTTP-LEVEL-HEADER: 1 X-ANOTHER-HTTP-LEVEL-HEADER: 1 OK

Для сервера, прослушивающего порт 8081, существует директива add_header в блоке server<>, но не в его дочернем location/ блоке. Заголовок, определенный в блоке server<>, переопределяет два заголовка, определенных в контексте http<>:

% curl -is localhost:8081 HTTP/1.1 200 OK Server: nginx/1.21.5 Date: Mon, 21 Feb 2022 10:12:20 GMT Content-Type: text/plain Content-Length: 2 Connection: keep-alive X-SERVER-LEVEL-HEADER: 1 OK

В дочернем блоке location /test есть директива add_header, и она перекрывает как заголовок из родительского блока server<>, так и два заголовка из контекста http>:

% curl -is localhost:8081/test HTTP/1.1 200 OK Server: nginx/1.21.5 Date: Mon, 21 Feb 2022 10:12:25 GMT Content-Type: text/plain Content-Length: 2 Connection: keep-alive X-LOCATION-LEVEL-HEADER: 1 OK

Если мы хотим, чтобы блок location<> сохранял заголовки, определенные в его родительских контекстах, вместе с любыми заголовками, определенными локально, мы должны переопределить родительские заголовки в блоке location<>. Это как раз то, что мы сделали в блоке location /correct:

% curl -is localhost:8081/correct HTTP/1.1 200 OK Server: nginx/1.21.5 Date: Mon, 21 Feb 2022 10:12:30 GMT Content-Type: text/plain Content-Length: 2 Connection: keep-alive X-HTTP-LEVEL-HEADER: 1 X-ANOTHER-HTTP-LEVEL-HEADER: 1 X-SERVER-LEVEL-HEADER: 1 X-LOCATION-LEVEL-HEADER: 1 OK

Ошибка №5: Директива proxy_buffering off

Буферизация прокси-сервера включена по умолчанию в NGINX (proxy_buffering в режиме on). Буферизация прокси-сервера означает, что NGINX сохраняет ответ с сервера во внутренних буферах по мере его поступления и не начинает отправлять данные клиенту до тех пор, пока весь ответ не будет буферизован. Буферизация помогает оптимизировать производительность при работе с медленными клиентами – поскольку NGINX буферизует свой ответ столько времени, сколько требуется клиенту для его получения, прокси-сервер может получить свой ответ быстро и вернуться к работе с другими запросами.

Когда буферизация прокси отключена, NGINX буферизует только первую часть ответа сервера, прежде чем начать отправлять его клиенту, в буфере, размер которого по умолчанию составляет одну страницу памяти (4 КБ или 8 КБ в зависимости от операционной системы). Обычно этого места вполне достаточно для заголовка ответа. Далее NGINX тут же отправляет ответ клиенту по мере его получения, оставляя сервер бездействовать пока NGINX сможет принять следующий сегмент ответа.

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

Существует лишь небольшое количество вариантов использования, в которых отключение буферизации прокси-сервера может иметь смысл (например, “длинный запрос”), поэтому мы настоятельно не рекомендуем изменять значение по умолчанию. Дополнительные сведения см. в Руководстве администратора NGINX Plus.

Ошибка №6: Неправильное использование директивы if

Директиву if сложно использовать, особенно в блоках location<>. Она не всегда функционирует, как вы ожидаете, и даже может привести к сбоям в сегментах. На самом деле, это настолько сложный вопрос, что в NGINX Wiki есть статья под названием If is Evil — почитайте, чтобы подробно изучить и разобраться как избежать данной проблемы.

В общем говоря, единственными директивами, которые вы всегда можете безопасно использовать в блоке if<>, являются return и rewrite. В следующем примере if используется для обнаружения запросов, которые включают заголовок X‑Test (это может быть любое условие, которое вы хотите проверить). NGINX возвращает ошибку 430 («Поля заголовка запроса слишком велики»), перехватывает ее в именованном локейшне @error_430 и отправляет запрос в группу upstream b.

location / < error_page 430 = @error_430; if ($http_x_test) < return 430; >proxy_pass http://a; > location @error_430

Для этого и многих других применений if часто можно полностью избежать директивы. В следующем примере, запрос включает заголовок X‑Test, блок map<> присваивает переменной $upstream_name значение b, и запрос перенаправляется в вышестоящую группу с этим именем.

map $http_x_test $upstream_name < default "b"; "" "a"; ># . location / < proxy_pass http://$upstream_name; >

Ошибка №7: Чрезмерные проверки работоспособности

Довольно часто несколько виртуальных серверов настраиваются на прокси-запросы к одной и той же вышестоящей группе (другими словами, для включения идентичной директивы proxy_pass в несколько блоков server<>). Ошибка данной ситуации заключается в том, чтобы включать директиву health_check в каждый блок server<>. Это лишь создает дополнительную нагрузку на вышестоящие серверы, не предоставляя никакой дополнительной информации.

Исправление ситуации довольно очевидно и состоит в том, чтобы оставить только одну проверку работоспособности для каждого блока upstream<>. Здесь мы определяем проверку работоспособности для вышестоящей группы под именем b в именованном локейшене, вместе с соответствующими тайм-аутами и настройками заголовка.

location / < proxy_set_header Host $host; proxy_set_header "Connection" ""; proxy_http_version 1.1; proxy_pass http://b; >location @health_check < health_check; proxy_connect_timeout 2s; proxy_read_timeout 3s; proxy_set_header Host example.com; proxy_pass http://b; >

В сложных конфигурациях это поможет еще больше упростить управление, сгруппировав все на одном виртуальном сервере вместе с API NGINX Plus и панелью мониторинга, как в этом примере.

server < listen 8080; location / < # … >location @health_check_b < health_check; proxy_connect_timeout 2s; proxy_read_timeout 3s; proxy_set_header Host example.com; proxy_pass http://b; >location @health_check_c < health_check; proxy_connect_timeout 2s; proxy_read_timeout 3s; proxy_set_header Host api.example.com; proxy_pass http://c; >location /api < api write=on; # directives limiting access to the API (see 'Mistake 8' below) >location = /dashboard.html < root /usr/share/nginx/html; >>

Для дополнительной информации о проверке работоспособности серверов HTTP, TCP, UDP и gRPC см. в Руководстве администратора NGINX Plus.

Ошибка №8: Незащищенный доступ к метрикам

Основные показатели работы NGINX доступны в модуле Stub Status. Для NGINX Plus вы также можете собрать гораздо более обширный набор показателей с помощью API NGINX Plus. Включите сбор метрик с помощью директивы stub_status или api в блок server<> или location<>, который станет URL, обеспечивающим доступ к метрикам. (Для API NGINX Plus будет необходимо настроить зоны общей памяти для объектов NGINX – виртуальных серверов, вышестоящих групп, кэшей и т. д. – данные которых вам необходимо учитывать; см. инструкции в Руководстве администратора NGINX Plus.)

Некоторые из показателей представляют собой конфиденциальную информацию, которая может быть использована для атаки на ваш веб-сайт или приложение, на которое происходит проксирование через NGINX, и ошибка, которую мы иногда видим в пользовательских конфигурациях, заключается в невозможности ограничить доступ к соответствующему URL-адресу. Рассмотрим некоторые из способов защиты метрик. В первых примерах мы будем использовать stub_status.

Благодаря нижеизложенной конфигурации, метрики станут доступны любому интернет пользователю (используйте http://example.com/basic_status).

server < listen 80; server_name example.com; location = /basic_status < stub_status; >>
Защитите данные с помощью базовой HTTP-аутентификации

Чтобы защитить метрики паролем с помощью базовой аутентификации HTTP, включите директивы auth_basic и auth_basic_user_file. В файле (в данном случае .htpasswd) перечислены имена пользователей и пароли клиентов, которые могут войти в систему, чтобы просмотреть показатели:

server < listen 80; server_name example.com; location = /basic_status < auth_basic “closed site”; auth_basic_user_file conf.d/.htpasswd; stub_status; >>
Защитите метрики с помощью директив allow и deny

Если вы не хотите, чтобы пользователям приходилось вводить имя и пароль, и вы знаете IP-адреса, с которых они будут получать доступ к метрикам, другим вариантом является директива allow. Вы можете указать отдельные адреса IPv4 и IPv6, а также диапазоны CIDR. Директива **deny all** запрещает доступ с любых других адресов.

server < listen 80; server_name example.com; location = /basic_status < allow 192.168.1.0/24; allow 10.1.1.0/16; allow 2001:0db8::/32; allow 96.1.2.23/32; deny all; stub_status; >>
Объедините оба метода

А что если понадобится объединить оба метода? Мы можем разрешить клиентам получать доступ к метрикам с определенных адресов без пароля и по-прежнему требовать входа для клиентов, поступающих с остальных адресов. Для этого мы используем директиву satisfy any. Она позволяет NGINX разрешить доступ клиентам, которые либо входят в систему с помощью учетных данных базовой HTTP-аутентификации, либо используют предварительно утвержденный IP-адрес. Для дополнительной безопасности вы можете установить значение satisfy to all, чтобы требовать входа в систему от людей, которые приходят с конкретных адресов.

server < listen 80; server_name monitor.example.com; location = /basic_status < satisfy any; auth_basic “closed site”; auth_basic_user_file conf.d/.htpasswd; allow 192.168.1.0/24; allow 10.1.1.0/16; allow 2001:0db8::/32; allow 96.1.2.23/32; deny all; stub_status; >>

С NGINX Plus вы используете те же методы для ограничения доступа в конечной точке API NGINX Plus (http://monitor.example.com:8080/api/ в следующем примере), так же, как и панель мониторинга активности в реальном времени на http://monitor.example.com/dashboard.html.

Эта конфигурация разрешает доступ без пароля только клиентам, поступающим из сети 96.1.2.23/32 или локального хоста. Поскольку директивы определены на уровне server<>, одни и те же ограничения применяются как к API, так и к панели мониторинга. В качестве дополнительного примечания параметр write=on для api означает, что эти клиенты могут использовать API для внесения изменений в конфигурацию.

Дополнительные сведения о настройке API и панели мониторинга см. в Руководстве администратора NGINX Plus.

server < listen 8080; server_name monitor.example.com; satisfy any; auth_basic “closed site”; auth_basic_user_file conf.d/.htpasswd; allow 127.0.0.1/32; allow 96.1.2.23/32; deny all; location = /api/ < api write=on; >location = /dashboard.html < root /usr/share/nginx/html; >>

Ошибка №9: Использование ip_hash, когда весь трафик поступает из одного и того же блока /24 CIDR

Алгоритм ip_hash балансирует нагрузку на трафик между серверами в блоке upstream<> на основе хэша IP-адреса клиента. Ключ хеширования — это первые три октета адреса IPv4 или полностью адрес IPv6. Метод обеспечивает сохранение сеанса, что означает, что запросы от клиента всегда передаются на один и тот же сервер, за исключением случаев, когда сервер недоступен.

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

http < upstream < ip_hash; server 10.10.20.105:8080; server 10.10.20.106:8080; server 10.10.20.108:8080; >server >

Важно отметить, что здесь есть одна загвоздка: все “перехватывающие” устройства находятся в одной и той же сети 10.10.0.0/ 24, поэтому для NGINX это выглядит так, что весь трафик поступает с адресов в этом диапазоне CIDR. Помните, что алгоритм ip_hash хэширует первые три октета адреса IPv4. В нашем развертывании первые три октета одинаковы – 10.10.0 – для каждого клиента, поэтому хэш одинаков для всех них, и нет никаких оснований для распределения трафика на разные серверы.

Исправить ситуацию можно, воспользовавшись алгоритмом хэширования с переменной $binary_remote_addr в качестве хэш-ключа. Эта переменная фиксирует полный адрес клиента, конвертируя его в двоичное представление, которое составляет 4 байта для адреса IPv4 и 16 байт для адреса IPv6. Теперь хэш отличается для каждого перехватывающего устройства, и балансировка нагрузки работает должным образом.

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

http < upstream < hash $binary_remote_addr consistent; server 10.10.20.105:8080; server 10.10.20.106:8080; server 10.10.20.108:8080; >server >

Ошибка №10: Игнорирование преимущества вышестоящих групп

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

http < server < listen 80; server_name example.com; location / < proxy_set_header Host $host; proxy_pass http://localhost:3000/; >> >

Все довольно просто, не так ли? Директива proxy_pass указывает NGINX куда отправлять запросы от клиентов. Все, что нужно сделать NGINX, это преобразовать имя хоста в адрес IPv4 или IPv6. Как только соединение установлено, NGINX пересылает запросы на этот сервер.

Ошибка здесь может заключается в предположении, что, поскольку существует только один сервер – и, следовательно, нет причин настраивать балансировку нагрузки, – бессмысленно создавать блок upstream<>. На самом же деле, блок upstream<> открывает несколько функций по повышению производительности, отраженных в этой конфигурации:

http < upstream node_backend < zone upstreams 64K; server 127.0.0.1:3000 max_fails=1 fail_timeout=2s; keepalive 2; >server < listen 80; server_name example.com; location / < proxy_set_header Host $host; proxy_pass http://node_backend/; proxy_next_upstream error timeout http_500; >> >

Директива zone устанавливает зону общей памяти, в которой все рабочие процессы NGINX на хосте могут получать доступ к информации о конфигурации и состоянии вышестоящих серверов. Несколько вышестоящих групп могут совместно использовать эту зону. Благодаря NGINX Plus эта зона также позволяет использовать API NGINX Plus для изменения серверов в вышестоящей группе и настройки отдельных серверов без перезапуска NGINX. Дополнительные сведения см. в Руководстве администратора NGINX Plus.

Директива server содержит несколько параметров, которые вы можете использовать для настройки поведения сервера. В этом примере мы изменили настройки NGINX чтобы выявить, какой из серверов недоступен и не может принимать запросы. Сервер принимается за неработоспособный если попытка связи завершается неудачей хотя бы один раз в течение каждого 2-секундного периода (по умолчанию это один раз в 10-секундный период).

Мы объединяем этот параметр с директивой proxy_next_upstream, чтобы настроить то, что NGINX считает неудачной попыткой связи. Таким образом, он станет передавать запросы следующему серверу в вышестоящей группе. К условиям ошибки и тайм-аута по умолчанию мы добавляем http_500, чтобы NGINX рассматривал код ошибки HTTP 500 (внутренняя ошибка сервера) с вышестоящего сервера как неудачную попытку.

Директива keepalive задает количество незанятых keepalive-подключений к вышестоящим серверам, сохраненных в кэше каждого рабочего процесса. Мы уже обсуждали преимущества такого шага в Ошибке №3: Отсутствие соединения с вышестоящими серверами.

Благодаря NGINX Plus можно настроить дополнительные функции с помощью вышестоящих групп:

  • Как мы упоминали выше, NGINX Open Source преобразует имена хостов серверов в IP-адреса только один раз, во время запуска. Параметр resolve для директивы server позволяет NGINX Plus отслеживать изменения IP-адресов, соответствующих доменному имени вышестоящего сервера, и автоматически изменять конфигурацию вышестоящего сервера без необходимости перезагрузки.

Параметр service дополнительно позволяет NGINX Plus использовать записи DNS SRV, которые включают информацию о номерах портов, весе и приоритетах. Это имеет решающее значение в микросервисах, где номера портов часто назначаются динамически.

Дополнительные сведения о разрешении адресов серверов см. в разделе Использование DNS для обнаружения служб с помощью NGINX и NGINX Plus в нашем блоге.

  • Параметр slow_start в директиве server позволяет NGINX Plus постепенно увеличивать объем запросов, отправленных на сервер, признанный исправным и доступным для приема запросов. Это предотвратит внезапный поток запросов, которые могут перегрузить сервер и снова привести к сбою.

Директива queue позволяет NGINX Plus размещать запросы в очереди в случае невозможности выбрать вышестоящий сервер для обслуживания запроса, вместо того, чтобы немедленно возвращать клиенту сообщение об ошибке.

Ресурсы:
  • Gixy, анализатор конфигурации NGINX на GitHub
  • NGINX Amplify, который включает в себя инструмент Analyzer

Присоединяйтесь к нашему телеграмм-каналу DevOps FM.

Nginx proxy_pass: как настроить модуль?

Nginx proxy manager — инструмент, который поможет создать веб-сервер. Технология применяется на многих нагруженных ресурсах, таких как Instagram, мессенджеры и социальные сети. Также его применяют, как reverse tcp proxy, который распределяет нагрузку между веб-сервисами. Предоставляет возможность установить SSL-сертификаты для кэширования и соединения, для увеличения производительности. Главная задача web reverse proxy – обслуживание графического контента, например, HTML или изображений. Каждый сервер имеет порт, который побуждает принимать запросы именно от него. Применяться может для кэширования заголовков, обустройства инвертированного прокси-сервера и для других масштабных задач.

Когда мы заходим на сайт, то запрос от браузера приходит на веб-сервер, чтобы оценить его и отдать корректные данные обратно, через реверс-прокси.

Nginx proxy pass manager: как выполнить настройку? - Troywell VPN

Отличие engine x от обычного server

Nginx прокси не работает через единичный поток информации, то есть каждый запрос разделяется на однотипные структуры меньшего размера. Каждая структура обрабатывается отдельным процессом, а после отработки – данные вновь объединяются в блок, который возвращает системе результат. Таким образом, увеличивается количество однотипных обрабатываемых запросов до 1024.

Как еще используют nginx proxy manager

При помощи engine x возможно настроить проксирование запросов, поступающих на удаленный server, используя директиву proxy_pass.

Как работает ngx_http_proxy_module простыми словами

Модуль помогает реализовать функционал. Например, если в локальной сети есть сервисы, которые не имеют прямой доступ к Сети, но пользователь хочет его открыть. Для этого нужно осуществить настройку точки входа на сервисы при помощи nginx-сервера, а затем, при помощи него, проксировать запросы.

Пример

Опишем на примере, каким образом работает проксирование:

  1. Применяют для настройки чат-серверов matrix и mattermost на виртуальном сервере. Запросы проксируются на server при помощи nginx, принимающего входящие соединения.
  2. Допустим, у вас есть большой server с контейнерами, например, докера. На нем работает множество различных сервисов. Вы устанавливаете еще один контейнер с чистым engine x, на нем настраиваете проксирование запросов на эти контейнеры. Сами контейнеры имеют доступ только к локальному интерфейсу сервера. Таким образом, они будут полностью закрыты извне, и при этом вы можете гибко управлять доступом.
  3. При наличии server с гипервизором, на виртуальной машине создается шлюз и локальная сетка, состоящая из виртуальных машин, недоступная извне. В данной локальной сети шлюз настраивается по умолчанию, и представляет собой виртуальную машину. На серверах сети можно разместить сервисы, при этом настройку файервола не осуществлять. Доступ к servers проксируется через nginx, которые подключены к отдельной виртуальной машине с настроенными портами.
  4. Организация доступа к synology. Для этого нужно получить бесплатный сертификат Let’s encrypt, настроить проксирование через текущий IP, а затем через шлюз перебросить их внутрь локальной сети. При помощи фаервола возможно ограничить доступ к server через IP, при помощи которого работает engine x. На synology никаких настроек осуществлять не нужно.
  5. Если пользователь или компания разворачивает большой проект, который разделен на составляющие, размещенные на отдельных серверах. Например, отдельно находится форум и проксирование возможно перенести на отдельный сервер. При этом возможна реализация location с переадресацией.

Вариантов применения engine x – много. Рассмотрим плюсы настройки для описанных выше кейсов.

Плюсы настройки engine x

  • Быстрая настройка https доступа к сервисам, которая не будет изменять и затрагивать эти сервисы. То есть, пользователь, настраивая engine x, обеспечивает передачу данных от сервера к службам через http.
  • Адреса проксируемых запросов легко изменять. Например, при подготовке сайта к nginx rewrite, пользователь настраивает все на сервере, на котором проксируются запросы. Произведите изменение url сайта в engine x прокси на новый и примените настройки. Откатить изменения возможно также быстро и легко.

Nginx proxy pass manager: как выполнить настройку? - Troywell VPN

Пример настройки прокси

С теорией закончили. Перейдем теперь к примеру настройки прокси. В примере будем использовать следующие обозначения:

blog.1111.ru Имя тестового ресурса
nginx_srv Внешний сервер с engine x
blog_srv Локальный сервер для проксирования
94.142.141.246 внешний ip nginx_srv
192.168.13.31 ip адрес blog_srv
77.37.224.139 ip адрес для входа на сайт

Настройка proxy_pass в engine x

Попробуем объяснить настройку на реальном примере. Технический домен blog.1111.ru. В DNS создаем запись, которая приводит запрос на сервер, с установленным nginx_srv. Проксируем запросы с сервера на blog_srv, с реальным сайтом. Конфигурация:

error_log /var/log/nginx/blog.1111.ru -error.log;

proxy_set_header Host $host;

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

proxy_set_header X-Real-IP $remote_addr;

Открываем http:// blog.1111.ru. Попасть мы должны на blog_srv, на котором работает server. Представим, что он тоже на engine x. Содержимое должно быть идентично информации с http://192.168.13.31 (локалка). Логи серверов показывают, что запрос был направлен на nginx_srv, а затем произошел редирект на nginx_srv.

Реальный IP виден только в конце лога, поэтому REMOTE_ADDR не сможет вернуть настоящий IP. Но это можно исправить.

Создаем тестовую страницу chat_srv, при помощи которой будет возможно проверить IP клиента.

Даем ей название, например, myip.php.

Заходим на http:// blog.1111.ru/myip.php и смотрим, определяет ли server адрес. Т.к. мы не внесли исправления, то определяется nginx_srv, а не реальный адрес клиента.

Nginx proxy pass manager: как выполнить настройку? - Troywell VPN

Как передать IP клиента в nginx (обратный) при proxy_pass

На принимающей стороне производим обратную замену прокси. Через:

В итоге получаем:

listen 80 default_server;

index index.php index.html index.htm;

fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

error_page 404 /404.html;

error_page 500 502 503 504 /50x.html;

Сохраняем изменения и перепроверяем http:// blog.1111.ru/myip.php. Должен отобразиться реальный IP клиента.

Сохраняем конфиг, перечитываем его и снова проверяем Вы должны увидеть свой реальный ip адрес. Его же вы увидите в логе на blog_srv.

Вопросы от пользователей по engine x

Применяя https через proxy_pass нужно ли производить настройку https бэкенда?

Не обязательно, но возможно у вас есть программное обеспечение, которое не может функционировать в таком формате. Вы получите ссылку http://site.ru:443. Тогда придется осуществить настройку обмена данными между engine x и бэкендом.

Есть ли простой способ передачи let’s encrypt с engine x на backend? (Обновление и генерация осуществляются на engine x).

Есть два способа реализации. На nginx настраивается nfs сервер и уже затем, на бэкенде можно привязать директорию /etc/letsencrypt. Это позволит использовать сертификаты таким образом, будто они находятся на локальной директории.

Вариант два – использование bash-скрипта. Он будет копировать сертификаты через scp.

На бэкенде службы нужно постоянно перезапускать, после обновления сертификата.

Когда бэкенд обращается к адресу сайта, то запрос передается на nginx reverse proxy, потому что в ДНС указан его IP. Возникает проблема с отработкой скриптов и проверкой движка.

Чтобы ответ возвращался правильно, нужно поправить host /etc/hosts на бэкенде, прописав статические данные об имени сайта и локальным IP. Соответственно, запрос будет приходить не с nginx proxy cache от server, а локально.

Nginx или haproxy выбрать для проксирования?

Софт похожий по определению, поэтому четкого ответа на заданный вопрос дать невозможно. Но в haproxy есть функционал, который для nginx доступен только в расширенной версии. Выбирать нужно в соответствии с задачей, которую предстоит решить.

Добавляет ли engine x в режиме proxy_pass дополнительные сетевые задержки?

Да, но connect timeout мал, особенно если nginx proxy manager и сервер принадлежат одной и той же локальной сети. В общем, можно пренебречь задержками. При проксировании через интернет – следите за величиной задержки. Желательно, чтобы servers располагались как можно ближе друг к другу.

Nginx proxy pass manager: как выполнить настройку? - Troywell VPN

Заключение

Самая распространенная задача – проксирование внешних запросов в закрытую сеть. Если закрытая сеть не открыта для постороннего доступа, то нет необходимости в https при передаче информации на бэкенд.

Надеемся, что статья была полезной и интересной! Следите за обновлениями в нашем блоге.

Nginx proxy pass, настройка проксирования запросов в nginx

В Nginx proxy_pass является основной директивой нужной для проксирования (перенаправления в другие блоки конфигурации) запросов, дополнительный функционал реализуется за счет других правил, прописывающихся в файле виртуального хоста.

Средствами Nginx можно настроить различные серверные конфигурации, во многих из них основой является проксирвоание — перенаправление запросов с одного адреса на другой, с одного имени на другое, с одного порта на другой.

В качестве адреса для проксирования может задаваться доменное имя, ip адрес, комбинация ip адреса и порта.

Рассмотрим два случая:

  • проксирование на другой домен с сохранением запрашиваемого домена в адресной строке
  • проксирование с редиректом (со сменой запрашиваемого домена в адресной строке)

Они довольно специфические, самый базовый вариант использования можно увидеть в статье про запуск на сервере проекта на NodeJS

Настройка проксирования через Nginx proxy_pass

В примере рассматривается настройка проксирования запросов с subdomain.example.com на https://subdomain.example.ru/suf

На сервере, на котором настраивалось проксирование применяется Apache в качестве бэкенд сервера и Nginx в качестве фронтенд сервера. Проксирование реализовано на уровне фронтенда, до Apache запросы в первоначальном виде не доходят. Домен при существующих настройках будет оставаться тем же по которому пришел запрос (в этом отличие от редиректа).

server
server_name subdomain.example.com;
listen *:80;
proxy_read_timeout 200s;
access_log off;

include static.conf;

location /
root /var/www/sites/subdomain.example.com;
proxy_pass https://subdomain.example.ru/suf;

proxy_set_header X-Forwarded-Host subdomain.example.com:80;
proxy_set_header X-Forwarded-Server subdomain.example.com;
proxy_set_header X-Forwarded-For https://subdomain.example.ru/suf;

static.conf здесь — отдельный файл в котором заданы настройки кэширования, также они могут задаваться в любом другом конфиге.

За счет приведенных ниже директив реализуется реверсивное проксирование запросов, это аналог ProxyPassReverse в Apache:

proxy_set_header X-Forwarded-Host subdomain.example.com:80;
proxy_set_header X-Forwarded-Server subdomain.example.com;
proxy_set_header X-Forwarded-For https://subdomain.example.ru/suf;

Добавив конфигурационный файл активируем его стандартным способом — через создание символьной ссылки

ln -s /etc/nginx/sites-availible/example.com /etc/nginx/sites-enabled

Проверяем конфигурацию веб-сервера

Если ошибок нет — даем команду на перечитывание конфигурационных файлов

Nginx proxy_pass

Директива nginx proxy redirect

Схожим образом можно настроить в конфигурационном файле nginx редирект с одного адреса на другой.

Применяя аналогичный конфигурационный файл, но с раскомментированной директивой proxy_redirect можно получить перенаправление, при этом адрес сайта в адресной строке браузера будет подменяться.

Очень часто используется простое проксирование на IP адрес из приватной сети

location /
root /var/www/sites/example.com;
proxy_pass https://127.0.0.10;

С самой простой реализацией Nginx proxy_pass, приведенной выше, запускаются проекты на NodeJS и фреймоворке Express. Изначально они стартуют на localhost и порту 3000

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

настройка проксирования запросов в nginx

Здесь в закомментирвоанном виде представлены директивы для перенаправления запросов на порт 9000 к сервису php-fpm.

Читайте о том что делать если на сервере с конфигурацией с proxy_pass стала возникать ошибка с кодом 502.

Основы работы с Nginx: проксирование, балансировка нагрузки, буферизация и кэширование

В этом руководстве мы обсудим возможности HTTP-проксирования веб-сервера Nginx, которые позволяют ему передавать запросы на http-серверы бэкэнда для дальнейшей обработки. Nginx часто настраивается как обратный прокси-сервер, который помогает масштабировать инфраструктуру или передавать запросы другим серверам, которые не предназначены для обработки больших клиентских нагрузок.

Вы научитесь масштабировать свою инфраструктуру, используя встроенные функции балансировки нагрузки Nginx. Также вы узнаете, как с помощью буферизации и кеширования улучшить производительность прокси-операций клиентов.

Основы проксирования

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

Вторя причина настроить HTTP-проксирование – это наличие в инфраструктуре серверов приложений, которые не могут обрабатывать клиентские запросы напрямую в производственных средах. Многие фреймворки предоставляют встроенные веб-серверы, но большинство из них не столь надежны, как высокопроизводительные серверы, такие как Nginx. Использование обратного прокси Nginx может улучшить пользовательский опыт и повысить безопасность.

Проксирование в Nginx осуществляется путем обработки запроса, направленного на сервер Nginx, и передачи его другим серверам для фактической обработки. Результат запроса передается обратно на Nginx, который затем передает информацию клиенту. Другими серверами в этом случае могут быть удаленные компьютеры, локальные серверы или даже другие виртуальные серверы, определенные в настройке Nginx. Серверы, к которым обращается прокси Nginx, называются upstream серверами.

Nginx может проксировать запросы на серверы, которые обмениваются данными с помощью протоколов http(s), FastCGI, SCGI и uwsgi или memcached через отдельные наборы директив для каждого типа проксирования. В этом мануале мы сосредоточимся на протоколе http. Экземпляр Nginx отвечает за передачу запроса и связь с любым компонентом обмена сообщениями в формате, который может понять upstream сервер.

Директива proxy_pass

Самый простой тип проксирования включает в себя передачу запроса на один сервер, который может связываться с помощью http. Этот тип проксирования известен как proxy pass и обрабатывается одноименной директивой proxy_pass.

Директива proxy_pass в основном встречается в контекстах location. Она также поддерживается блоками if в контексте location и limit_except. Когда запрос совпадает с адресом, указанным в proxy_pass, он пересылается по этому URL-адресу.

Рассмотрим такой пример:

# server context
location /match/here proxy_pass http://example.com;
>
. . .

В приведенном выше фрагменте конфигурации в конце блока server в определении proxy_pass не указывается URI. Для определений, соответствующих этому шаблону, запрошенный клиентом URI будет передан на upstream сервер без изменений.

Например, когда этот блок обрабатывает запрос /match/here/please, URI запроса будет отправлен на сервер example.com как http://example.com/match/here/please.

Рассмотрим альтернативный сценарий:

# server context
location /match/here proxy_pass http://example.com/new/prefix;
>
. . .

В приведенном выше примере прокси-сервер определяется вместе с сегментом URI в конце (/new/prefix). Когда в определении proxy_pass указывается URI, то часть запроса, которая соответствует определению location, заменяется этим URI.

К примеру, запрос /match/here/please будет передаваться на upstream сервер как http://example.com/new/prefix/please. Префикс /match/here заменяется на /new/prefix. Об этом важно помнить.

Иногда такая замена невозможна. В этих случаях URI в конце определения proxy_pass игнорируется, и на upstream сервер передается исходный URI клиента или URI, измененный другими директивами.

Например, при использованием регулярных выражений Nginx не может определить, какая часть URI соответствует выражению, поэтому он отправляет исходный URI-запрос клиента. Или, например, если директива rewrite используется в одном и том же location, она переписывает URI клиента, но он все же обрабатывается в одном блоке. В этом случае будет передан переписанный URI.

Обработка заголовков в Nginx

Чтобы upstream сервер обработал запрос должным образом, одного URI недостаточно. Запрос, поступающий от имени клиента через Nginx, будет выглядеть иначе, чем запрос, поступающий непосредственно от клиента. Большая часть этого – заголовки, которые согласуются с запросом.

Когда Nginx проксирует запрос, он автоматически вносит некоторые поправки в заголовки, полученные от клиента.

  • Nginx избавляется ото всех пустых заголовков. Нет смысла передавать пустые значения другому серверу; это только усложнит передачу запроса.
  • Все заголовки, которые содержат символы подчеркивания, Nginx по умолчанию рассматривает как недопустимые. Он удалит их из запроса. Если вы хотите, чтобы Nginx интерпретировал их как валидные, вы можете установить в директиве underscores_in_headers значение on, в противном случае такие заголовки никогда не попадут на бэкэнд-сервер.
  • Заголовок Host переписывается значением, определяемым переменной $proxy_host. Это может быть IP-адрес или имя и номер порта upstream сервера, как указано в директиве proxy_pass.
  • Заголовок Connection заменяется значением close. Этот заголовок используется для передачи информации о конкретном соединении, установленном между двумя сторонами. В этом случае Nginx устанавливает это значение, чтобы указать upstream серверу, что это соединение будет закрыто после ответа на исходный запрос. Не следует ожидать, что это upstream соединение будет постоянным.

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

Также следует убедиться, что в нестандартных заголовках нет подчеркиваний, что если ваше бэкэнд-приложение будет обрабатывать такие заголовки. Если вам нужны заголовки, в которых используется символ подчеркивания, вы можете установить директиве underscores_in_headers значение on (это валидно либо в контексте http, либо в контексте объявления server по умолчанию для комбинации IP-адреса/порта). Если вы этого не сделаете, Nginx пометит эти заголовки как некорректные и просто сбросит их, прежде чем перейти к upstream серверу.

Заголовок Host имеет особое значение в большинстве прокси-сценариев. Как указано выше, по умолчанию этот заголовок получит значение переменной $proxy_host, которая содержит домен или IP-адрес и порт, взятые непосредственно из определения proxy_pass. Это поведение определяется по умолчанию, так как это единственный адрес, на который точно отвечает upstream сервер Nginx.

Заголовок Host часто имеет такие значения:

  • $proxy_host: устанавливает в Host домен или IP-адрес и порт из определения proxy_pass. Это значение по умолчанию надежно с точки зрения Nginx, но оно не всегда подходит для правильной обработки запроса.
  • $http_host: устанавливает в Host заголовок Host из клиентского запроса. Заголовки, отправленные клиентом, всегда доступны Nginx в качестве переменных. Переменные начинаются с префикса $http_, после которого устанавливается имя заголовка в нижнем регистре, а все тире заменяются нижним подчеркиванием. Помните, что переменная $http_host не сработает, если в запросе клиента нет валидного заголовка Host.
  • $host: эта переменная может принимать в качестве значений имя хоста из запроса, заголовок host из клиентского запроса или имя сервера соответствующего запроса.

В большинстве случаев нужно установить в заголовке Host переменную $host. Это наиболее гибкий вариант, который обычно обеспечивает точное заполнение заголовка.

Настройка или сброс заголовков

Чтобы настроить или установить заголовки для прокси-соединений, можно использовать директиву proxy_set_header. Например, чтобы изменить заголовок Host и добавить дополнительные заголовки, нужно использовать что-то вроде этого:

# server context
location /match/here proxy_set_header HOST $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://example.com/new/prefix;
>
. . .

Здесь заголовок Host получит значение переменной $host, в которой должна содержаться информация о запрошенном исходном хосте. Заголовок X-Forwarded-Proto предоставляет прокси-серверу информацию о схеме исходного запроса клиента (будь то http или https-запрос).

X-Real-IP указывает IP-адрес клиента, чтобы прокси-сервер мог правильно принимать решения или вести лог на основе этой информации. Заголовок X-Forwarded-For – это список, содержащий IP-адрес каждого сервера, по которому проходил запрос. В приведенном выше примере устанавливается значение переменной $proxy_add_x_forwarded_for. Эта переменная принимает значение исходного X-Forwarded-Forheader, извлеченного из клиента, и добавляет IP-адрес сервера Nginx в конец.

Конечно, директиву proxy_set_header стоит переместить в контекст server или http, чтоб иметь возможность ссылаться на нее:

# server context
proxy_set_header HOST $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
location /match/here proxy_pass http://example.com/new/prefix;
>
location /different/match proxy_pass http://example.com;
>

Раздел Upstream для балансировки нагрузки проксируемых соединений

В предыдущих примерах вы увидели, как сделать настроить простое HTTP-прокси соединение на одном сервере. Nginx позволяет легко масштабировать эту конфигурацию, указав целые пулы бэкэнд-серверов, на которые можно передавать запросы.

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

Рассмотрим простой пример:

# http context
upstream backend_hosts server host1.example.com;
server host2.example.com;
server host3.example.com;
>
server listen 80;
server_name example.com;
location /proxy-me proxy_pass http:// backend_hosts ;
>
>

В приведенном выше примере был создан контекст upstream под названием backend_hosts. После определения это имя будет доступно в proxy pass как обычный домен. Как вы можете видеть, в блоке server все запросы, сделанные в example.com/proxy-me/…, передаются в пул, который вы определили выше. В этом пуле хост выбирается с помощью настраиваемого алгоритма. По умолчанию это простой процесс round-robin (каждый запрос будет поочередно маршрутизироваться на другой хост).

Изменение алгоритма балансировки в контексте upstream

Настроить алгоритм в пуле upstream можно с помощью таких флагов и директив:

  • round robin: Алгоритм балансировки нагрузки по умолчанию, который используется, если не указано других директив. Каждый запрос будет последовательно передаваться серверам, определенным в контексте upstream.
  • least_conn: Указывает, что новые соединения всегда должны быть привязаны к бэкэнду, который имеет наименьшее количество активных соединений. Это может быть особенно полезно в ситуациях, когда соединения с бэкэндом могут сохраняться в течение некоторого времени.
  • ip_hash: Этот алгоритм балансировки распределяет запросы между серверами на основе IP-адреса клиента. Первые три октета используются в качестве ключа, на основе которого выбирается сервер для обработки запроса. В результате клиенты, как правило, обслуживаются одним и тем же сервером при каждом подключении, что обеспечивает согласованность сеансов.
  • hash: Этот алгоритм балансировки в основном используется с прокси-сервером memcached. Серверы делятся на группы на основе значения произвольно предоставленного хэш-ключа. Ключ может быть текстом, переменной или разными комбинациями. Это единственный метод балансировки, который требует от пользователя предоставить данные (ключ).

При изменении алгоритма блок может выглядеть так:

# http context
upstream backend_hosts least_conn;
server host1.example.com;
server host2.example.com;
server host3.example.com;
>
. . .

В приведенном выше примере сервер будет выбран по наименьшему количеству соединений. Можно также добавить директиву ip_hash, чтобы обеспечить «липкость» сессии.

Что касается метода hash, вы должны указать ключ для хэша. Это может быть что угодно:

# http context
upstream backend_hosts hash $remote_addr$remote_port consistent;
server host1.example.com;
server host2.example.com;
server host3.example.com;
>
. . .

В приведенном выше примере запросы будут распределяться на основе значений IP-адреса и порта клиента. Также здесь есть опциональный параметр consistent, который реализует алгоритм хэширования ketama consistent. Это означает, что если upstream серверы изменятся, это будет иметь минимальное воздействие на кэш.

Установка веса сервера для балансировки

В объявлениях бэкэнд-серверов по умолчанию все серверы весят одинаково. Это предполагает, что каждый сервер может и должен обрабатывать одинаковый объем нагрузки (с учетом эффектов алгоритмов балансировки). Тем не менее, вы также можете установить пользовательский вес своих серверов:

# http context
upstream backend_hosts server host1.example.com weight=3;
server host2.example.com;
server host3.example.com;
>
. . .

Теперь host1.example.com будет получать в три раза больше трафика, чем другие два сервера. Вес каждого сервера по умолчанию равен 1.

Использование буферов для освобождения бэкэнд-серверов

Один из главных вопросов при проксировании – насколько изменится скорость работы при добавлении сервера. Увеличение или уменьшение количества серверов можно значительно смягчить с помощью системы буферизации и кэширования Nginx.

При проксировании на другой сервер на опыт клиента влияет скорость двух разных подключений:

  • Подключения клиента к прокси Nginx.
  • И подключения прокси-сервера Nginx к серверу.

Nginx имеет возможность корректировать свое поведение на основе того, какое из этих соединений вы хотите оптимизировать.

Без буферов данные с прокси-сервера сразу же отправляются к клиенту. Если клиентские соединения быстрые, буферизацию можно отключить, чтобы клиент как можно скорее мог получить данные. При использовании буферов прокси-сервер Nginx будет временно хранить ответ бэкэнда, а затем передавать эти данные клиенту. Если клиент работает медленно, это позволит серверу Nginx быстрее закрыть соединение с бэкэндом. Затем он сможет обрабатывать передачу данных клиенту любым возможным способом.

Nginx по умолчанию использует буферизацию, так как скорость соединения, как правило, меняется в зависимости от клиента. Буферизация настраивается с помощью следующих директив. Их можно установить в контексте http, server или location. Важно иметь в виду, что директивы size касаются каждого запроса, поэтому они могут повлиять на производительность серверов при поступлении множества клиентских запросов.

  • proxy_buffering: эта директива определяет, включена ли буферизация для этого контекста и его дочерних контекстов. По умолчанию имеет значение on.
  • proxy_buffers: Эта директива контролирует количество (первый аргумент) и размер (второй аргумент) буферов. По умолчанию используется 8 буферов, размер которых равен одной странице памяти (либо 4k, либо 8k). Увеличение количества буферов позволяет буферизовать дополнительную информацию.
  • proxy_buffer_size: Исходная часть ответа от бэкэнд-сервера, которая содержит заголовки, буферизуется отдельно от остальных данных. Эта директива устанавливает размер буфера для этой части ответа. По умолчанию она будет того же размера, что и proxy_buffers, но поскольку здесь хранятся только заголовки, ее можно уменьшить.
  • proxy_busy_buffers_size: Эта директива устанавливает максимальное количество занятых буферов. Хотя клиент может считывать данные только из одного буфера за раз, буферы помещаются в очередь для отправки фрагментов данных клиенту. Эта директива управляет размером буферного пространства, которое может находиться в этом состоянии.
  • proxy_max_temp_file_size: Это максимальный размер временного файла каждого запроса на диске. Они создаются, если ответ бэкэнда слишком велик и не помещается в буфер.
  • proxy_temp_file_write_size: Это количество данных, которые Nginx будет записывать во временный файл за один раз, если ответ прокси-сервера слишком велик и не помещается в буфер.
  • proxy_temp_path: Это путь к области на диске, где Nginx должен хранить временные файлы, когда ответ upstream сервера не помещается в буфер.

Как вы можете видеть, Nginx предоставляет довольно много разных директив для настройки поведения буферизации. В большинстве случаев вам не придется использовать их, но некоторые из этих значений могут пригодиться. Возможно, наиболее полезными являются proxy_buffers и proxy_buffer_size.

В этом примере увеличивается количество доступных буферов для обработки запросов и уменьшается размер буфера для хранения заголовков:

# server context
proxy_buffering on;
proxy_buffer_size 1k;
proxy_buffers 24 4k;
proxy_busy_buffers_size 8k;
proxy_max_temp_file_size 2048m;
proxy_temp_file_write_size 32k;
location / proxy_pass http://example.com;
>

Если у вас есть быстрые клиенты, которым нужно быстро отправить данные, можно полностью отключить буферизацию. Nginx будет по-прежнему использовать буферы, если сервер upstream быстрее, чем клиент, но он попытается немедленно передать данные клиенту. Если клиент работает медленно, это может привести к тому, что upstream соединение останется открытым до тех пор, пока клиент не сможет получить данные. Когда буферизация выключена, будет использоваться только буфер, определенный директивой proxy_buffer_size:

# server context
proxy_buffering off;
proxy_buffer_size 4k;
location / proxy_pass http://example.com;
>

Высокая доступность (опционально)

Проксирование Nginx можно сделать более надежным, добавив избыточный набор балансировщиков нагрузки, чтобы создать инфраструктуру высокой доступности.

Настройка высокой доступности – это инфраструктура без единой точки отказа, и балансировщики нагрузки являются частью этой конфигурации. Имея несколько балансировщиков нагрузки, вы сможете предотвратить простои, если один из балансировщиков станет недоступен.

Кэширование и снижение времени ответа

Буферизация помогает освободить сервер бэкэнда для обработки большего количества запросов, но Nginx также может кэшировать контент с бэкэнд-серверов, устраняя необходимость подключения к upstream серверу для обработки запросов.

Настойка прокси-кэша

Для настройки кэширования ответов бэкэнд серверов можно использовать директиву proxy_cache_path, которая определяет пространство для хранения кэша. Её следует задавать в контексте http.

В приведенном ниже примере показано, как использовать эту и некоторые другие директивы для настройки системы кэширования.

# http context
proxy_cache_path /var/lib/nginx/cache levels=1:2 keys_zone=backcache:8m max_size=50m;
proxy_cache_key «$scheme$request_method$host$request_uri$is_args$args»;
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;

Директива proxy_cache_path определяет каталог в файловой системе, где нужно хранить кэш. В этом примере это каталог /var/lib/nginx/cache. Если этот каталог не существует, вы можете создать его и определить права доступа к нему:

sudo mkdir -p /var/lib/nginx/cache
sudo chown www-data /var/lib/nginx/cache
sudo chmod 700 /var/lib/nginx/cache

Параметр levels= указывает, как будет организован кэш. Nginx создаст ключ кеша путем хэширования значения ключа (он настраивается ниже). В результате будет создан каталог, имя которого состоит из одного символа (это будет последний символ хешированного значения), и подкаталог с именем из двух символов (следующие два символа в конце хэша). Это помогает Nginx быстро найти соответствующие значения.

Параметр keys_zone= определяет имя зоны кеша (backcache). Здесь также указывается, сколько метаданных можно хранить. В этом случае сервер будет хранить 8 МБ ключей. На 1 мегабайте Nginx может хранить около 8000 записей. Параметр max_size устанавливает максимальный размер кэшированных данных.

Директива proxy_cache_key устанавливает ключ, который будет использоваться для хранения кешированных значений. Этот же ключ используется для проверки того, можно ли запросить данные из кеша. Здесь используется комбинация схемы (http или https), метода HTTP-запроса, а также запрошенного хоста и URI.

Директива proxy_cache_valid может быть указана несколько раз. Она позволяет определить, как долго должны храниться значения в зависимости от кода состояния. В данном примере удачные и переадресованные ответы хранятся в течение 10 минут, а ответы 404 удаляются каждую минуту.

Теперь зона кэширования настроена, но пока что Nginx не знает, когда именно применять кеширование.

Эта информация указывается в контексте location для бекэнд серверов:

# server context
location /proxy-me proxy_cache backcache;
proxy_cache_bypass $http_cache_control;
add_header X-Proxy-Cache $upstream_cache_status;
proxy_pass http://backend;
>
. . .

Используя директиву proxy_cache, можно указать, что для этого контекста следует использовать зону кэширования backcache. Nginx проверит запись перед тем, как перейти к серверу.

Директива proxy_chache_bypass принимает значение переменной $http_cache_control. Эта переменная сообщает, запросил ли клиент свежий, не кэшированный ответ. При использовании этой директивы Nginx сможет корректно обрабатывать запросы такого типа. Никаких дальнейших настроек не для этого не требуется.

Мы также добавили дополнительный заголовок X-Proxy-Cache. Этот заголовок принимает значение переменной $upstream_cache_status. Это позволяет увидеть, обработан ли запрос из кэша, этих данных не было в кэше или клиент запросил новый ответ. Это особенно полезно для отладки.

Рекомендации по кэшированию результатов

Кеширование увеличивает скорость прокси-сервера. Но не стоит забывать о нескольких нюансах.

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

Если на сайте есть динамические элементы, им следует уделить внимание. Решение этой проблемы зависит от бекэнд-сервера. Для личных данных используется заголовок Cache-Control со значением no-cache, no-store или private.

  • no-cache: ответ не будет отправлен, пока сервер не проверит данные на бекэнд-сервере. Это значение используется с динамическими данными. Хэшированные метаданные заголовка Etag проверяются при каждом запросе. Если бекэнд вернет теже значения, то данные отправляются из кэша клиенту.
  • no-store: данные не должны храниться в кэше ни при каких обстоятельствах. Это самый безопасный подход при работе с личными данными.
  • private: данные не должны кэшироваться в общем кэше. То есть, к примеру, браузер пользователя может кешировать данные, а прокси-сервер нет.
  • public: данные можно кэшировать везде.

Есть связанный с этим поведением заголовок max-age, который определяет срок хранения кэша в секундах.

Его значение зависит от чувствительности данных. При разумном использовании этого заголовка конфиденциальные данные будут в безопасности, а часто изменяемый контент будет своевременно обновляться.

Если вы используете nginx и на бэкэнде, добавьте директиву expires, которая определяет значение max-age заголовка Cache-Control:

location / expires 60m;
>
location /check-me expires -1;
>

Первый блок поддерживает кэш в течение часа. Второй блок присваивает заголовку Cache-Control значение no-cache. Для внесения других изменений примените директиву add_header:

location /private expires -1;
add_header Cache-Control «no-store»;
>

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *