1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

docker で nginx + php-fpm の構成で UNIX ソケットで連携させる

Posted at

先日、作成した nginx + php-fpm 環境は TCP で連携させていた。この実行環境で nginx と php-fpm 間を UNIX Socket で連携できるようにする。

手順の流れ

  1. php-fpm を UNIX ソケットで listen させるには
  2. php-fpm を UNIX ソケットで連携させる dokcer を用意する
  3. コンテナを稼働させて、設定の反映を確認する

php-fpm を UNIX ソケットで listen させるには

大まかには次の設定が必要になる。

  1. php-fpm イメージに登録されているユーザーを nginx のイメージに追加する
  2. php-fpm への接続を TCP から UNIX Socket に変更する設定ファイルを追加する
  3. nginx が php-fpm に接続する際の設定ファイルを追加する

php-fpm イメージに登録されているユーザーを nginx のイメージに追加する

php-fpm イメージに登録されているユーザーを nginx イメージに追加する理由は、php-fpm の UNIX ソケットを操作する権限を追加する必要があるためだ。この権限を追加しないと、nginx からのリクエストを php-fpm に渡すことができない。

php-fpm コンテナを起動し、php-fpm イメージに登録されているユーザーを確認する

$ docker run --detach --net=sample_nw --name php-sample php/sample:20220327
8ffaac4a194dbfee7cd214f4a84fb613b3dbc929dca0f46305d4550f9129acda
$ docker ps
CONTAINER ID   IMAGE                 COMMAND                  CREATED          STATUS          PORTS      NAMES
8ffaac4a194d   php/sample:20220327   "docker-php-entrypoi…"   31 seconds ago   Up 29 seconds   9000/tcp   php-sample
$ docker exec -i -t 8ffaac4a194d /bin/sh

# cat /etc/passwd | grep nginx
# cat /etc/passwd | grep www-data
www-data:x:82:82:Linux User,,,:/home/www-data:/sbin/nologin
/var/www/html # id www-data
uid=82(www-data) gid=82(www-data) groups=82(www-data),82(www-data)

WEB サーバー関連のユーザーとして、uid=82(www-data) gid=82(www-data) groups=82(www-data),82(www-data)が登録されているので、このユーザーを nginx のイメージに登録する。

nginx イメージに登録されているユーザーを確認する

nginx のコンテを起動して、登録ユーザーを確認する。

www_data グループは登録されているが、www-data ユーザーは登録されていない。php イメージには、www-data のグループとユーザー登録されているので、nginx のイメージには www-data ユーザを登録する。

$ docker ps
CONTAINER ID   IMAGE                     COMMAND                  CREATED          STATUS          PORTS                NAMES
95a89d5cac6d   php-socket_nginx-sample   "/docker-entrypoint.…"   18 seconds ago   Up 17 seconds   0.0.0.0:80->80/tcp   php-socket_nginx-sample_1
d82e7e343c79   php-socket_php-sample     "docker-php-entrypoi…"   19 seconds ago   Up 17 seconds   9000/tcp             php-socket_php-sample_1
$ docker exec -i -t bde24e5cbe3d /bin/sh
# cat www-data /etc/group
www-data:x:82:www-data
# cat nginx /etc/group
nginx:x:101:nginx
# grep www-data /etc/passwd
# grep nginx /etc/passwd
nginx:x:101:101:nginx:/var/cache/nginx:/sbin/nologin
# id nginx
uid=101(nginx) gid=101(nginx) groups=101(nginx),101(nginx)
# head /etc/nginx/nginx.conf

user  nginx;
worker_processes  auto;

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


