Поиск по сайту:

Понимание структуры файла конфигурации Nginx и контекстов конфигурации


Введение

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

Понимание контекстов конфигурации Nginx

В этом руководстве будет рассмотрена структура основного файла конфигурации Nginx. Расположение этого файла будет зависеть от того, как был установлен Nginx. Во многих дистрибутивах Linux этот файл находится по адресу /etc/nginx/nginx.conf. Если его там нет, он также может находиться в /usr/local/nginx/conf/nginx.conf или /usr/local/etc/nginx/nginx.conf.

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

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

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

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

Основные контексты

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

Основной контекст

Самый общий контекст — это «основной» или «глобальный» контекст. Это единственный контекст, который не содержится в типичных блоках контекста, которые выглядят следующим образом:

# The main context is here, outside any other contexts

. . .

context {

    . . .

}

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

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

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

Контекст событий

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

Этот контекст будет выглядеть в конфигурационном файле вне любых других контекстов в квадратных скобках:

# main context

events {

    # events context
    . . .

}

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

Обычно метод обработки соединения выбирается автоматически на основе наиболее эффективного выбора, доступного на платформе. Для систем Linux лучше всего подходит метод epoll.

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

HTTP-контекст

Определение контекста HTTP, вероятно, является наиболее распространенным использованием Nginx. При настройке Nginx в качестве веб-сервера или обратного прокси-сервера контекст «http» будет содержать большую часть конфигурации. Этот контекст будет содержать все директивы и другие контексты, необходимые для определения того, как программа будет обрабатывать соединения HTTP или HTTPS.

Контекст http является братом контекста событий, поэтому они должны быть перечислены рядом, а не вложены друг в друга. Оба они являются дочерними элементами основного контекста:

# main context

events {
    # events context

    . . .

}

http {
    # http context

    . . .

}

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

Некоторые из директив, с которыми вы, вероятно, столкнетесь, управляют расположением по умолчанию журналов доступа и ошибок (access_log и error_log), настраивают асинхронный ввод-вывод для файловых операций (aio, sendfile и directio), а также настроить статусы сервера при возникновении ошибок (error_page). Другие директивы настраивают сжатие (gzip и gzip_disable), точную настройку параметров поддержания активности TCP (keepalive_disable, keepalive_requests и keepalive_timeout), а также правила, которым будет следовать Nginx, чтобы попытаться оптимизировать пакеты и системные вызовы (sendfile, tcp_nodelay и tcp_nopush). Дополнительные директивы настраивают корневой и индексный файлы документа на уровне приложения (root и index) и настраивают различные хэш-таблицы, которые используются для хранения различных типов данных (*_hash_bucket_size и *_hash_max_size для имен_серверов, типов и переменных). Для получения дополнительной информации обратитесь к документации Nginx.

Контекст сервера

Контекст \сервера объявляется внутри контекста \http. Это наш первый пример вложенных контекстов в квадратных скобках. Это также первый контекст, допускающий несколько объявлений.

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

# main context

http {

    # http context

    server {

        # first server context

    }

    server {

        # second server context

    }

}

Вы можете объявить несколько контекстов server, поскольку каждый экземпляр определяет определенный виртуальный сервер для обработки клиентских запросов. У вас может быть столько серверных блоков, сколько вам нужно, каждый из которых может обрабатывать определенное подмножество соединений.

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

  • listen: комбинация IP-адреса/порта, для ответа на которую предназначен этот серверный блок. Если запрос сделан клиентом, который соответствует этим значениям, этот блок потенциально будет выбран для обработки соединения.
  • имя_сервера: эта директива является другим компонентом, используемым для выбора блока сервера для обработки. Если есть несколько серверных блоков с директивами listen одинаковой специфики, которые могут обработать запрос, Nginx проанализирует заголовок «Host» запроса и сопоставит его с этой директивой.

Директивы в этом контексте могут переопределить многие директивы, которые могут быть определены в контексте http, включая ведение журнала, корень документа, сжатие и т. д. В дополнение к директивам, взятым из контекста http, мы также можем настроить файлы для попытаться ответить на запросы (try_files), выполнить перенаправление и перезапись (return и rewrite) и установить произвольные переменные (set).

Контекст местоположения

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

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

Общий синтаксис выглядит так:

location match_modifier location_match {

    . . .

}

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

# main context

server {
    
    # server context

    location /match/criteria {

        # first location context

    }

    location /other/criteria {

        # second location context

        location nested_match {

            # first nested location

        }

        location other_nested {

            # second nested location

        }

    }

}

В то время как контексты сервера выбираются на основе запрошенной комбинации IP-адреса/порта и имени хоста в заголовке «Host», блоки местоположения дополнительно разделяют обработку запросов в блоке сервера, просматривая URI запроса. URI запроса — это часть запроса, которая идет после имени домена или комбинации IP-адреса/порта.

Скажем, например, что клиент запрашивает http://www.example.com/blog через порт 80. http, www.example.com, и компоненты порта 80 по отдельности будут использоваться для определения того, какой серверный блок выбрать. После того, как сервер выбран, часть /blog (URI запроса) будет оцениваться по определенным местоположениям, чтобы определить, какой дополнительный контекст следует использовать для ответа на запрос.

