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


はじめに

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

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

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

nginxとphp-fpm間のイメージ図

まずはざっくりと全体のイメージ図で繋がりを見てみます。

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

通信設定の流れ(ざっくりと説明)

大まかな流れ

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

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

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

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

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

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-fpmphp-fpm.d/zzz-www.confnginx/default.confで使うことになります。

Dockerfile(php)

#略
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)

#略
# nginx config file
COPY ./default.conf /etc/nginx/conf.d/default.conf
COPY ./nginx.conf:/etc/nginx/nginx.conf
#略

COPY命令のディレクトリは構造により異なります。
コンテナ内のファイルシステム上を指すパスは一定です。

接続の設定(nginx)

nginxでは、nginx/default.confnginx/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 のグローバル設定項目

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とする場合、
用途としてはデータ記録など読み書き可能なファイルで、具体例として掲示板のようなものが該当します。

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

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

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

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

おわりに

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

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