events {
    worker_connections  1024;
# exit

www-data と nginx のグループは登録されているが、ユーザーが登録されているのは、nginx ユーザーだけなのが上記で確認できた。

また、/etc/nginx/nginx.conf に登録されている nginx プロセスのユーザーは nginx で登録されているので、php-fpm の UNIX ソケットを操作する権限を付与するために、/etc/nginx/nginx.conf の設定を変更する。

php-fpm への接続を TCP から UNIX Socket に変更する設定ファイルを追加する

php-fpm がリクエストを待ち受ける設定ファイルは、下記のディレクトリ下にある。

$ docker run --detach --net=sample_nw --name php-sample php/sample:20220327
8ffaac4a194dbfee7cd214f4a84fb613b3dbc929dca0f46305d4550f9129acda
$ docker ps
CONTAINER ID   IMAGE                 COMMAND                  CREATED          STATUS          PORTS      NAMES
8ffaac4a194d   php/sample:20220327   "docker-php-entrypoi…"   31 seconds ago   Up 29 seconds   9000/tcp   php-sample
$ docker exec -i -t 8ffaac4a194d /bin/sh
# ls -l /usr/local/etc/php-fpm.d/
total 56
-rw-r--r--    1 root     root           357 Mar 23 19:57 docker.conf
-rw-r--r--    1 root     root         20876 Mar 23 19:57 www.conf
-rw-r--r--    1 root     root         20876 Mar 23 19:56 www.conf.default
-rw-r--r--    1 root     root            45 Mar 23 19:57 zz-docker.conf
# cat /usr/local/etc/php-fpm.d/zz-docker.conf
[global]
daemonize = no

[www]
listen = 9000

上記の設定ファイル /usr/local/etc/php-fpm.d/zz-docker.conf はなかなかの曲者で、Dockerfile の COPY や docker-compose.yaml で、ファイルを利用して UNIX ソケットを listen する設定に置き換えても、変更点が反映されない

調べてみると、公式の php-fpm イメージの Dokcerfile の記述で、ユーザーの変更を公式の Dockerfile が上書きしていたことが原因だった。

	} | tee php-fpm.d/docker.conf; \
	{ \
		echo '[global]'; \
		echo 'daemonize = no'; \
		echo; \
		echo '[www]'; \
		echo 'listen = 9000'; \
	} | tee php-fpm.d/zz-docker.conf

参考

では、php-fpm を UNIX ソケットで listen させるにはどうしたら良いのかというと、

  • php-fpm の設定は、後から読み込まれる設定で上書きされる
  • 設定ファイルは、ファイル名の昇順で読み込まれる
  • この仕様を利用して、/usr/local/etc/php-fpm.d/zz-docker.conf よりも後に設定ファイルを読み込ませて、TCP で listen する設定を、UNIX ソケットで listen する設定で上書きする

ことが必要になる。

この回避策を記載している WEB ページはいくつかあったが、上記の理由は記載されていなかった。そのため、ファイル名を socket.conf にして設定変更を試みても反映されない理由がわからず、原因の確定に時間がかなりかかった。

php-fpm を UNIX ソケットで連携させる dokcer を用意する

docker は先日、作成した設定を流用する。

前回作成した docker の設定を複製する

作成した docker の設定が下記ディレクトリ $HOME/works/docker-sample/php-tcp にあるとする。それを複製する。

$ pwd
$HOME/works/docker-sample
$ ls -l
total 0
drwxr-xr-x  5 centipede  centipede  160  3 27 13:36 php-tcp
$ cp -r php-tcp
$ ls -l
total 0
drwxr-xr-x  7 centipede  centipede  224  3 28 15:19 php-socket
drwxr-xr-x  5 centipede  centipede  160  3 27 13:36 php-tcp

docker-compose.yaml を編集する

php-fpm が生成する UNIX ソケットを、nginx のコンテナから参照できるようにする。

$ cp -p docker-compose.yaml docker-compose.yaml.orig
$ vim docker-compose.yaml
$ cat docker-compose.yaml
version: '3'

# nginx と php-fpm のコンテナ間で共有されるボリューム名を設定
volumes:
  php-fpm-sock:

services:
  php-sample:
    restart: always
    build:
      context: .
      dockerfile: ./php/Dockerfile
    volumes:
      # 共有されるボリュームを次のディレクトリにマウントさせる
      - php-fpm-sock:/var/run/php-fpm

  nginx-sample:
    restart: always
    ports:
      - 80:80
    build:
      context: .
      dockerfile: ./nginx/Dockerfile
    volumes:
      # 共有されるボリュームを次のディレクトリにマウントさせる
      - php-fpm-sock:/var/run/php-fpm
      # ngixn のプロセスを実行するユーザーを変更するために、設定ファイルを置き換える
      - ./nginx/settings/nginx.conf:/etc/nginx/nginx.conf
    depends_on:
      - php-sample

変更点は次の通り。

$ diff -uw docker-compose.yaml.orig docker-compose.yaml
--- docker-compose.yaml.orig	2022-03-27 13:51:28.000000000 +0900
+++ docker-compose.yaml	2022-03-28 15:20:51.000000000 +0900
@@ -1,11 +1,16 @@
 version: '3'

+volumes:
+  php-fpm-sock:
+
 services:
   php-sample:
     restart: always
     build:
       context: .
       dockerfile: ./php/Dockerfile
