nginxの基本設定を改めてちゃんと調べてみた

本記事は<a href="https://qiita.com/advent-calendar/2017/lifull">LIFULL Advent Calendar 2017 - Qiita</a>の17日目の記事です。

nginxの設定を触る機会があり、改めてnginx.confについて調べてみました。
私自身勘違いしていた箇所や、わかっていなかった箇所があり新たな気づきになりました。
※まだ勘違いしているところがあればコメントいただけると嬉しいです。

# nginx.confの構造
nginx.confは以下の様な構造で記述されています。
ファイル自体は``` /etc/nginx/nginx.conf/ ```にあるかと思います。

nginx.confは通常以下の様な形式で書かれています。

``` /etc/nginx/nginx.conf
# 基本ディレクティブ
設定A 設定値1;
設定B 設定値2;

# ブロックディレクティブ
events {
    設定C  設定値3;
          :
}

http {
    server {
        設定D  設定値4;
        location 設定値5 {
            設定E 設定値6;
        }
    }
server {
　　設定E 設定値6;
}
```

設定のことを「ディレクティブ」と呼びます。
``` 設定A 設定値1; ```がディレクティブです。
また、``` events { 設定C  設定値3; } ```という形式で書かれたものは「ブロックディレクティブ」と呼びます。

また、ディレクティブは設定ファイル内のどこにでも記述できるものではなく、ディレクティブ毎に決められたコンテキスト内に記述する必要があります。
つまりブロックディレクティブ内か外か、どのブロックディレクティブ内に記載するかが決まっているという事です。



#mainコンテキスト
ブロックディレクティブの外に記述する基本ディレクティブについて記載していきます。
##user
nginxを起動したときのmaster process以外を起動するユーザを設定できます。
master processはrootユーザーで起動します。

``` user nginx; ```で設定して起動すると以下の様にworker processがnginxで起動している事がわかります。

```
$ ps aux |grep nginx |grep -v grep
root      1006  0.0  0.1  59144  1048 ?        Ss   Dec11   0:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf
nginx     1008  0.0  0.3  59592  3792 ?        S    Dec11   0:00 nginx: worker process
```

##worker_processes
worker_processesはnginxのworkerプロセス数を設定します。
通常はCPUのコア数以下に設定します。イベント駆動のアーキテクチャのため、CPUのコア数より多い数のプロセスを動作させても意味がありません。
今回はEC2のt2.microを使用しているのででコア数は1です。そのため、1を設定するので問題ありません。
また、autoの設定もあります。autoだとコア数を見て自動で設定してくれるようです。
↓ auto設定の時のworker

```
$ ps aux |grep nginx |grep -v grep
root      1006  0.0  0.1  59144  1048 ?        Ss   Dec11   0:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf
nginx     1008  0.0  0.3  59592  3792 ?        S    Dec11   0:00 nginx: worker process
```

worker processは1になっています。

##worker_rlimit_nofile
ファイルディスクリプタの設定になります。ここを設定する事でworkerプロセスが最大に開けるファイル数の制限ができます。
``` ulimit -n ```以上のファイル数を処理できるようになり、「too many open files」問題を回避できます。

```
$ ulimit -n
1024
```

まずはファイルディスクリプタとは何かから。
ファイルディスクリプタはプログラムがアクセスするファイルや標準入出力などをOSが識別するために用いる識別子です。
0~2まではOSは予約済みとなっています。

| 識別番号       | 説明       |
|:--------------|:------------------|
| 0             | 標準入力（stdin）   |
| 1             | 標準出力（stdout）  |
| 2             | 標準エラー出力（stderr） |
| 3~            | ファイルオープンした際に順にディスクリプタが割り当てられる |
	
シェルを書く時によく使う``` command > /dev/null 2>&1 ```の数字部分がファイルディスクリプタに当たります。
上記のコマンドを左から説明をすると

| コマンド | 説明	            |
|:--------|:----------------|
|command  | 任意のコマンド。|
|>        | 標準出力のリダイレクト	|
|/dev/null|Unix系におけるスペシャルファイルの1つ。書き込み内容を全て破棄する|
|>&	|>&{ファイルディスクリプタ}は結果をマージする|

つまり「{command}の標準出力(1)を標準エラー出力(2)にマージして内容を捨てる」ということです。
(場合にもよりますが、何か問題が起きた時に検知できないのでとりあえずで結果を捨てるのはよよろしくないです）

``` 
$ hoge
-bash: hoge: コマンドが見つかりません
$ hoge > /dev/null 2>&1 ＃ 何も出力なし。
```

上記の様にエラーメッセージも何も出力されなくなります。

次にファイルディスクリプタ：3以降がどうなるかです。以下を見るとすぐわかると思います。

```
file =  File.open('/tmp/hoge', 'r')
puts file.fileno # ファイルディスクリプタ  ⇒ 3
file.close

file =  File.open('/tmp/fuga', 'r')
puts file.fileno # ファイルディスクリプタ  ⇒ 3　file.closeをするとファイルディスクリプタは再利用される。


file =  File.open('/tmp/fuga', 'r')
puts file.fileno # ファイルディスクリプタ  ⇒ 4
```

この様にファイルオープンする度に連番で割り振られていきます。
ファイルディスクリプタは連番で採用されていきますが上限があります。
﻿上限はulimitで確認できます。

```
$ ulimit -n
1024
```

ここでworker_connectionsを1024以上に設定する場合は、上限を変更する必要が出てきます。
というのも同時接続数がファイルの上限を超えてしまうため、「too many open files」となってしまうためです。そのため、worker_connectionsもしくはファイルディスクリプタ（worker_rlimit_nofile）を変更する必要があります。

ファイルディスクリプタを変更するときはOS全体で扱えるファイル数を超えないように注意が必要です。確認方法は以下になります。

```
$ cat /proc/sys/fs/file-max
99560
```

##error_log
そのままerror logの出力場所になります。
この設定はhttpやserverブロックなどでも使用することができます。

##pid
PIDを格納するファイルの出力場所の指定になります。
``` ps ```コマンドで確認してしまうのであまり見る事は無い様に思います。


#eventコンテキスト
イベント処理に関するモジュールについての設定項目になります。
##worker_connections
「nginx」全体の接続数です。
私は勘違いしていたのですが「同時接続数 = worker_processes * worker_connections」ではない様です。
worker_rlimit_nofileでも記載しましたがworker_connectionsはファイルディスクリプタ値以上を設定することが出来ないので、必要に応じて変更する必要があります。

##multi_accept
``` multi_accept on; ```にする事でリクエストを同時に受け付けられる様になります。
onにするかはマシンスペックなどとのご相談になると思います。

##use epoll
コネクションの処理方法を記述します。
Linuxカーネル2.6以上の場合はepoll、BSDの場合kqueueとするのが良い様です。
特に指定しなかった場合は自動で選択される様です。

##accept_mutex
onにするとworkerプロセスは順番に新しい接続を受け入れるようになります。
新しい接続の量が少ない場合はworkerプロセスの一部がシステムリソースを無駄にすることになります。
デフォルトではoffとなっています。


#httpコンテキスト
httpに関するモジュールについての設定項目になります。
##server_tokens
エラー画面の時に Nginxのバージョンを表示するかどうかの設定です。
セキュリティ上、バージョンを表示することは好ましく無いのでoffとしておいた方がいいです。

##include /etc/nginx/mime.types;
MIMEタイプと拡張子の関連付けを定義したファイルの読み込みです。
MIMEタイプとは「タイプ名/サブタイプ名」で定義されたデータ形式の指定です
WEBサーバとブラウザはこのMIMEタイプを用いてデータの形式を指定しています。

以下のように定義されています。

```
types {
    text/html                             html htm shtml;
    text/css                              css;
    text/xml                              xml;
    image/gif                             gif;
    image/jpeg                            jpeg jpg;
    application/javascript                js;
    ...
}
```

##default_type
mime.typesで拡張子からMIMEタイプを決定できなかった際に、ここで指定したMIMEタイプが適応されます。デフォルトはtext/plainとなっています。

##access_log
access logの出力先です。
``` log_format ```を使用する事でフォーマットを指定することができます。
またserverコンテキスト内に記載することもできます。

##sendfile
onにする事でカーネルのsendfileを使用します。
ハードディスクio処理とsocket-io処理のバランスを取るため、有効にするのが良さそうです。
デフォルトはoffになっています。

##tcp_nopush
onにする事でレスポンスヘッダとファイルの内容をまとめて送るようになり、少ないパケット数で効率良く送ることができます。

##tcp_nodelay
通常TCPパケット・セグメントの容量いっぱいにデータを格納しようとするため、パケットの送信までにはどうしても待ち時間が発生してしまいます。
onにする事でこの待ち時間を無くすことができ、結果パケット数と送信量を増やすことができます。

##keepalive_timeout
HTTPの接続時間です。

古い記事になってしまうが設定次第でプロキシサーバとバックエンドサーバ、双方の負荷軽減に効果がありそう
<a href="https://qiita.com/toritori0318/items/a85d0b1efbd3ea1fc950">NginxでリバースプロキシをKeepAliveしたときの性能検証 - Qiita</a>
<a href="http://www.nari64.com/?p=579">Nginx の keep-alive の設定と検証 - How old are you?</a>

##gzip
onにすることでレスポンスにgzipを使用するようにでき、サーバー間の転送量を抑えることができます。
``` gzip_types ```でgzipを有効にするファイル形式を指定し、``` gzip_proxied ```で指定しているリクエストタイプに対してのみ有効になります。
また``` gzip_min_length ```を指定すると設定値以下の容量の時は圧縮しないようにできます。小さいサイズの時は圧縮にかかるコストの方が高くなってしまうためです。

nginxの前段にキャッシュサーバやCDNが配置されている際は``` gzip_vary ```を設定する必要があります。
<a href="https://qiita.com/cubicdaiya/items/09c8f23891bfc07b14d3">コンテンツキャッシュとVaryヘッダとnginx - Qiita</a>

##gunzip
onにするとgzipに対応していないブラウザに対しての処理を有効にできます。
``` gzip_disable ```でgzipに対応していないブラウザの時圧縮を無効化することもできます。

#serverコンテキスト
serverに関する設定項目になります。
vhostの設定などを行ったりします。

設定項目は基本的にhttpと同じになります。

##listen
どのポートで受けるかを定義します。
nginxをそのまま使う場合はhttpブロック内に記載しますが、通常はvhostで使用すると思うのでserverブロック内に記載することになると思います。

##server_name
指定したhostでのリクエストの時、設定を適応させるかを設定します。
``` location ```ごとに別のサーバを指定することも可能です。

処理の流れは以下を参照いただくといいと思います。
<a href="http://mogile.web.fc2.com/nginx/http/request_processing.html">nginxがリクエストをどのように処理するか</a>

##root
このserverブロック内で使用するルートディレクトリの指定です。

##try_files
スペース区切りでファイルを記述すると順番にファイルにアクセスしていきます。

##rewrite
リダイレクトをさせたい時に設定します。
正規表現による指定も可能です。

#locationコンテキスト
指定したlocationの場合に設定を適応させることができます。
このpathへのアクセスの場合はこのlocationブロック内の設定を適応させると言うことができます。
記述方法はこちら
<a href="http://www2.matsue-ct.ac.jp/home/kanayama/text/nginx/node36.html">5.2 locationの例</a>

#まとめ
最初にも書いたのですが、私自身勘違いしていた箇所や、わかっていなかった箇所があり新たな気づきになりました。
正しく設定されていないとパフォーマンスへの影響やセキュリティ上よくないことにもなるので理解の上、設定していきたいですね。

ここに記載していない設定項目もまだありますが、その項目を設定する機会があれば書き足していこうと思います。



###参考）
<a href="https://qiita.com/iwai/items/1e29adbdd269380167d2">Nginxのパフォーマンスを極限にするための考察</a>
<a href="https://qiita.com/toshihirock/items/78286fccf07dbe6df38f">ファイルディスクリプタについて理解する</a>
<a href="https://gist.github.com/koudaiii/386eb55a29b1adc19c5e">nginx最大パフォーマンスを出すための基本設定</a>
<a href="https://qiita.com/tukiyo3/items/8124d5066737debdc6bb">メモ: nginxのAccess-Control-Allow-Origin設定 - Qiita</a>