Одной из наиболее полезных, но часто неправильно понятых и неправильно настроенных функций NGINX является ограничение скорости . Это позволяет ограничить количество HTTP-запросов, которые пользователь может сделать за определенный период времени. Запрос может быть простым GET
запросом на домашнюю страницу веб-сайта или POST
запросом в форме входа.
Ограничение скорости может быть использовано в целях безопасности, например, для замедления атак с использованием подбора паролей. Это может помочь защитить от DDoS-атак , ограничивая частоту входящих запросов значением, типичным для реальных пользователей, и (с ведением журнала) идентифицировать целевые URL-адреса. В более общем смысле, он используется для защиты вышестоящих серверов приложений от чрезмерного количества одновременных запросов пользователей.
В этом блоге мы расскажем об основах ограничения скорости с NGINX, а также о более сложных конфигурациях. Ограничение скорости работает так же в NGINX Plus.
Чтобы узнать больше об ограничении скорости с NGINX, зарегистрируйтесь на нашем онлайн-вебинаре .
Как работает ограничение скорости NGINX
Ограничение скорости NGINX использует алгоритм утечки, который широко используется в телекоммуникационных сетях и компьютерных сетях с коммутацией пакетов, чтобы справиться с пакетными помехами, когда полоса пропускания ограничена. Аналогия с ведром, где вода заливается сверху и вытекает снизу; если скорость, с которой вода наливается, превышает скорость, с которой она протекает, ведро переполняется. С точки зрения обработки запросов, вода представляет запросы от клиентов, а корзина представляет очередь, в которой запросы ожидают обработки в соответствии с алгоритмом планирования «первым пришел — первым обслужен» (FIFO). Утечка воды представляет запросы, выходящие из буфера для обработки сервером, а переполнение представляет запросы, которые отбрасываются и никогда не обслуживаются.
Настройка базового ограничения скорости
Ограничение скорости настраивается с помощью двух основных директив, limit_req_zone
и limit_req
, как в этом примере:
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
server {
location /login/ {
limit_req zone=mylimit;
proxy_pass http://my_upstream;
}
}
limit_req_zone
Директива определяет параметры для ограничения скорости при одновременном обеспечении ограничения скорости в контексте , где он появляется (в данном примере для всех запросов к / Войти / ).
limit_req_zone
Директива , как правило , определяется в http
блоке, что делает его доступным для использования в различных контекстах. Он принимает следующие три параметра:
- Ключ — определяет признак запроса, к которому применяется ограничение. В этом примере это переменная NGINX
$binary_remote_addr
, которая содержит двоичное представление IP-адреса клиента. Это означает, что мы ограничиваем каждый уникальный IP-адрес частотой запросов, определенной третьим параметром (мы используем эту переменную, потому что она занимает меньше места, чем строковое представление IP-адреса клиента$remote_addr
). - Зона — Определяет зону разделяемой памяти, используемую для хранения состояния каждого IP-адреса, и частоту обращения к URL-адресу, ограниченному запросом. Хранение информации в общей памяти означает, что она может быть разделена между рабочими процессами NGINX. Определение состоит из двух частей: имя зоны, определяемое
zone=
ключевым словом, и размер после двоеточия. Информация о состоянии около 16 000 IP-адресов занимает 1 мегабайт, поэтому наша зона может хранить около 160 000 адресов. Если хранилище исчерпано, когда NGINX необходимо добавить новую запись, он удаляет самую старую запись. Если свободного места все еще недостаточно для размещения новой записи, NGINX возвращает код состояния503(TemporarilyUnavailable)
, Кроме того, чтобы предотвратить исчерпание памяти, каждый раз, когда NGINX создает новую запись, он удаляет до двух записей, которые не использовались в предыдущие 60 секунд. - Rate — устанавливает максимальную частоту запросов. В этом примере скорость не может превышать 10 запросов в секунду. NGINX фактически отслеживает запросы с точностью до миллисекунды, поэтому этот предел соответствует 1 запросу каждые 100 миллисекунд. Поскольку мы не допускаем посылки, это означает, что запрос отклоняется, если он поступает менее чем через 100 миллисекунд после предыдущего разрешенного.
limit_req_zone
Директива устанавливает параметры для ограничения скорости и общей зоны памяти, но она фактически не ограничивает скорость запроса. Для этого вам нужно применить ограничение к конкретному location
или server
блоку, включив туда limit_req
директиву. В этом примере мы ограничиваем скорость запросов к / login / .
Таким образом, теперь каждый уникальный IP-адрес ограничен 10 запросами в секунду для / login /, или, точнее, не может сделать запрос для этого URL в течение 100 миллисекунд от его предыдущего.
Обработка очередей
Что если мы получим 2 запроса в течение 100 миллисекунд друг от друга? Для второго запроса NGINX возвращает код 503
состояния клиенту. Это, вероятно, не то, что мы хотим, потому что приложения, как правило, имеют импульсный характер Вместо этого мы хотим буферизовать любые лишние запросы и своевременно обслуживать их. Здесь мы используем burst
параметр для limit_req
, как в этой обновленной конфигурации:
location /login/ {
limit_req zone=mylimit burst=20;
proxy_pass http://my_upstream;
}
В burst
определяет параметр , сколько запросов клиент может сделать в превышении скорости , указанной зона (с нашей образцом mylimit зоной, ограничение скорости составляет 10 запросов в секунду, или 1 каждые 100 миллисекунд). Запрос, который приходит раньше, чем через 100 миллисекунд после того, как предыдущий был помещен в очередь, и здесь мы устанавливаем размер очереди равным 20.
Это означает, что если 21 запрос поступает с данного IP-адреса одновременно, NGINX немедленно перенаправляет первый запрос в вышестоящую группу серверов и помещает оставшиеся 20 в очередь. Затем он пересылает запрос в очереди каждые 100 миллисекунд и возвращает 503
клиенту только в том случае, если во входящем запросе количество запросов в очереди превысило 20.
Очередь без задержки
Конфигурация burst
приводит к плавному потоку трафика, но не очень практична, потому что она может сделать ваш сайт медленным. В нашем примере 20-й пакет в очереди ожидает 2 секунды для пересылки, после чего ответ на него может больше не быть полезным для клиента. Чтобы разрешить эту ситуацию, добавьте nodelay
параметр вместе с burst
параметром:
location /login/ {
limit_req zone=mylimit burst=20 nodelay;
proxy_pass http://my_upstream;
}
С помощью этого nodelay
параметра NGINX по-прежнему выделяет слоты в очереди в соответствии с burst
параметром и накладывает настроенное ограничение скорости, но не путем разнесения пересылки запросов в очереди. Вместо этого, когда запрос приходит «слишком рано», NGINX пересылает его немедленно, если в очереди для него есть доступный слот. Он помечает этот слот как «занятый» и не освобождает его для использования другим запросом, пока не пройдет соответствующее время (в нашем примере, после 100 миллисекунд).
Предположим, что, как и прежде, очередь из 20 слотов пуста, и с данного IP-адреса одновременно поступает 21 запрос. NGINX перенаправляет все 21 запрос немедленно и помечает 20 слотов в очереди как занятые, затем освобождает 1 слот каждые 100 миллисекунд (если вместо этого было 25 запросов, NGINX немедленно переадресует 21 из них, помечает 20 слотов как принятые и отклоняет 4 запроса с помощью статус 503
).
Теперь предположим, что через 101 миллисекунду после пересылки первого набора запросов одновременно поступают еще 20 запросов. Только 1 слот в очереди был освобожден, поэтому NGINX перенаправляет 1 запрос и отклоняет остальные 19 со статусом 503
. Если вместо этого прошло 501 миллисекунда, прежде чем поступят 20 новых запросов, 5 слотов свободны, поэтому NGINX немедленно пересылает 5 запросов и отклоняет 15.
Эффект эквивалентен ограничению скорости 10 запросов в секунду. Эта nodelay
опция полезна, если вы хотите наложить ограничение скорости без ограничения допустимого интервала между запросами.
Примечание: Для большинства развертываний, мы рекомендуем в том числе burst
и nodelay
параметры в limit_req
директиве.
Расширенные примеры конфигурации
Комбинируя базовое ограничение скорости с другими функциями NGINX, вы можете реализовать более детальное ограничение трафика.
Whitelisting
В этом примере показано, как наложить ограничение скорости на запросы от любого, кто не входит в «белый список».
geo $limit {
default 1;
10.0.0.0/8 0;
192.168.0.0/24 0;
}
map $limit $limit_key {
0 "";
1 $binary_remote_addr;
}
limit_req_zone $limit_key zone=req_zone:10m rate=5r/s;
server {
location / {
limit_req zone=req_zone burst=10 nodelay;
# ...
}
}
В этом примере используется как geo
и map
директив. geo
Блок присваивает значение 0
для $limit
для IP — адресов в белом списке и 1
для всех остальных. Затем мы используем карту для перевода этих значений в ключ, такой что:
- Если
$limit
есть0
,$limit_key
устанавливается пустая строка. - Если
$limit
есть1
,$limit_key
устанавливается IP-адрес клиента в двоичном формате.
Соединяя их вместе, $limit_key
задается пустая строка для белого списка IP-адресов, а в противном случае — для IP-адреса клиента. Когда первый параметр limit_req_zone
каталога (ключ) является пустой строкой, ограничение не применяется, поэтому IP-адреса из белого списка (в подсетях 10.0.0.0/8 и 192.168.0.0/24) не ограничены. Все остальные IP-адреса ограничены 5 запросами в секунду.
limit_req
Директива применяется ограничение на /
месте и позволяет всплески до 10 пакетов через установленный предел без задержки на пересылку
Включение нескольких limit_req
директив в местоположение
Вы можете включить несколько limit_req
директив в одном месте. Применяются все ограничения, соответствующие данному запросу, то есть используется наиболее ограничительный. Например, если более одной директивы накладывает задержку, используется самая длинная задержка. Точно так же запросы отклоняются, если это является следствием какой-либо директивы, даже если другие директивы пропускают их.
Расширяя предыдущий пример, мы можем применить ограничение скорости к IP-адресам в белом списке:
http {
# ...
limit_req_zone $limit_key zone=req_zone:10m rate=5r/s;
limit_req_zone $binary_remote_addr zone=req_zone_wl:10m rate=15r/s;
server {
# ...
location / {
limit_req zone=req_zone burst=10 nodelay;
limit_req zone=req_zone_wl burst=20 nodelay;
# ...
}
}
}
IP-адреса в белом списке не соответствуют первому пределу скорости ( req_zone ), но соответствуют второму ( req_zone_wl ) и поэтому ограничены 15 запросами в секунду. IP-адреса, отсутствующие в белом списке, соответствуют обоим ограничениям скорости, поэтому применяется более ограничительный: 5 запросов в секунду.
Настройка связанных функций
логирование
По умолчанию NGINX регистрирует запросы, которые задерживаются или отбрасываются из-за ограничения скорости, как в этом примере:
2015/06/13 04:20:00 [error] 120315#0: *32086 limiting requests, excess: 1.000 by zone "mylimit", client: 192.168.1.2, server: nginx.com, request: "GET / HTTP/1.0", host: "nginx.com"
Поля в записи журнала включают в себя:
limitingrequests
— Индикатор того, что в записи журнала записан предел скорости.excess
— Количество запросов в миллисекунду по настроенной скорости, которую представляет этот запрос.zone
— Зона, которая определяет установленный лимит скорости.client
— IP-адрес клиента, сделавшего запрос.server
— IP-адрес или имя хоста сервера.request
— Фактический HTTP-запрос, сделанный клиентом.host
— ЗначениеHost
заголовка HTTP.
По умолчанию NGINX регистрирует отклоненные запросы на error
уровне, как показано [error]
в примере выше (он регистрирует отложенные запросы на один уровень ниже, поэтому info
по умолчанию). Чтобы изменить уровень ведения журнала, используйте limit_req_log_level
директиву. Здесь мы устанавливаем отклоненные запросы на вход в систему на warn
уровне:
location /login/ {
limit_req zone=mylimit burst=20 nodelay;
limit_req_log_level warn;
proxy_pass http://my_upstream;
}
Код ошибки, отправленный клиенту
По умолчанию NGINX отвечает кодом состояния, 503
когда клиент превышает свой предел скорости. Используйте limit_req_status
директиву для установки другого кода состояния ( 444
в этом примере):
location /login/ {
limit_req zone=mylimit burst=20 nodelay;
limit_req_status 444;
}
Отказ от всех запросов в определенном месте
Если вы хотите отклонить все запросы для определенного URL-адреса, а не просто ограничить их, настройте для него блок и включите all
директиву:
location /foo.php {
deny all;
}
Заключение
Мы рассмотрели много особенностей ограничения скорости , что предложение NGINX и NGINX Plus, включая настройку скорости запросов для разных мест по HTTP запросов и настройке дополнительных функций, ограничение скорости , такие как burst
и nodelay
параметров. Мы также рассмотрели расширенную настройку для применения различных ограничений для IP-адресов клиентов из белого и черного списков и объяснили, как регистрировать отклоненные и отложенные запросы.