こちらは、Nginxアドベントカレンダーの12月10日分の記事です。
Nginxがネイティブで提供しているlimit_reqモジュールは、
対象サイトに対する過度なリクエストを制御する機能を提供します。
使ってみた系の記事は多々あるので、
もう一歩踏み込んでユースケースに沿った実装について言及します。
流量制御のトレンド
従来、流量制御はサイトダウンの保護を目的として導入されるケースが多く、
LBアプライアンスの機能やapacheのMaxConnectionsなどによる制御が中心でした。
ただし、"プレミア商品のWeb限定販売"や"Web限定特別セール"など、
小売の中心がWebへと移ってきている昨今、サイトダウンを目的とした攻撃の制御だけでは不十分です。
上記に加え、転売による利ざやの確保などを目的とした、
「一見すると正常ユーザに見える不正なアクセス」をいかに防ぐか、
これが非常に重要になってきています。
ユースケース
①バックエンド転送の総量を制限したい
まずはこれが基本的なケースです。
:
limit_req_zone key_all zone=z_all:50m rate=300r/s;
limit_req_zone key_sales zone=z_sales:50m rate=100r/s;
:
location / {
location /sales/ {
limit_req zone=z_all burst=2000;
limit_req zone=z_sales burst=2000;
proxy_pass http://192.168.0.110;
}
limit_req zone=z_all burst=2000;
proxy_pass http://192.168.0.110;
}
:
limit_req_zone
: 流量制御設定の定義
limit_req
: 定義した流量制御設定の利用
がイメージに近いと思います。
また、上記のような設定で、バックエンドサーバ全体に対する流量を300rpsに制御しつつ、
/sales/
へのリクエストを100rpsに制御することができます。
②ユーザごとに任意の流量で制限したい
なるべく、全てのユーザに対して均等な機会を提供したいのは、
多くのサイト運営者がお考えになることと思います。
\\それ、Nginxでできます!//
limit_req_zone
ディレクティブは下記のようなフォーマットです。
limit_req_zone <key> zone=<name>:<size> rate=<rate> [sync];
第二引数のkey
ごとに、rate
の値が管理されることになるため、
ユーザごとに一意となるkey
を設定すれば、ユーザごとの流量制御を実現できます。
ユーザのIPアドレスごとに制御したい場合、下記のような設定となります。
1ユーザあたり5r/sで制御する設定となります。
:
limit_req_zone key_all zone=z_all:50m rate=300r/s;
limit_req_zone key_sales zone=z_sales:50m rate=100r/s;
limit_req_zone $binary_remote_addr zone=z_peruser:10m rate=1r/s;
:
location / {
location /sales/ {
limit_req zone=z_all burst=2000;
limit_req zone=z_sales burst=2000;
limit_req zone=z_peruser burst=5;
proxy_pass http://192.168.0.110;
}
limit_req zone=z_all burst=2000;
limit_req zone=z_peruser burst=5;
proxy_pass http://192.168.0.110;
}
:
key
部に$binary_remote_addr
と設定することで、ユーザのIPアドレスの値を元に制御しています。
※ここでは言及しませんが、remote_addrがELBのIPアドレスなどにならないように注意が必要です。
当然、$cookie_jsessionid
と設定することで、Cookieの値を元に制御することも可能です。
③特定のユーザ・拠点からのアクセスは流量制御しない
ユーザごとに流量制御したいけど、自社オフィスからのアクセスの場合流量制御したくないケースなどが該当します。
Requests with an empty key value are not accounted.
マニュアルに上記のような記述があり、
<key>
の値が空の場合は、流量制御が適用されない、と書かれているわけです。
つまり、何らかの条件に基づいて<key>
を空にするような実装を施せばよいので、
下記のような実装によって実現可能です。
:
geo $limit {
default 1;
10.0.0.0/8 0;
192.168.0.0/24 0;
x.x.x.x/32 0;
}
map $limit $limit_req_key {
0 "";
1 $binary_remote_addr;
}
limit_req_zone key_all zone=z_all:50m rate=300r/s;
limit_req_zone key_sales zone=z_sales:50m rate=100r/s;
limit_req_zone $limit_req_key zone=z_peruser:10m rate=1r/s;
:
location / {
location /sales/ {
limit_req zone=z_all burst=2000;
limit_req zone=z_sales burst=2000;
limit_req zone=z_peruser burst=5;
proxy_pass http://192.168.0.110;
}
limit_req zone=z_all burst=2000;
limit_req zone=z_peruser burst=5;
proxy_pass http://192.168.0.110;
}
:
IPアドレスによって$limit
に 0 or 1 を設定し、
$limit
が 1 の場合は$limit_req_key
にIPアドレスを設定しています。
これにより、ユーザごとのアクセス制御と流量制御のホワイトリストを実現しています。
④クエリストリングのキーによって流量制御したい
ビジネスロジックやURLを変更できないが、リクエストの種別を判定して流量制御したいケースもあると思います。
そちらについては、以前ブログで紹介したことがあるので、手前味噌ですが下記をご覧ください。
https://iryond.hatenablog.com/entry/2016/09/16/010856
最後に
Nginxを使った流量制御パターンをいくつか紹介しました。
他にも、conn_limit
モジュールを活用したコネクション数での流量制御パターンや、
アクセス頻度(10reqまではサクサク、それ以降は1分に1回など)に応じた流量制御なども
Nginx単体で実現可能なので、次回以降紹介したいと思います。
Nginx本家のブログでも活用方法に関する紹介があるので、下記などもご覧ください。
本家版:https://www.nginx.com/blog/rate-limiting-nginx/
日本語翻訳版:https://iryond.hatenablog.com/entry/2017/07/08/203025
誤記や訂正依頼、「こんなことできないの?」などありましたらコメントいただければ幸いです。