+    volumes:
+      - php-fpm-sock:/var/run/php-fpm

   nginx-sample:
     restart: always
@@ -14,3 +19,8 @@
     build:
       context: .
       dockerfile: ./nginx/Dockerfile
+    volumes:
+      - php-fpm-sock:/var/run/php-fpm
+      - ./nginx/settings/nginx.conf:/etc/nginx/nginx.conf
+    depends_on:
+      - php-sample

nginx の Dockerfile を変更する

php-fpm の UNIX ソケットを操作するために、nginx のイメージにユーザーを追加する。

この時に、php のイメージに登録されている www-data ユーザーのユーザー ID と一致させること。今回は ID が 82 だったので、ユーザー ID が 82 をもつ www-data ユーザーを nginx のイメージに作成する。

$ cp nginx/Dockerfile nginx/Dockerfile.tcp
$ vim nginx/Dockerfile
$ cat nginx/Dockerfile
FROM nginx:1.21.6-alpine

# add nginx group and nginx user
RUN adduser -S www-data -G www-data -u 82 \
    && echo "www-data ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers \
    && echo 'www-data:www-data' | chpasswd

COPY nginx/settings/default.conf /etc/nginx/conf.d/default.conf

変更点は次の通り。

$ diff -uw nginx/Dockerfile.bak nginx/Dockerfile
--- nginx/Dockerfile.tcp	2022-03-27 17:50:49.000000000 +0900
+++ nginx/Dockerfile	2022-03-28 15:22:08.000000000 +0900
@@ -1,3 +1,8 @@
 FROM nginx:1.21.6-alpine

-COPY settings/default.conf /etc/nginx/conf.d/default.conf
+# add nginx group and nginx user
+RUN adduser -S www-data -G www-data -u 82 \
+    && echo "www-data ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers \
+    && echo 'www-data:www-data' | chpasswd
+
+COPY nginx/settings/default.conf /etc/nginx/conf.d/default.conf
\ No newline at end of file

UNIX ソケットを介して nginx と php-fpm の連携するように変更をする

nginx を実行するユーザーを nginx から www-data に変更するために、変更を加えた nginc.conf を用意する。

$ touch nginx/settings/nginx.conf
$ vim nginx/settings/nginx.conf
$ cat nginx/settings/nginx.conf
# change nginx user to www-data
user  www-data;
worker_processes  auto;

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


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf;
}

php-fpm の UNIX ソケットとやりとりするように、default.conf の設定を変更する。php-fpm の UNIX ソケットは、/var/run/php-fpm/php-fpm.sock に生成する。

$ cp nginx/settings/default.conf nginx/settings/default.conf.orig
$ vim nginx/settings/default.conf
$ cat nginx/settings/default.conf
server {
    listen 80;
    root /usr/share/nginx/html;

    location / {
        index          index.php index.html index.htm;
        fastcgi_pass   unix:/var/run/php-fpm/php-fpm.sock;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include        fastcgi_params;
    }
}

変更点は次の通り。

