Nginx и Leaky Bucket (дырявое ведро)

logo

leaky-bucket-logo В nginx реализован классический, для телекоммуникации, алгоритм "дырявого ведра" (leaky bucket), который призван контролировать поток обрабатываемого трафика, что помогает предотвращать ДДОС атаки. В этом посте будем понимать как на практике работает "дырявое ведро" (leaky bucket).

Как работает "дырявое ведро"

leaky_bucket_analogy Дырявое ведро ("дырявое ведро") - это алгоритм, который отвечает за формирование потока трафика. Параметрами алгоритма являются: размер буфера - "ковша/ведра" (bucket); количество данных (в байтах или битах), которые покидают буфер в установленную единицу времени. Этот алгоритм позволяет устанавливать частоту, с которой данные будут извлекаться из очереди/буфера (ведра/ковша) и передаваться по сети.

Таким образом алгоритм "дырявое ведро" является своего рода фильтром, пропускным пунктом, который регулирует поток данных, что в свою очередь помогает бороться с ДДОС атаками.

Конфигурация дырявого ведра в Nginx

Leaky Bucket (дырявое ведро) в Nginx регулируется директивами модуля ngx_http_limit_req_module, которые более или менее полно описаны в мануале за исключением может только третьего параметра nodelay директивы limit_req.

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


Реально рабочая конфигурация дырявого ведра в Nginx:

http {
    ...
 
    # http://nginx.org/ru/docs/http/ngx_http_limit_req_module.html
    limit_req_zone  $binary_remote_addr  zone=limitreq:10m   rate=3r/s;
    # http://nginx.org/ru/docs/http/ngx_http_limit_conn_module.html
    limit_conn_zone $binary_remote_addr zone=limitconn:10m;
 
    ...
}
 
server {
    ...
 
    limit_req zone=limitreq burst=120;
    limit_conn limitconn 5;
    limit_req_log_level notice;
    limit_req_status 429;
 
    ...
}

limit_req_zone создаёт зону с именем limitreq, в которой будут сохранятся состояния разных ключей вместе с отложенными запросами каждого состояния, размером 10 МБ и скоростью (rate) обработки 3 запроса в сек. (3r/s). В качестве ключа состояний будет использован $binary_remote_addr ИП-адрес посетителя.

limit_req собственно активирует наше "дырявое ведро" с параметрами зоны limitreq и размером всплеска (burst) в 120 запросов.

Что означает "размер всплеска" (burst)? "Размер всплеска" - это то число запросов которое может просочится, а вернее даже пролететь со свистом, сквозь наше "дырявое ведро" до начала применения ограничения "rate=3r/s".

За какое время должен произойти этот разрешённый всплеск запросов? Хороший вопрос, - Сысоев и гугл умалчивает эту информацию, но... Практика показала, что время выполнения "всплеска" не зависит от "rate=3r/s" и может произойти за 1-3-5 сек. например, или в течении тайм-аута текущего соединения с удалённым ИП. Т.е.:

  1. Как только было открыто соединение и в него посыпались запросы, то они начинают течь через ведро до того момента (независимо от "rate=3r/s"), пока не превысят значение параметра burst;
  2. Когда значение параметра burst превышено, то дно ведра закрывается ситом, в нашем случае rate=3r/s, с тремя дырками, через которое, уже с временным лимитом в 1 сек, вытекает по 3 капли (запроса), а данные не успевающие вытекать наполняют ёмкость ведра;
  3. Величиной ёмкости ведра является значение параметра burst, 120 в нашем случае. По мере накопления данных в ведре, они вытекают из него по принципу «первым пришёл, первым ушёл» (FIFO - First In, First Out).
  4. Запросы превысившие ёмкость ведра и выпадающие за его края отклоняются, а клиенту возвращается ответ с НТТР статусом 503 (Service Temporarily Unavailable);
  5. После полного опустошения ведра нам снова будет доступен "всплеск" (разово, т.е. откроется дыра для беспрепятственного прохода числа burst) + ёмкость ведра размером в тот же "всплеск" с лимитом вытекания "rate=3r/s".

...Да, теперь немного про параметр nodelay. Раз все запросы сверх burst по умолчанию откладываются в ведро размером того же burst, а:

  • -Избыточные запросы задерживаются до тех пор, пока их число не превысит максимальный размер всплеска. При превышении запрос завершается с ошибкой 503 (Service Temporarily Unavailable).

То с большой долей вероятности можно сказать, что параметр nodelay запрещает использовать ёмкость ведра для накопления запросов и после разового использования ресурсов всплеска burst + 3r/s (итого 123 запроса) все последующие отклоняются со статусом 503 (Service Temporarily Unavailable). Без параметра nodelay сервер обработает burst (120, всплеск разово) + 3r/s + ёмкость размером в burst для отложенных запросов = 243 запроса + ежесекундно по 3 запроса.

И последнее. Директивой limit_req_status мы задали HTTP статус 429 (Too Many Requests), который будет выдан клиенту в заголовке ответа ('HTTP/1.1 429 Too Many Requests'). Вместе с ответом HTTP 429 дополнительно может быть передан заголовок 'Retry-After: 3600', где 3600 это время в сек. через которое клиент сможет повторить запросы к серверу. HTTP статус 429 пока что официально не утверждён (https://tools.ietf.org/html/rfc6585#section-4), но думаю его одобрение уже не за горами.

Leakбез

Ликбез (ликвидация безграмотности).

Гринго, братанос, сестринос и различные камрады в сети по теме limit_req, как обычно, несут всякий бред и только на англоязычных ресурсах можно "надыбать" относительно адекватные посты. Например, в некоторых тредах и постах камрады:

  • - rate=3r/s считают кликами, а не запросами;
  • - limit_conn трактуют как число загрузок, а не соединений;
  • - burst привязывают к временному интервалу указанному в rate, т.е. на каждую секунду подразумевают разрешение всплеска.

После таких бредовых интертрепаций интерпретаций, авторами этих же бредовых тредов и постов, разворачиваются дополнительные холивары под заголовками типа "Не работает limit req" или "limit_req работает не так как ожидалось".

В зависимости от структуры шаблона и наполненности веб-страницы сайта, - с учётом загрузки изображений, файлов стилей и шрифтов, а также различных скриптов, можно насчитать от 60 до 200 и более запросов к серверу. Загрузка с сервера в браузер какого-либо файла является отдельным запросом закинутым через установленное с сервером ТСР соединение. Поэтому, более оптимальным значением аргумента burst в большинстве случаев будет в пределах 100-120, а для аргумента rate 3-10 r/s.

P.S. Пост из серии "Как для полных дебилов, в хорошем смысле этого слова".

Рекомендуемый контент

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

АХТУНГ! Все комменты модерасятся модерастом. Мессаги исключительно рекламного или оскорбительного содержания не публикуются, а поэтому злостным спамерам, пранкерам и прочей сетевой нечисти рекомендуем напрасно не тратить своего времени и удовлетворять свои больные фантазии на специализированных Интернет ресурсах! Разумная же критика, замечания, дополнения и хвалебные оды приветствуются, также допускается легкий флуд или троллинг :)


Защитный код
Обновить

Новое на форуме