🎃

NGINXのRate Limitingの設定調査メモ

2023/05/06に公開

Webサーバーで流量制御をしたいとフワッと要望があったので下調べした結果をメモ。
インフラの切り口では、NGINXを使ってRate Limitingを設定するのが適当そうであった。

NGINXのRate Limiting設定

NGINXのconfファイルで以下のような感じで設定する模様。

http {
  #...

  limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;

  server {
    #...

    location /search/ {
      limit_req zone=one;
    }
  }
}

limit_req_zonelimit_reqの2つのディレクティブで設定を行う。

  • limit_req_zone: httpブロックに記述
    流量制御の定義
  • limit_req: serverブロックまたはlocationブロックに記述
    どの定義を使って、流量制御を行うか。また流量制御の挙動を設定する
    後述するburst,nodelayで流量制御の挙動を調整する

Rate Limitingの設定項目と挙動

  1. 設定ファイルの作成
    デフォルトの/etc/nginx/nginx.confでは、httpブロックでinclude /etc/nginx/conf.d/*.conf;とincludeする記述があるので、default.confを作ってlimit_req_zoneとserverブロックを定義する。作成したdefault.confを/etc/nginx/conf.d/default.confに配置してやればよい。

    # limit_req_zone <key> zone=<name>:<size> rate=<rate> r/s or r/m;
    limit_req_zone lrz_key_all zone=lrz_all:50m rate=200r/s;
    
    server {
      listen       80;
      listen       [::]:80;
      server_name  _;
    
      root /var/www/html;
      index index.html index.htm index.nginx-debian.html;
    
      location / {
        limit_req zone=lrz_all burst=1000 nodelay;
        proxy_pass http://localhost:8080;
      }
    
      error_page  404              /404.html;
    
      # redirect server error pages to the static page /50x.html
      error_page   500 502 503 504  /50x.html;
      location = /50x.html {
        root   /usr/share/nginx/html;
      }
    }
    


  1. ざっくりとした設定の解釈

    • limit_req_zone:

      • key: 流量制御の対象
      • zone: 流量制御の定義名
      • rate: 流量制御の程度
        200r/sとかだと1秒間に200件のリクエストを受け付けるという意味合いになる。
    • limit_req

      • zone: 使用する流量制御の定義名
      • burst:
        rate=200r/sとした場合、burstなしでは、5ミリ秒(1000÷200r/s)以内に次のリクエストが来るとそのリクエストは、Rate Limitingによりエラーとされる。burstはキューのようなもので、burstを設定するとリクエストは待機状態となり、5ミリ秒経過後に待機中のリクエストを1つ処理するという挙動をするようである。
        burstの設定数をキューのサイズと見立てれば良いと思う。
      • nodelay:
        burstの設定だけだと、リクエストをrateに応じた間隔でシリアル処理することになる。nodelayを設定することで待機ではなく並列処理するように挙動が変わるようである。
        並列処理の多重度がburstの設定値(※)になることを理解しておくことが必要。並列処理固有の多重度を設定して、残りをキューに溜めておくような設定はできない。
        ※厳密にはburst+1となるようで、+1のスロットをnodelayなしとして使う。

  2. limit_req_zonekey
    流量制御の対象は、$binary_remote_addrとした場合は、特定のアクセス元からの大量アクセスに制限をかけるということになる。
    $binary_remote_addrのような変数値($で開始)ではなく、定数値とするとリクエスト全体が対象となるようである。今回はリクエスト全体を対象に流量制御を考えていく。

  3. limit_reqburstnodelay
    どのような、制御を行いたいかは、burstnodelayの設定が肝となってくるので、burstnodelayの扱いを掘り下げて考える必要がある。

Rate Limitingの設定について

参考にしたBLOGでは、burstnodelayの設定を推奨しており、前節の挙動なのであれば納得はできるが、安直に設定すればいいものではないとも思う。burstnodelayの設定次第では、rateで設定した数値より多数のリクエストを受け付ける場合もあり、それはバックエンドにあるアプリケーションが受け付けた数のリクエストを捌くことができるかどうかという話になる。つまりアプリケーションがリクエストを捌けるかどうかを設定値検討に含めることが肝要である。

  • burst設定+nodelayあり
    burst設定値を大きな値にするとアプリケーション側には大量のリクエストが殺到することになるので、バックエンドにあるアプリケーション側でもburstに見合ったキューの大きさ・流量制御が実装されていることが必須と言える。
    burstをアプリケーション側の都合での処理可能な多重度として扱ってしまうと(burstの値を小さくすると)、リクエストがラッシュした場合、多くのリクエストがRate Limitingで弾かれてしまうことになる。

  • burst設定+nodelayなし
    バックエンドのアプリケーションから見るとリクエストの発生間隔を平準化してくれる。アプリケーションが処理可能なリクエスト量(アプリケーションの処理能力やホストのスペックによる)をもとにrateの設定値を決めていく。
    nodelayなしにするのは、アプリレーション側で流量制御を実装していないアレ(自粛)な場合に使用せざるを得ないってシナリオだろうか。
    別にnodelayなしも悪いわけではない、システムとして処理可能なリクエスト量やレスポンスタイムが目標値を満たせる/許容できる範囲に収まればよいという考えが根底にあるはず。

基本方針としては、burst設定+nodelayありとして、アプリケーションサイドと話し合って設定値を決めていく必要があるなと思いました。
まだドキュメントベースの机上の話なので検証ができたら改訂したいと思いまする。NGINXのインスタンス数も考慮しなくてはならないし、、、

おわりに

NGINXでの流量制御では、リクエストの閾値を設定してアプリケーションサーバー側に過剰な負荷がかからないようにリクエストを弾くものであり、システム全体で見るとNGINXの流量制御だけでは片手落ちでWebサーバーの後ろにあるアプリケーション側でも流量制御の仕組みが必要であると考える。

参考にしたドキュメントとBLOG

https://docs.nginx.com/nginx/admin-guide/security-controls/controlling-access-proxied-http/
https://www.nginx.com/blog/rate-limiting-nginx/

Discussion