nginxとphp-fpmの通信設定について

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

今回はDockerでのnginxコンテナとphp-fpm(php)コンテナの通信設定(**unixドメインソケット**)に関することを調べてみました。

間違いなどがございましたら、ご指摘のほどよろしくお願い致します。

##nginxとphp-fpm間のイメージ図
まずはざっくりと全体のイメージ図で繋がりを見てみます。
![スクリーンショット 2021-03-12 10.35.09（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/70e5e072-5aee-eade-8755-7a7faeb9258b.png)

このようなイメージになるかと思います。(間違っていればご指摘ください)

##通信設定の流れ(ざっくりと説明)
**大まかな流れ**

1.``docker-compose.yml``で名前付きボリュームをマウントする。
unixドメインソケット(php-fpm-socket)のボリュームは app コンテナと web コンテナで共用したいのでマウントします。

2.php-fpm側で``php-fpm.d/zzz-www.conf``、nginx側で``nginx/default.conf``、``nginx/nginx.conf``を使い設定を行います。

**接続方法**
TCPかUNIXドメインソケットのどちらかを選択して接続します。

UNIXドメインソケットは、TCPソケット(INETドメインソケット)よりも``スループット``が優れてるらしい。
下記の記事は参考になります。是非、読んでみてください。


https://qiita.com/kuni-nakaji/items/d11219e4ad7c74ece748

順を追って、もう少し詳しく説明したいと思います。



##docker-compose.ymlでの設定
```docker-compose.yml
version: "3.8"

volumes:
  php-fpm-socket: #unixドメインソケットを使う為に名前付きボリュームを設定
  db-store:

services:
  # php
  app:
    container_name: php
    build: ./infra/docker/php
    volumes:
      - php-fpm-socket:/var/run/php-fpm 
#unixドメインソケット(php-fpm-socket)のボリュームはwebコンテナと共用するのでマウントする
      - ./backend:/work/backend

  # nginx
  web:
    container_name: nginx
    build: ./infra/docker/nginx
    ports:
      - 80:80
    volumes:
      - php-fpm-socket:/var/run/php-fpm
#unixドメインソケット(php-fpm-socket)のボリュームはappコンテナと共用するのでマウントする
      - ./backend:/work/backend
      #略
```
後ほど、**/var/run/php-fpm**を``php-fpm.d/zzz-www.conf``、``nginx/default.conf``で使うことになります。

##Dockerfile(php)
```dockerfile
#略
RUN mkdir /var/run/php-fpm && \ 
#略
COPY ./php-fpm.d/zzz-www.conf /usr/local/etc/php-fpm.d/zzz-www.conf
```
COPY命令のディレクトリは構造により異なります。
コンテナ内のファイルシステム上を指すパスは一定です。
##Dockerfile(nginx)
```dockerfile
#略
# nginx config file
COPY ./default.conf /etc/nginx/conf.d/default.conf
COPY ./nginx.conf:/etc/nginx/nginx.conf
#略
```
COPY命令のディレクトリは構造により異なります。
コンテナ内のファイルシステム上を指すパスは一定です。
##接続の設定(nginx)
nginxでは、``nginx/default.conf``、``nginx/nginx.conf``を使い設定を行います。

・``nginx/nginx.conf``は、php-fpmとunixドメインソケットで通信するために、初期状態から実行ユーザを**www-data**に変更するために用意します。
・ ``nginx/default.conf``はdefault.conf内でphp-fpm.sockのpathを指定する為に用意します。pathを指定することで``php-fpm``との通信が成立します。

```nginx/nginx.conf
# user  nginx;
user  www-data; #デフォルトからこの箇所のみ変更
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}
#略
```
``nginx/nginx.conf``では、php-fpmとunixドメインソケットで通信するために、初期状態から実行ユーザを**www-data**を追記します。

``変更する理由``
nginx+php-fpm環境でphpの処理にUNIXドメインソケットを使っている場合、DebianでPHPをアップグレードすると、「502 Bad Gateway」となって502エラーでハマるのを防ぐ為です。
詳しくは下記リンク先をお読みください。
https://setting-tool.net/nginx-php-fpm-socket-permission

```nginx/default.conf
#略
location ~ \.php$ {
        fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
        #/var/run/php-fpm/ に php-fpm.sock が作られてphpコンテナと通信が成立
    }
```
**unixドメインソケットで接続する場合**