Многие из директив, которые вы, вероятно, увидите в контексте местоположения, также доступны на родительских уровнях. Новые директивы на этом уровне позволяют вам обращаться к расположениям за пределами корня документа (псевдоним), помечать расположение как доступное только для внутреннего использования (internal) и проксировать на другие серверы или местоположения (используя проксирование http, fastcgi, scgi и uwsgi).

Другие контексты

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

  • split_clients: этот контекст настроен для разделения клиентов, которые получает сервер, на категории, помечая их переменными на основе процента. Затем их можно использовать для проведения A/B-тестирования, предоставляя разный контент разным хостам.
  • perl/perl_set: эти контексты настраивают обработчики Perl для расположения, в котором они появляются. Это будет использоваться только для обработки с помощью Perl.
  • map: этот контекст используется для установки значения переменной в зависимости от значения другой переменной. Он обеспечивает сопоставление значений одной переменной, чтобы определить, какое значение следует установить для второй переменной.
  • geo: как и контекст выше, этот контекст используется для указания сопоставления. Однако это сопоставление специально используется для классификации клиентских IP-адресов. Он устанавливает значение переменной в зависимости от подключаемого IP-адреса.
  • types: этот контекст снова используется для сопоставления. Этот контекст используется для сопоставления типов MIME с расширениями файлов, которые должны быть с ними связаны. Обычно это предоставляется Nginx через файл, который находится в основном файле конфигурации nginx.conf.
  • charset_map: это еще один пример контекста сопоставления. Этот контекст используется для сопоставления таблицы преобразования из одного набора символов в другой. В заголовке контекста перечислены оба набора, а в теле выполняется сопоставление.

Восходящий контекст

Восходящий контекст используется для определения и настройки «восходящих» серверов. Этот контекст определяет именованный пул серверов, к которым Nginx может затем передавать прокси-запросы. Этот контекст, вероятно, будет использоваться при настройке прокси-серверов различных типов.

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

# main context

http {

    # http context

    upstream upstream_name {

        # upstream context

        server proxy_server1;
        server proxy_server2;

        . . .

    }

    server {

        # server context

    }

}

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

Если контекст

Контекст «if» может быть установлен для обеспечения условной обработки директив. Подобно оператору if в традиционном программировании, директива if в Nginx будет выполнять содержащиеся инструкции, если данный тест возвращает «true».

Контекст if в Nginx предоставляется модулем перезаписи, и это основное предполагаемое использование этого контекста. Поскольку Nginx будет проверять условия запроса со многими другими специальными директивами, if не следует использовать для большинства форм условного выполнения. Это настолько важное замечание, что сообщество Nginx создало страницу под названием if is evil.

Проблема в том, что порядок обработки Nginx очень часто может приводить к неожиданным результатам. Единственными директивами, которые считаются надежно безопасными для использования внутри этих контекстов, являются директивы return и rewrite (те, для которых этот контекст был создан). Еще одна вещь, о которой следует помнить при использовании контекста if, заключается в том, что он делает директиву try_files в том же контексте бесполезной.

Чаще всего if будет использоваться для определения необходимости перезаписи или возврата. Чаще всего они будут существовать в блоках местоположения, поэтому общая форма будет выглядеть примерно так:

# main context

http {

    # http context

    server {

        # server context

        location location_match {

            # location context

            if (test_condition) {

                # if context

            }

        }

    }

}

Контекст Limit_except

Контекст limit_except используется для ограничения использования определенных методов HTTP в контексте местоположения. Например, если только определенные клиенты должны иметь доступ к содержимому POST, но все должны иметь возможность читать содержимое, вы можете использовать блок limit_except для определения этого требования.

Приведенный выше пример будет выглядеть примерно так:

. . .

# server or location context

location /restricted-write {

    # location context

    limit_except GET HEAD {

        # limit_except context

        allow 192.168.1.1/24;
        deny all;
    }
}

Это применит директивы внутри контекста (предназначенные для ограничения доступа) при обнаружении любых методов HTTP, кроме перечисленных в заголовке контекста. Результатом приведенного выше примера является то, что любой клиент может использовать команды GET и HEAD, но только клиенты из подсети 192.168.1.1/24 могут использовать другие методы.

Общие правила, которым нужно следовать в отношении контекстов

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

Применять директивы в максимально доступном контексте

Многие директивы действительны более чем в одном контексте. Например, существует довольно много директив, которые можно поместить в контекст http, server или location. Это дает нам гибкость в настройке этих директив.

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

Прежде всего, объявление на высоком уровне позволяет избежать ненужного повторения между одноуровневыми контекстами. Например, в приведенном ниже примере каждое из местоположений объявляет один и тот же корень документа:

http {
    server {
        location / {
            root /var/www/html;

            . . .

        }

        location /another {
            root /var/www/html;

            . . .

        }

    }
}

Вы можете переместить корень в блок сервера или даже в блок http, например:

http {
    root /var/www/html;
    server {
        location / {

            . . .

        }

        location /another {

            . . .

        }
    }
}

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

Используйте несколько одноуровневых контекстов вместо логики If для обработки

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

Во-первых, директива \if часто возвращает результаты, не соответствующие ожиданиям администратора. Хотя обработка всегда будет приводить к одному и тому же результату при одних и тех же входных данных, способ, которым Nginx интерпретирует среду, может отличаться от ожидаемого. предполагалось без серьезных испытаний.

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

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

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

Заключение

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

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

Далее вы можете узнать, как настроить аутентификацию по паролю с помощью Nginx.