Как стать автором
Обновить
69.34
Nixys
DevOps, DevSecOps, MLOps — системный IT-интегратор

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

Время на прочтение17 мин
Количество просмотров62K
Автор оригинала: Timo Stark of F5 and Sergey Budnevich of F5


Помогая пользователям 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 <error_log_location> в команде 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 {
    proxy_pass b;
}

Для этого и многих других применений 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.

Теги:
Хабы:
Всего голосов 70: ↑70 и ↓0+70
Комментарии3

Публикации

Информация

Сайт
nixys.ru
Дата регистрации
Дата основания
Численность
51–100 человек
Местоположение
Россия
Представитель
Vlada Grishkina-Makareva