``nginx/default.conf``で、**fastcgi_pass unix: /var/run/php-fpm/php-fpm.sock;**
とpathを指定します。``php-fpm.d/zzz-www.conf``と同じpath指定することで、nginxとphp-fpmでunixドメインメソットを使い通信できるようになります。

##接続の設定(php-fpm)
php-fpmでは、``php-fpm.d/zzz-www.conf``を使い設定を行います。

・``php-fpm.d/zzz-www.conf``は、php-fpm.sockのpathを指定する為と、実行ユーザーをnginx側の実行ユーザーと一致させる為に用意します。


```php-fpm.d/zzz-www.conf
listen = /var/run/php-fpm/php-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0666
```

``listen`` 
 FastCGIリクエストを受け入れるアドレスを設定、nginxと通信(接続)するためのUNIXドメインソケットのpathを指定します。fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;と同じpathを指定をすることで通信できるようになります。このオプションは、必須であり、 'ip.add.re.ss:port', 'port', '/path/to/unix/socket' 形式の構文が使えます。 

>
``参考:``[php manual | php-fpm.conf のグローバル設定項目](https://www.php.net/manual/ja/install.fpm.configuration.php)
>


``listen.owner = www-data``
 unix ソケットを使う場合に、そのパーミッションを設定します。 読み書きアクセス権限(ユーザー)をnginxの実行ユーザーと同一設定しないとウェブサーバーからの接続を受け付けることができません。

``listen.group = www-data``
同様にnginxの実行ユーザーと統一させる。

``注意``
**listen.ownerとlisten.groupで、nginxの実行ユーザーと別のユーザーを指定した場合**
例
``listen.owner = nginx``
``listen.group = nginx``
下記のように、存在していないユーザーが設置されてますよとエラーがでます。

```
$ docker-compose logs

php    | [12-Mar-2021 11:57:31] ERROR: [pool www] cannot get uid for user 'nginx': Success (0)
php    | [12-Mar-2021 11:57:31] ERROR: [pool www] cannot get uid for user 'nginx': Success (0)
php    | [12-Mar-2021 11:57:31] ERROR: FPM initialization failed
php    | [12-Mar-2021 11:57:31] ERROR: FPM initialization failed
```



``listen.mode = 0666``
これがないとphp側でエラーが出ます。

```
$ docker-compose logs

php    | [14-Mar-2021 09:54:34] ERROR: [/usr/local/etc/php-fpm.d/zzz-www.conf:5] unknown entry '# listen.mode'
php    | [14-Mar-2021 09:54:34] ERROR: Unable to include /usr/local/etc/php-fpm.d/zzz-www.conf from /usr/local/etc/php-fpm.conf at line 5
php    | [14-Mar-2021 09:54:34] ERROR: failed to load configuration file '/usr/local/etc/php-fpm.conf'
php    | [14-Mar-2021 09:54:34] ERROR: FPM initialization failed
```

**パーミッションの設定**
WEBサイトなどを見ているユーザは、第三者です。
WEBページとして公開するHTML文書などは、「読み取り」 を許可する必要がありますが、CGIのファイルなど直接呼び出されるプログラムには、「書き込み」を許可しなければならない場合があります。
従って、各ユーザーにどこまで権限を与えるのかを考え、パーミッションを変更する必要があります。

今回のように0666とする場合、
用途としてはデータ記録など読み書き可能なファイルで、具体例として掲示板のようなものが該当します。

パーミッションに関することは下記記事が分かりやすかったです。

https://qiita.com/shisama/items/5f4c4fa768642aad9e06




**``zzz-www.conf``というファイル名の理由**
これは 公式イメージが``zz-docker.conf``でlisten設定を破棄していることに対抗するためらしいです。後に読ませることでlisten設定が残るので、辞書順で後ろに来るように``zzz-www.conf``というファイル名にするらしいです。

詳しくは下記記事を読んでみてください。

>
fpm の設定ファイルを``zzz-www.conf``としていますが、これは 公式イメージが zz-docker.conf で listen 設定をぶっ潰しているのに対抗するためです。
後に読まれたものが勝つので。どう見ても暫定回避のような構造ですが仕方ありません。
>

https://qiita.com/hidekuro/items/46c00dec350c4a37fbb1#php

##おわりに
次はTCPやUNIXドメインソケットについて、もう少し詳しく学習してみようかなと思います。


最後まで読んでくださり誠にありがとうございました。