在 Docker 中,使用 nginx + php-fpm 配置并通过 UNIX 套接字进行通信
前几天,我建立了一个使用nginx + php-fpm的环境,通过TCP进行协作。现在我要改成使用UNIX Socket来实现nginx和php-fpm之间的协作。
手順的步骤
- 要使php-fpm在UNIX套接字上监听,需要准备一个与php-fpm在UNIX套接字上协作的docker,运行容器并确认配置的反映。
要在UNIX套接字上让php-fpm监听
大致上需要进行以下设定。
-
- 将已注册在php-fpm镜像中的用户添加到nginx镜像中
-
- 添加一个配置文件,将连接php-fpm的方式从TCP更改为UNIX Socket
- 添加一个配置文件,用于设置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进程分配操作php-fpm UNIX套接字的权限,需要修改/etc/nginx/nginx.conf的设置来授权nginx用户。
增加一个配置文件,将与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套接字,更改仍无法生效。
当我进行调查时,发现原因在于公式的 php-fpm Dockerfile 中的描述覆盖了用户的更改。
- 参照: php/Dockerfile at a41dc76cb007ac64177d6999f7be91aae149ee9e · docker-library/php · GitHub
} | tee php-fpm.d/docker.conf; \
{ \
echo '[global]'; \
echo 'daemonize = no'; \
echo; \
echo '[www]'; \
echo 'listen = 9000'; \
} | tee php-fpm.d/zz-docker.conf
请在本地语言中进行该短语的释义。
-
- Documentation regarding configuration. #241
-
- PHPの公式DockerイメージでUNIXソケット通信しようとして罠にハマる
- docker-composeでunixソケットを使った、Nginx、php-fpmコンテナを作る
要将php-fpm监听UNIX套接字,应该怎么做?
-
- php-fpm の設定は、後から読み込まれる設定で上書きされる
-
- 設定ファイルは、ファイル名の昇順で読み込まれる
- この仕様を利用して、/usr/local/etc/php-fpm.d/zz-docker.conf よりも後に設定ファイルを読み込ませて、TCP で listen する設定を、UNIX ソケットで listen する設定で上書きする
需要这件事情。 zhè .)
有几个网页详细说明了这种回避策,但没有提到上述的原因。因此,无法理解为什么将文件名更改为socket.conf后配置更改不会反映,确定原因花费了相当长的时间。
准备一个将php-fpm与UNIX套接字配合使用的docker。
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 文件
使nginx容器能够访问php-fpm生成的UNIX套接字。
$ 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镜像中添加用户。
在此情况下,需要将www-data用户的用户ID与php镜像中注册的用户ID匹配。由于该ID为82,因此需要在nginx镜像中创建一个具有用户ID 82的www-data用户。
$ 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
更改以使得 nginx 和 php-fpm 通过 UNIX Socket 进行协作。
为了将执行 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;
}
修改default.conf的配置,以便与php-fpm的UNIX套接字进行通信。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。
将读取文件 /usr/local/etc/php-fpm.d/zz-docker.conf 后,将 php-fpm 监听 TCP 9000 端口的配置覆盖为监听 UNIX Socket 的配置。为此,请将文件名 /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没有监听端口。
接下来,要检查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() 的结果。