[AWS EC2]Laravelアプリケーション向けのnginxとphp-fpmの設定　~備忘録~

## はじめに
この記事はプログラミング初学者による備忘録用の記事であり、また、少しでも他の初学者のお役に立てればと思い書いています。

今回は、EC2インスタンス内にインストールしているnginxとphpインストール後、起動させた[php-fpm](https://qiita.com/susuz/items/7715bb97114b5ff6eeb1)の設定をLaravelアプリケーション向けに変更する作業を行なったので、一連の流れと関連の知識を備忘録としてまとめておきたいと思います。

間違いなどがございましたら、ご指摘のほどよろしくお願い致します。
## nignxの設定変更
以前の[記事](https://qiita.com/susuz/items/fcf8dfd1ed999f284a56)では、nginxのインストールと起動を行いましたが、初期設定のままではEC2上に``git clone``したLaravelアプリケーションに対応していません。

従って、Laravelアプリケーション向けに設定を変更する必要があります。

変更の手順と内容は下記の通りです。


### ・viを起動して``/etc/nginx/nginx.conf``を編集する

``ec2-userユーザー``の状態で、下記のコマンドを実行しviを起動させて``/etc/nginx/nginx.conf``を開きます。

```
[ec2-user@ip-###-##-##-## ~]$ sudo vi /etc/nginx/nginx.conf
```

上記のコマンドを実行後、下記のようなファイルの内容が表示されるのでコードを追加・修正していきます。

```/etc/nginx/nginx.conf
# 略

# Load dynamic modules. See /usr/share/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;

# 略

    # Load modular configuration files from the /etc/nginx/conf.d directory.
    # See http://nginx.org/en/docs/ngx_core_module.html#include
    # for more information.
    include /etc/nginx/conf.d/*.conf;

    server {
        listen       80 default_server;
        listen       [::]:80 default_server;
        server_name  _;
        root    /home/username/特定のディレクトリ/public; #===この行を修正(/usr/share/nginx/html;を/home/username/特定のディレクトリ/public;に)

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

        location / {
            #==========追加==========
            try_files $uri $uri/ /index.php?$query_string;
            #=======================
        }

        #==========追加==========
        location ~ \.(css|gif|ico|jpeg|jpg|js|pdf|png|svg|swf|zip|eot|otf|ttf|woff|woff2) {
            expires 1y;
            access_log off;
            log_not_found off;
        }
        #=======================

        #==========追加==========
        location ~ \.php$ {
        fastcgi_pass   unix:/run/php-fpm/www.sock;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include        fastcgi_params;
        }
        #=======================

        error_page 404 /404.html;
            location = /40x.html {
        }

        error_page 500 502 503 504 /50x.html;
            location = /50x.html {
        }
    }   
# 略
}
```
### ・追加、修正内容の説明

**・rootディレクティブ**:

rootディレクティブでは、**ドキュメントルート**(リクエストのルートディレクトリ)を指定します。
今回は、ドキュメントルートを``/home/username(ec2-user以外)/特定のディレクトリ/public;``に修正しました。
usernameは、アプリケーションをgit cloneしたEC2インスタンス上の実行ユーザーが対象となり、それ以下のディレクトリは階層構造によってpublicディレクトリの位置も変わるので、各自修正して下さい。


``/home/username/特定のディレクトリ/public;``を指定した理由は、**Laravelのリクエストの[ライフサイクル](https://readouble.com/laravel/6.x/ja/lifecycle.html)を学ぶと理解できると思います。**

Laravelでは、公式ドキュメントでも説明されている通り、アプリケーションに対するリクエストは全て``public/index.phpファイル``が入り口となります。
従って、**Webサーバ(Apache/Nginx)の設定で、全てのリクエストを``public/index.phpファイル``へ渡すようにする必要があるのです。**

>Sets the root directory for requests. For example, with the following configuration<br>location /i/ {
    root /data/w3;
}
``引用:``[nginx docs root](http://nginx.org/en/docs/http/ngx_http_core_module.html#root)
<br>

**・locationディレクティブ**:

locationディレクティブでは、URIのパス毎の処理を設定します。

locationディレクティブの書式は、``location 条件となるパス {処理}``のように記述します。
``括弧{ }``で囲まれた部分がlocationコンテキスト(**処理**)であり、リクエストURIのパスがlocationディレクティブのパス条件に一致した場合、locationコンテキストに記述した**処理**が適応されます。

>Sets configuration depending on a request URI.<br>The matching is performed against a normalized URI, after decoding the text encoded in the “%XX” form, resolving references to relative path components “.” and “..”, and possible compression of two or more adjacent slashes into a single slash.
``引用:``[nginx docs location](http://nginx.org/en/docs/http/ngx_http_core_module.html#location)

``~条件となるパス~``
パスの条件の評価方法は、**前方一致と正規表現**の2つであり、URIのパスの前に**プレフィックス**を付けることも可能です。
今回の``location /``は、パスの手前に記号がなく、この場合の条件は**前方一致**となります。

どのパスも``/``始まりであるので、``location / {処理}``は、**全てのパスで{}内の処理をする**、という意味になります。

``~使用可能なプレフィックス~``
|プレフィックス|説明|
|:--|:--|
|なし|前方一致|
|^~|前方一致、一致した場合は正規表現の条件を評価しない|
|=|完全一致|
|~|正規表現(大文字・小文字を区別する)|
|~*|正規表現(大文字・小文字を区別しない)|
<br>

**・try_filesディレクティブ**:
try_filesディレクティブは、引数に指定されたパスが存在するか順に調べ、存在すればそれをレスポンスし、無ければ最後に指定されたパスに内部リダイレクトします。

今回は、``try_files $uri $uri/ /index.php?$query_string;``と記述しています。



・``$uri`` ・・・ リクエストされたURIにファイルが存在するかを調べます。
・``$uri/`` ・・・ ファイルが無ければ、リクエストされたURIにディレクトリが存在するかを調べます。
・``/index.php?$query_string`` ・・・ ディレクトリが存在しなければ、index.phpに内部リダイレクトします。

>Checks the existence of files in the specified order and uses the first found file for request processing; the processing is performed in the current context. The path to a file is constructed from the file parameter according to the root and alias directives. It is possible to check directory’s existence by specifying a slash at the end of a name, e.g. “$uri/”. If none of the files were found, an internal redirect to the uri specified in the last parameter is made. 
``引用:``[nginx docs try_files](http://nginx.org/en/docs/http/ngx_http_core_module.html#try_files)
<br>

**・location ~ \.(css|略)**:

```
location ~ \.(css|gif|ico|jpeg|jpg|js|pdf|png|svg|swf|zip|eot|otf|ttf|woff|woff2) {
            expires 1y;
            access_log off;
            log_not_found off;
        }
```
上記の設定では、JavaScriptやCSS、jpgといった静的なファイルをブラウザにキャッシュさせ、アクセスログの出力を停止するようにしています。

静的なファイルに対するアクセスに関しては、PHPに対するアクセスなどと比較してログの必要性が低いと判断したため、nginxのアクセスログを出力しないようにしています。

**・location ~ \.php$**:

```
location ~ \.php$ {
        fastcgi_pass   unix:/run/php-fpm/www.sock;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include        fastcgi_params;
        }
```
上記の設定では、拡張子がphpであるファイルに対して処理内容を設定しています。

``~各設定値の説明~``

**・fastcgi_pass**:
unixソケットかTCPのpassを指定します。
例での値は、``/etc/php-fpm.d/www.conf``のlistenの値で同じpassとして使用されます。 (unixソケット)
<br>
**・fastcgi_index**:
スラッシュ(/)で終わるURIの後に付加するファイル名を設定します。

``例``

```
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /home/www/scripts/php$fastcgi_script_name;
```
上記のような設定では、例えばスラッシュの後ろに``/page.php``を追加した際はSCRIPT_FILENAMEのパラメータが``/home/www/scripts/php/page.php``となり、スラッシュだけの場合は``/home/www/scripts/php/index.php``となり、fastcgi_indexで設定した値(index.php)が使用されます。
<br>
**・fastcgi_param**:
FastCGIサーバーに渡すべきパラメータを設定します。値にはテキスト、変数、およびそれらの組み合わせを含めることができます。

``・SCRIPT_FILENAME``: PHPでスクリプト名を決定するために使用され、PHP-FPMに渡されます。(SCRIPT_FILENAMEパラメータは``/home/www/scripts/php/info/index.php`` と等しい)
**※このパラメータが設定されていない場合、PHP-FPMは空の内容の200 OKを応答し、エラーもしくは警告は出ません。**

``・$document_root``:
現在のリクエストのrootまたはaliasディレクティブの値

``・$fastcgi_script_name``:URI がスラッシュで終わっている場合、URI をリクエストするか、URIにfastcgi_indexディレクティブで設定されたインデックスファイル名をつけてリクエストさせます。
この変数は、 PHPのスクリプト名を決定する SCRIPT_FILENAME と PATH_TRANSLATED パラメータを設定するために使用することができます。

``・include``:
他のファイルから設定を読み込みます。
<br>
**・error_pageディレクティブ**:
error_pageディレクティブには指定したエラーコードが発生したときに表示するページのURIを指定します。

記述内容は、``error_page コード ... [=[レスポンスコード]] uri;``となっています。

例えば、下記のように記述するとサーバのエラーが発生したときに"/50x.html"ページへ内部リダイレクトします。

```
error_page 500 502 503 504 /50x.html;
location = /50x.html {
    root   /usr/share/nginx/html;
}
```



### ・nginxの再起動
nginxの設定ファイルの変更箇所を反映させるため、nginxを再起動します。

```
$ sudo systemctl restart nginx
```

## php-fpmの設定
``ec2-user``ユーザーの状態で下記のコマンドを実行し、``/etc/php-fpm.dディレクトリ``の``www.conf``をviで開いてください。

```
$ sudo vi /etc/php-fpm.d/www.conf
```
上記のコマンドを実行後、下記のようなファイルの内容が表示されるのでコードを修正していきます。

```/etc/php-fpm.d/www.conf
# 略

; Unix user/group of processes
; Note: The user is mandatory. If the group is not set, the default user's group
;       will be used.
; RPM: apache user chosen to provide access to the same directories as httpd
user = username  ;=============修正(apacheをusernameに変更)
; RPM: Keep a group allowed to write in log dir.
group = username ;=============修正(apacheをusernameに変更)

; The address on which to accept FastCGI requests.
; Valid syntaxes are:
;   'ip.add.re.ss:port'    - to listen on a TCP socket to a specific IPv4 address on
;                            a specific port;
;   '[ip:6:addr:ess]:port' - to listen on a TCP socket to a specific IPv6 address on
;                            a specific port;
;   'port'                 - to listen on a TCP socket to all addresses
;                            (IPv6 and IPv4-mapped) on a specific port;
;   '/path/to/unix/socket' - to listen on a unix socket.
; Note: This value is mandatory.
listen = /run/php-fpm/www.sock

# 略

; Set permissions for unix socket, if one is used. In Linux, read/write
; permissions must be set in order to allow connections from a web server.
; Default Values: user and group are set as the running user
;                 mode is set to 0660
listen.owner = newsmemoapp
listen.group = newsmemoapp
listen.mode = 0660
```
### ・修正箇所の説明

```
user = username  ;
group = username ;
```
上記の修正箇所では、アプリケーションを動かす際に権限関係でエラーが発生しないよう、プールのuserとgroupを特定のユーザー名に変更しています。
今回の場合、git cloneしたLaravelアプリケーションは、ec2-user以外の実行ユーザーのホームディレクトリである、``/home/実行ユーザー/ディレクトリ``配下に存在するので、プールのuserとgroupを実行ユーザー名に変更します。(対象のアプリケーションの各ファイル、各ディレクトリの所有者、グループは全て実行ユーザーとなっています。)

```
listen = /run/php-fpm/www.sock
```
上記の修正箇所は、nginxとphp-fpmの通信方法によって内容が変わってきます。

UNIXドメインソケットを利用している場合は、``listen = /run/php-fpm/www.sock``を指定します。
TCPを利用している場合は、``listen = 127.0.0.1:9000``を指定します。

```
listen.owner = newsmemoapp
listen.group = newsmemoapp
listen.mode = 0660
```
上記の修正箇所は、**UNIXドメインソケット**を使用する場合に限り、php-fpm.sockのパーミッションをnginxが読める状態にする為に修正しています。

listen.ownerとlisten.groupの値を実行ユーザーと同じにすることで、php-fpm.sockの所有ユーザ・グループを変更することができます。

>unix ソケットを使う場合に、そのパーミッションを設定します。Linux では、 読み書きアクセス権限を設定しないとウェブサーバーからの接続を受け付けることができません。 多くの BSD 由来のシステムでは、パーミッションにかかわらず接続を受け付けることができます。 デフォルト値: ユーザーとグループは実行しているユーザーと同じ、モードは 0660
``引用:``[PHP docs php-fpm.conf のグローバル設定項目](https://www.php.net/manual/ja/install.fpm.configuration.php#listen-owner)



### ・php-fpmの再起動
``/etc/php-fpm.d/www.conf``の編集が終わったら、設定を反映させるためにphp-fpmを再起動します。

``ec2-userユーザー``の状態で下記のコマンドを実行してください。

```
$ sudo systemctl restart php-fpm
```

続いて、下記のコマンドを追加して下さい。

```
$ ps -o user,group,cmd -e | grep -v grep | grep php-fpm
```
psコマンドで起動中のプロセスを表示します。

下記のようにphp-fpm: pool wwwとなっている行のユーザーとグループが特定のユーザーに変更されていれば大丈夫です。

```
root     root     php-fpm: master process (/etc/php-fpm.conf)
username   username   php-fpm: pool www
username   username   php-fpm: pool www
username   username   php-fpm: pool www
username   username   php-fpm: pool www
username   username   php-fpm: pool www
```

## 終わりに
nginxとphp-fpmの設定を変更後、ブラウザで``http://EC2のパブリックIPアドレス/login``にアクセスしてください。
Laravelアプリケーションのログイン画面が表示されていれば今回の環境構築は問題ありません。


## 補足
### ・ドキュメントルートとは

>ドキュメントルートとは、Webサーバ（HTTPサーバ）やFTPサーバが外部に公開するファイルなどが置かれたディレクトリ（フォルダ）。そのディレクトリにあるファイルと、下の階層にあるファイルやディレクトリが公開される。
``引用:``[ドキュメントルート IT用語辞典](https://e-words.jp/w/%E3%83%89%E3%82%AD%E3%83%A5%E3%83%A1%E3%83%B3%E3%83%88%E3%83%AB%E3%83%BC%E3%83%88.html)

### ・includeディレクティブとは
includeディレクティブを使用することで、**他のファイルから設定の読み込みが可能**となります。
>Includes another file, or files matching the specified mask, into configuration. Included files should consist of syntactically correct directives and blocks.
``引用:``[nginx docs include](http://nginx.org/en/docs/ngx_core_module.html#include)

今回の記事で編集した``/etc/nginx/nginx.conf``では、下記の2箇所で使用されています。

```/etc/nginx/nginx.conf
 # Load modular configuration files from the /etc/nginx/conf.d directory.
    # See http://nginx.org/en/docs/ngx_core_module.html#include
    # for more information.
    include /etc/nginx/conf.d/*.conf; #============includeディレクティブ

    server {
        listen       80 default_server;
        listen       [::]:80 default_server;
        server_name  _;
        root         /home/username/特定のディレクトリ/public;

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf; #============includeディレクティブ
```
このように、includeディレクティブが設定されていることで下記のファイルが読み込まれるようになっています。

```
.
└──etc
    ├── nginx
    │   ├── conf.d
    │   │   └── php-fpm.conf // 対象のファイル
    │   ├── default.d
    │   │   └── php.conf // 対象のファイル
    │   └── nginx.conf
    └── php-fpm.d
        └── www.conf
```




## 参考文献
[nginx docs](https://nginx.org/en/docs/http/ngx_http_core_module.html)
[php-fpm.conf 設定](https://www.php.net/manual/ja/install.fpm.configuration.php)
[Clear Linux* Project Documentation -PHP and PHP-FPM](https://docs.01.org/clearlinux/latest/tutorials/php.html)
[nginx PHP FastCGI の例](https://mogile.web.fc2.com/nginx_wiki/start/topics/examples/phpfcgi/)
[Laravel6.x docs デプロイ](https://readouble.com/laravel/6.x/ja/deployment.html)