Nginxメモ

# Nginxメモ

## 目次

<!-- TOC -->

- [Nginxメモ](#nginx%E3%83%A1%E3%83%A2)
    - [目次](#%E7%9B%AE%E6%AC%A1)
    - [Nginx概要](#nginx%E6%A6%82%E8%A6%81)
        - [アーキテクチャの種類](#%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3%E3%81%AE%E7%A8%AE%E9%A1%9E)
        - [思想の違い](#%E6%80%9D%E6%83%B3%E3%81%AE%E9%81%95%E3%81%84)
    - [基本構文](#%E5%9F%BA%E6%9C%AC%E6%A7%8B%E6%96%87)
        - [ディレクティブ・コンテキスト](#%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%86%E3%82%A3%E3%83%96%E3%83%BB%E3%82%B3%E3%83%B3%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88)
        - [変数・文字列・正規表現](#%E5%A4%89%E6%95%B0%E3%83%BB%E6%96%87%E5%AD%97%E5%88%97%E3%83%BB%E6%AD%A3%E8%A6%8F%E8%A1%A8%E7%8F%BE)
    - [基本的な設定](#%E5%9F%BA%E6%9C%AC%E7%9A%84%E3%81%AA%E8%A8%AD%E5%AE%9A)
        - [基本](#%E5%9F%BA%E6%9C%AC)
        - [仮想ホスト](#%E4%BB%AE%E6%83%B3%E3%83%9B%E3%82%B9%E3%83%88)
        - [リバースプロキシ](#%E3%83%AA%E3%83%90%E3%83%BC%E3%82%B9%E3%83%97%E3%83%AD%E3%82%AD%E3%82%B7)
    - [参考](#%E5%8F%82%E8%80%83)

<!-- /TOC -->

## Nginx概要

### アーキテクチャの種類

- サーバにはfork型(プロセス駆動型)とselect型(イベント駆動型)がある

- fork型はクライアントとの一対一を想定したサーバを増殖(fork)させることで多数のクライアントに対応する

- select型は元からクライアントとの一対多を想定したサーバを用意し、多数のクライアントを順にさばいていく

- 前者のfork型では分身（リソース）の制限からあまりにも多数のクライアントには対応できないが、nginxはselect型であるためそれを解決する

- select型とはいえnginxも全く分身を作らないわけでは無く、ワーカーという分身をコア数程度は作る


### 思想の違い

- nginxの設定ファイルはURLに対応するような記述をする

- URLは　`Request URL: https://www.kyoto-u.ac.jp/ja`　のように
`https`(スキーム名)、`www.kyoto-u.ac.jp`(ホスト・ドメイン)、`/ja`(サブディレクトリ)等からなる、上位→下位の入れ子構造をしている

- URLに対応するよう作られたnginxの設定も、同様にHTTP,server,locationを入れ子にしている

## 基本構文

### ディレクティブ・コンテキスト

- 設定ファイルはnginx.conf

- 設定ファイルの中身は**ディレクティブ**と呼ばれる設定項目の集合。書式は
 `ディレクティブ名　値;`

- 適用範囲を明確にするため、複数のディレクティブをブロック`{}`で囲むことがある（当然ブロックが入れ子状になることもある）
上位のブロックで適用された設定は下位のブロックにも受け継がれる（継承）。こうしたブロックを**コンテキスト**と呼ぶ

- ディレクティブの末尾はセミコロン`;`

コンテキストの例

~~~nginx

server {　#serverコンテキスト
    listen 80; #listenディレクティブ
    server_name test.matsue-ct.ac.jp; #server_nameディレクティブ
}

~~~

- [コンテキストと呼ばれるものには何種類かあるが](https://www.codeflow.site/ja/article/understanding-the-nginx-configuration-file-structure-and-configuration-contexts)
良く目にするのは

1. グローバルコンテキスト
2. eventコンテキスト 
3. Http
    1. Server
        1. Location
            1. Nested Location (入れ子になったLocation)
            2. If in location (Location内のIf)
            3. limit_except
            
4. If

- グローバルコンテキストだけは`{}`で囲まない特別なコンテキストで、userディレクティブ、worker_prosessディレクティブ、error_logディレクティブ等を内包する。そして設定ファイルの最上部に書く

- eventコンテキストは設定ファイルに必ず必要で、1つの設定ファイルに1つのeventコンテキストと決まっている（中身が空でも置く）

### 変数・文字列・正規表現

- nginxには組み込み変数として様々な変数が用意されている。
`$変数名`とすることで任意の変数にアクセスできる

-  nginxで文字列を表す際、そのまま書いても、ダブルクオテーション・シングルクオテーションで囲っても認識される。
後者はセミコロンや空白などを文字列として表したいときに有用

- 正規表現（PCRE）については[こっちで](https://murashun.jp/blog/20190215-01.html)

## 基本的な設定

ケーススタディ的に見ていく

### 基本

~~~nginx

worker_processes 1;

events {}

http {
    include mime.types;
    access_log    /var/log/nginx_access.log;
    error_log     /var/log/nginx_error.log  info;

    server {
        listen 80;
        server_name net-0.matsue-ct.ac.jp;
        index index.html index.htm;
        location  /  {
            root    /usr/local/www/nginx;
        }
    }
}

~~~

- `error_log`
    - エラーログの出力先を指定
- `access_log` 
    - アクセスログの出力先を指定
http, server, location, If in location, limit_except などの コンテキストで指定できる.
コンテキスト毎に異なるファイルに出力可能

- `include`
    - 指定したファイルを該当箇所に挿入する。
ただしパスはconfファイルからの相対パス。
可読性を上げたり、設定の再利用を容易にする
- `listen`
    - 受信するアドレスとポート番号の指定

- `server_name`

    - `server_name サーバ名　[サーバ名2] [サーバ名3]...`
HTTPリクエストヘッダ内中のHostパラメータにある名前と、ここに列挙したいずれかの 名前にヒットした場合、
このディレクティブが置かれた server コンテキストが 適用される。

    - 仮想ホストを建てる際に必須のディレクトリ

- `index`

    - インデックス――――ファイル名を指定せずにディレクトリだけ指定されてアクセスされた時に返す用のファイルを指定する

    - server_nameディレクティブのように複数指定が可能。先頭から順に検索される

- `location`

    - プロトコルレベル（httpコンテキスト）、サーバレベル（serverコンテキスト）ときて、ファイルの場所を指定するサブディレクトリレベルでの振り分けを行うコンテキスト

    - `location [修飾子] サブディレクトリ`で指定したサブディレクトリに一致したアクセスが来ると、コンテキスト中の処理を行う

    - サブディレクトリとの一致については優先規則がある

    - 修飾子によってURIをlocationにマッチさせる方法が変わる

    >=	完全一致
	~   対正規表現 (大文字小文字を区別)
    ~*  対正規表現 (大文字小文字を区別しない)
    ^~  先頭一致 (マッチした時点で終了)
    (修飾子なし)	先頭一致 (後方参照あり)
    

- `root`

    - ドキュメントルートのパスを指定

    - あるルーティング設定をファイルサーバとして割り振りたい時活躍する
    
    - サーバ全体にファイルサーバの役割を任せるなら
    次のようにserverコンテキストに置いて下位に継承させるのが良い

```nginx

    http {
    server {
        server_name example.com;
        root /home/test/webapp/public/;  
        location /icons/ { }
        location /musics/ { }
    }
}

```

### 仮想ホスト

serverコンテキストを並列にすることで実現できる
例えば次のようになる

~~~nginx
server {
    listen 80;
    server_name www.hoge.jp  w3.hoge.jp;
    ...
}
server {
    listen 80;
    server_name www2.hoge.jp;
    ...
}
~~~

- HTTPリクエストのHOSTパラメータとserver_nameをマッチさせるが、もしそのパラメータが存在しない場合空白`""`とみなされる

- もしこれらの server_name と一致 しなかった場合には、デフォルトのサーバでリクエストは処理される。 デフォルトのサーバは、最初に定義されているサーバ、`www.hoge.jp`となる

- もし、デフォルトサーバを一番目のサーバではなく、指定したサーバで 処理させたい場合には、default_server パラメータを listen ディレクティブ に用いることが出来る。

~~~nginx
    server {
        listen 80  default_server;
        server_name www2.hoge.jp;
        ...
~~~

### リバースプロキシ

http_proxy_moduleに備わっているディレクティブを活用する。rewriteモジュールも関係するらしいがここでは触れない

- `proxy-pass`
    
    - リバースプロキシを実現するには欠かせないディレクティブ

    - `/back/`へのアクセスを`http://backend/back`として backendサーバに中継する場合

    ~~~nginx

        location /back/ {
            proxy_pass  http://backend;
        ...
        }

    ~~~
    - プロトコル、サーバ名で指定した場合には、もともとのサブディレクトリを含んだURLが 引き渡されている

    - プログラムを状態稼働させておいてlisten状態にし、リクエストを転送するという使い方もある。
    その場合proxy_passには `http://127.0.0.1:8080;`を指定することも多い

    - reverse_passがサブディレクトリまで含んだURIを指定していた場合どうなるか

    ~~~nginx

        location /back/ {
            proxy_pass  http://backend/home/;
            ...
        }

    ~~~

    - `/back/` へのURI は、proxy_pass ディレクティブで指定した `http://backend/home/` に置き換わる。


    - [proxy_passに与えるURLにトレイリングスラッシュが存在するかどうかで挙動が違う](https://kuroeveryday.blogspot.com/2019/03/effect-of-trailing-slash-on-nginx-reverse-proxy.html)らしいので注意
    正直この辺りは文献によって違っている気がする。一対一の対応にしたいのであればlocationディレクティブに関してもproxy-passディレクティブに関しても、指定するURIにはトレイリングスラッシュをつけるのが無難か
    - 公式ドキュメントに

     >When location is specified using a regular expression, and also inside named locations.In these cases, should be specified without a URI. 

    とある通り、正規表現で指定したlocationの場合、直前の例で見たサブディレクトリを含んだ指定方法は推奨されていない


- `proxy_set_header`

    - プロキシサーバに送られるリクエストヘッダのフィールドを再定義、あるいは追加する

    - 書式は`proxy_set_header  フィールド名  値;`

    - デフォルトで以下の2つが定義済み

    ~~~nginx
        proxy_set_header  Host  $proxy_host;
        proxy_set_header  Connection  close;

    #$proxy_host
    #プロキシサーバ名
    ~~~~
    

    - `proxy_set_header  Host  $host;`のように追加すると、プロキシするサーバが仮想サーバである場合にサーバ名を利用できたりして便利

- `upstream`

    - サーバグループを定義する。負荷を分散させたいとき等プロキシサーバを複数用意したいときに使う

    - 書式は

    `
    upstream  グループ名  {     
        server サーバ名　サーバのドメイン   [パラメータ]  
        ...
    } 
    `

    - 例としては次のようになる
        
    ```nginx
        upstream  ourserver {
            server net-1.cc.matsue-ct.ac.jp   weight=3; #weightパラメータは優先度の重み付けを決定する
            server net-2.cc.matsue-ct.ac.jp   weight=2;
            server net-3.cc.matsue-ct.ac.jp;
            server net-4.cc.matsue-ct.ac.jp   backup;　#backupパラメータは他の全てのサーバがdownしているときのみ該当サーバが動くよう設定する
        }
        server {
            location / {
                proxy_pass  http://ourserver;
            }
        }
    ```

    ## 参考
    [Nginx入門](http://www2.matsue-ct.ac.jp/home/kanayama/text/nginx/all.html)
    [基本的な正規表現一覧](https://murashun.jp/blog/20190215-01.html)
    [バーチャルホスト（仮想ホスト）とは - IT用語辞典 e-Words](http://e-words.jp/w/%E3%83%90%E3%83%BC%E3%83%81%E3%83%A3%E3%83%AB%E3%83%9B%E3%82%B9%E3%83%88.html)
    [ディレクトリインデックス(DirectoryIndex)](https://www.adminweb.jp/apache/docroot/index2.html)
    [Nginxのlocationとproxy_passの末尾スラッシュによる挙動の違いを理解する](https://kuroeveryday.blogspot.com/2019/03/effect-of-trailing-slash-on-nginx-reverse-proxy.html)
    [Module ngx_http_proxy_module](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass)