Nginxの流量制限を調べてみた

## Nginxにおいての流量制限とは

アクセスの急増でWebサーバの負荷が高くなった場合、サーバーにアクセスするリクエストの頻度やTCPの接続数を制限するなどをして、Webサービスの処理効率を維持することをNginx流量制限だと考えています。
**Webサービスの処理効率を高めるのではなく、維持すること**

## なぜ流量制限が必要なのか

- 高負荷時でもウェブサーバーの処理効率を維持し、可用性を保つ
- IP制限などによりDoS攻撃からサーバーを守る 
 
## Nginx流量制限の種類
Nginxはリクエストの[処理段階](http://nginx.org/en/docs/dev/development_guide.html#http_phases)に応じて制限機能を提供されています。
PreAccess段階（NGX_HTTP_PREACCESS_PHASE）のHTTPリクエストの処理頻度制限（limit_req）とTCP接続数制限（limit_conn）、あと、Access段階（NGX_HTTP_ACCESS_PHASE）のIP制限があります。

### 1.HTTPリクエスト処理頻度の制限（limit_req）
制限対象（クライアントIPとnginxサーバーなど）ごとのHTTPリクエスの処理頻度を制限します。

- **処理段階:** PreAccess段階（NGX_HTTP_PREACCESS_PHASE）
- **Nginxモジュール：**　[http_limit_req_module](http://nginx.org/en/docs/http/ngx_http_limit_req_module.html)
デフォルトで有効になっています。
- **スコープ：** 全てのworkerプロセス（based on shared butter）
- **アルゴリズム：**leaky buket
![leakyTap-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/920316/79164b7c-fd79-c5b0-4db0-8598e4086e41.png)<br>
このアルゴリズムは上記の図が示しているように、突発なアクセス（Bursty Flow）を一旦バケットに溜め込んで一定の速度（req/sec）で処理していく(Fixed flow)イメージです。
設定した速度に達するとバケットにリクエストが蓄積始めます。この時点からユーザーにレスポンスが遅くなります。ただし、リクエストはもれなく処理されます。バケットが溢れると新しいリクエストの処理はできなくなって事前に定義したエラーがクライアント（ユーザー）に返されます。
- **設定方法**
    - 共有バファーおよびけ制限対象と制限速度を設定する<br>
      Syntax: `limit_req_zone $key zone=$name:$size rate=$rate;`<br>
      &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*$key*は制限対象を定義します。例えば、`$binary_remote_addr`の場合、クライアントのIPを基づいて制限をします。 *$name*は共有バファー名、$sizeは例えば、1M,10Mなど、１Mは16000個IPのアクセス情報を格納できます。*rate*はリクエスト数/秒、r/s、あるいはリクエスト/分 r/m。<br>
      Context: http<br><br>

    - 同時接続数制限<br>
      Syntax: `limit_req zone=＄name [burst=＄number][nodelay];`<br>
      &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*$number*はバケットに溜められるリクエストの数、デフォルトは０、*nodelay*はburst中のリクエストを遅延なくすぐ処理します。<br>
      Context: http<br>
      スコープ: 全てのworkerプロセス（based on shared butter）<br>

    - 制限発生時のログ設定<br>
      Syntax: `limit_req_log_level info|notice|warn|error;`<br>
      Default: `limit_req_log_level error;`<br>
      Context: http, server, location<br>

    - 制限発生時クライアントに返すエラーコード<br>
      Syntax: `limit_req_status code;`<br>
      Default: `limit_req_status 503;`<br>
      Context: http, server, location<br>

    - 設定例（基本）

```
http {
  ...
  limit_req_zone $binary_remote_addr zone=limit_per_ip:1m rate=5r/s;
  server {
    ...
    location = /path {
      limit_req zone=limit_per_ip burst=20 nodelay;
      ...
    }
  }
}
```

### 2.接続数制限（limit_conn）

制限対象（クライアントIP、nginxサーバーなど）ごとの接続数を制限します。ここの接続はTCP接続のことを指しています。

- **処理段階:** アクセス段階（NGX_HTTP_PREACCESS_PHASE）
- **Nginxモジュール：**　[http_limit_conn_module](http://nginx.org/en/docs/http/ngx_http_limit_conn_module.html)
デフォルトで有効になっています。<br>
この制限の有効性は[NGX_HTTP_POST_READ_PHASE](http://nginx.org/en/docs/dev/development_guide.html#http_phases#NGX_HTTP_POST_READ_PHASE)段階にrealipモジュールは正しいエンドユーザーのIPを取れるかどうかに左右されます。
- **スコープ：** 全てのworkerプロセス（based on shared butter）
- **設定方法**
    - 共有バファーおよびけ制限対象の設定する<br>
      Syntax: `limit_conn_zone $key zone=＄name:＄size;`<br>
      Context: http<br>
      *$key*は制限対象を定義します、*＄name*は共有バファー名、*$size*はこれだけのリクエストしか維持できません。

    - 同時接続数制限<br>
      Syntax: `limit_conn zone $number;`<br>
      Context: http, server, location<br>

    - 制限発生時のログ設定<br>
      Syntax: `limit_conn_log_level info|notice|warn|error;`<br>
      Default: `limit_conn_log_level error;`<br>
      Context: http, server, location<br>

    - 制限発生時クライアントに返すエラーコード<br>
      Syntax: `limit_conn_status $code;`<br>
      Default: `limit_conn_status 503;`<br>
      Context: http, server, location

    - 設定例(基本）

```
http {
    ...
    limit_conn_zone $binary_remote_addr zone=limit_per_ip:10m;
    limit_conn_log_level error;
    limit_conn_status 503;
    ...
}
```

### 3.IP制限

IPホワイト・ブラックリストによりアクセスを制限するときに使います。

- **処理段階:** アクセス段階（NGX_HTTP_ACCESS_PHASE）
- **Nginxモジュール：**　[http_access_module](http://nginx.org/en/docs/http/ngx_http_access_module.html)
デフォルトで有効になっています。
- **設定方法:**<br>
  Syntax: `allow address|CIDR|unix: all;`<br>
  Context: http, server, location,limit_except<br>
  -----<br>
  Syntax: `deney address|CIDR|unix: all;`<br>
  Context: http, server, location,limit_except<br>

### 4.注意事項

- limit_connとlimit_reqを同時に設定された場合、limit_reqが優先されます。limit_reqが適用された場合、limit_connの設定がスキップされます。

以上、とりあえず、Nginxの流量制限機能をざっくりみてきました。
実際にlimit_reqとlimit_connの設定を適用する場合、適当な制限数値にたどりつくまではサービス自体の性質などを考慮し、色々チューニングが必要だと思います。

次回は実際に設定し、検証したいかなと思います。
それでは、また！

明日は @yoko-yan さんです！　

# レファレンス
Nginxの１１の処理フェーズ
http://nginx.org/en/docs/dev/development_guide.html#http_phases
https://www.geeksforgeeks.org/leaky-bucket-algorithm/