$ diff -uw nginx/settings/default.conf.orig nginx/settings/default.conf
--- nginx/settings/default.conf.orig	2022-03-27 18:00:38.000000000 +0900
+++ nginx/settings/default.conf	2022-03-27 18:31:28.000000000 +0900
@@ -4,7 +4,7 @@

     location / {
         index          index.php index.html index.htm;
-        fastcgi_pass   php-sample:9000;
+        fastcgi_pass   unix:/var/run/php-fpm/php-fpm.sock;
         fastcgi_index  index.php;
         fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
         include        fastcgi_params;

php-fpm の Dockerfile を変更する

php-fpm が TCP 9000 ポートを listen させる設定 /usr/local/etc/php-fpm.d/zz-dcoker.conf を読み込んだ後に、その設定を UNIX ソケットを listen する設定に上書きするために、ファイル名 /usr/local/etc/php-fpm.d/zz-www.conf を php-fpm のイメージに取り込ませる。

$ cp php/Dockerfile php/Dockerfile.tcp
$ vim php/Dockerfile
$ cat php/Dockerfile
FROM php:8.1.4-fpm-alpine

RUN cp /usr/local/etc/php/php.ini-production /usr/local/etc/php/php.ini

COPY php/settings/php.ini /usr/local/etc/php/conf.d/php.ini
# import the conf file ordred by file name to overwrite listening TCP port setting to unix socket
COPY php/settings/php-fpm.d/zz-www.conf /usr/local/etc/php-fpm.d/zz-www.conf

COPY php/src /usr/share/nginx/html

変更点は次の通り。

$ diff -uw php/Dockerfile.tcp php/Dockerfile
--- php/Dockerfile.tcp	2022-03-27 17:51:59.000000000 +0900
+++ php/Dockerfile	2022-03-28 15:36:56.000000000 +0900
@@ -1,5 +1,9 @@
 FROM php:8.1.4-fpm-alpine

 RUN cp /usr/local/etc/php/php.ini-production /usr/local/etc/php/php.ini
+
 COPY php/settings/php.ini /usr/local/etc/php/conf.d/php.ini
+# import the conf file to overwrite listening TCP port setting to unix socket
+COPY php/settings/php-fpm.d/zz-www.conf /usr/local/etc/php-fpm.d/zz-www.conf
+
 COPY php/src /usr/share/nginx/html

コンテナを稼働させて、設定の反映を確認する

念のため、現在のディレクトリを確認する。

$ pwd
$HOME/works/docker-sample/php-socket
$ ls -l
$ ls -l
total 16
-rw-r--r--  1 centipede  centipede  471  3 28 15:20 docker-compose.yaml
-rw-r--r--  1 centipede  centipede  248  3 27 13:51 docker-compose.yaml.orig
drwxr-xr-x  6 centipede  centipede  192  4  3 18:50 nginx
drwxr-xr-x  7 centipede  centipede  224  3 28 15:20 php
drwxr-xr-x  2 centipede  centipede   64  3 28 13:29 src
$ tree
$ tree
.
├── docker-compose.yaml
├── docker-compose.yaml.orig
├── nginx
│   ├── Dockerfile
│   ├── Dockerfile.tcp
│   └── settings
│       ├── default.conf
│       ├── default.conf.orig
│       └── nginx.conf
└── php
    ├── Dockerfile
    ├── Dockerfile.tcp
    ├── settings
    │   ├── php-fpm.d
    │   │   └── zz-www.conf
    │   └── php.ini
    └── src
        └── index.php

6 directories, 12 files

コンテナを稼働させる。

$ docker-compose up -d
$ docker ps
CONTAINER ID   IMAGE                     COMMAND                  CREATED      STATUS      PORTS                NAMES
95a89d5cac6d   php-socket_nginx-sample   "/docker-entrypoint.…"   6 days ago   Up 6 days   0.0.0.0:80->80/tcp   php-socket_nginx-sample_1
d82e7e343c79   php-socket_php-sample     "docker-php-entrypoi…"   6 days ago   Up 6 days   9000/tcp             php-socket_php-sample_1

php のコンテナにログインして、状態を確認する。

$ docker exec -i -t d82e7e343c79 /bin/sh
/var/www/html # ls -l /usr/local/etc/php-fpm.d/
docker.conf       www.conf          www.conf.default  zz-docker.conf    zz-www.conf
/var/www/html # ls -l /usr/local/etc/php-fpm.d/zz-www.conf
-rw-r--r--    1 root     root           173 Mar 28 06:20 /usr/local/etc/php-fpm.d/zz-www.conf
/var/www/html # cat /usr/local/etc/php-fpm.d/zz-www.conf
[global]
daemonize = no

[www]

user = www-data
group = www-data

listen = /var/run/php-fpm/php-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode  = 0660
/var/www/html # ls -l /var/run/php-fpm/
total 0
srw-rw----    1 www-data www-data         0 Mar 28 06:23 php-fpm.sock
/var/www/html # netstat -natp
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 127.0.0.11:44299        0.0.0.0:*               LISTEN      -
/var/www/html # exit

php-fpm の設定ファイルが取り込まれ、UNIX ソケットが想定通りに生成されている。netstat -natp を実行した結果、php-fpm が ポートを listen していないことが確認できた。

次に、nginx の状態を確認する。

$ docker exec -i -t 95a89d5cac6d /bin/sh
/ # ls -l /var/run/php-fpm/
total 0
srw-rw----    1 www-data www-data         0 Mar 28 06:23 php-fpm.sock
/ # netstat -natp | grep nginx
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      1/nginx: master pro
/ # exit

nginx のコンテナから php-fpm の UNIX ソケットが見えていることが確認できた。

ホストマシンから curl で接続をしてみる。

$ curl -I http://localhost/
HTTP/1.1 200 OK
Server: nginx/1.21.6
Date: Sun, 03 Apr 2022 09:58:42 GMT
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
X-Powered-By: PHP/8.1.4

http://localhost/ にも接続をして、phpinfo() の実行結果が表示されることを確認した。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?