如何正确地link php-fpm 和Nginx Docker 容器?
How to correctly link php-fpm and Nginx Docker containers?
我正在尝试 link 2 个单独的容器:
问题是 php 脚本不起作用。也许 php-fpm 配置不正确。
这是源代码,在我的 repository 中。这是文件 docker-compose.yml
:
nginx:
build: .
ports:
- "80:80"
- "443:443"
volumes:
- ./:/var/www/test/
links:
- fpm
fpm:
image: php:fpm
ports:
- "9000:9000"
和 Dockerfile
我用它来构建一个基于 nginx 的自定义图像:
FROM nginx
# Change Nginx config here...
RUN rm /etc/nginx/conf.d/default.conf
ADD ./default.conf /etc/nginx/conf.d/
最后,这是我自定义的 Nginx 虚拟主机配置:
server {
listen 80;
server_name localhost;
root /var/www/test;
error_log /var/log/nginx/localhost.error.log;
access_log /var/log/nginx/localhost.access.log;
location / {
# try to serve file directly, fallback to app.php
try_files $uri /index.php$is_args$args;
}
location ~ ^/.+\.php(/|$) {
fastcgi_pass 192.168.59.103:9000;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param HTTPS off;
}
}
谁能帮我正确配置这些容器以执行 php 脚本?
P.S.
我 运行 容器通过 docker-composer 像这样:
docker-compose up
从项目根目录。
不要在 nginx 配置中硬编码容器的 ip,docker link 将 linked 机器的主机名添加到容器的主机文件中,你应该可以按主机名 ping。
编辑:Docker 1.9 网络不再需要 link 容器,当多个容器连接到同一个网络时,它们的主机文件将被更新,以便它们可以通过主机名相互访问.
每次 docker 容器从映像(甚至 stop/start-ing 现有容器)旋转起来时,容器都会获得由 docker 主机分配的新 IP。这些 ip 与您的实际机器不在同一子网中。
see docker linking docs(这是 compose 在后台使用的)
but more clearly explained in the docker-compose
docs on links & expose
links
links:
- db
- db:database
- redis
An entry with the alias' name will be created in /etc/hosts inside containers for this service, e.g:
172.17.2.186 db
172.17.2.186 database
172.17.2.187 redis
expose
Expose ports without publishing them to the host machine - they'll only be accessible to linked services. Only the internal port can be specified.
如果您将项目设置为通过环境变量获取端口和其他凭据,links automatically set a bunch of system variables:
To see what environment variables are available to a service, run docker-compose run SERVICE env
.
name_PORT
Full URL, e.g. DB_PORT=tcp://172.17.0.5:5432
name_PORT_num_protocol
Full URL, e.g. DB_PORT_5432_TCP=tcp://172.17.0.5:5432
name_PORT_num_protocol_ADDR
Container's IP address, e.g. DB_PORT_5432_TCP_ADDR=172.17.0.5
name_PORT_num_protocol_PORT
Exposed port number, e.g. DB_PORT_5432_TCP_PORT=5432
name_PORT_num_protocol_PROTO
Protocol (tcp or udp), e.g. DB_PORT_5432_TCP_PROTO=tcp
name_NAME
Fully qualified container name, e.g. DB_1_NAME=/myapp_web_1/myapp_db_1
我认为我们还需要为 fpm 容器提供音量,不是吗?所以=>
fpm:
image: php:fpm
volumes:
- ./:/var/www/test/
如果我不这样做,我会在发出请求时 运行 进入这个异常,因为 fpm 找不到请求的文件:
[error] 6#6: *4 FastCGI sent in stderr: "Primary script unknown" while reading response header from upstream, client: 172.17.42.1, server: localhost, request: "GET / HTTP/1.1", upstream: "fastcgi://172.17.0.81:9000", host: "localhost"
我知道它有点老 post,但我遇到了同样的问题,无法理解为什么您的代码不起作用。
经过大量测试后,我发现了原因。
fpm 似乎从 nginx 接收到完整路径并尝试在 fpm 容器中查找文件,因此它必须与 nginx 配置中的 server.root
完全相同,即使它不是'不存在于 nginx 容器中。
演示:
docker-compose.yml
nginx:
build: .
ports:
- "80:80"
links:
- fpm
fpm:
image: php:fpm
ports:
- ":9000"
# seems like fpm receives the full path from nginx
# and tries to find the files in this dock, so it must
# be the same as nginx.root
volumes:
- ./:/complex/path/to/files/
/etc/nginx/conf.d/default.conf
server {
listen 80;
# this path MUST be exactly as docker-compose.fpm.volumes,
# even if it doesn't exist in this dock.
root /complex/path/to/files;
location / {
try_files $uri /index.php$is_args$args;
}
location ~ ^/.+\.php(/|$) {
fastcgi_pass fpm:9000;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
Dockerfile
FROM nginx:latest
COPY ./default.conf /etc/nginx/conf.d/
新答案
Docker Compose 已更新。他们现在有 version 2 file format。
Version 2 files are supported by Compose 1.6.0+ and require a Docker Engine of version 1.10.0+.
他们现在支持 Docker 的网络功能,当 运行 设置一个名为 myapp_default
的默认网络时
来自 their documentation 您的文件将如下所示:
version: '2'
services:
web:
build: .
ports:
- "8000:8000"
fpm:
image: phpfpm
nginx
image: nginx
由于这些容器会自动添加到默认的 myapp_default 网络中,因此它们可以相互通信。然后,您将在 Nginx 配置中拥有:
fastcgi_pass fpm:9000;
另外正如@treeface 在评论中提到的,请记住确保 PHP-FPM 正在侦听端口 9000,这可以通过在您需要 listen = 9000
的地方编辑 /etc/php5/fpm/pool.d/www.conf
来完成.
旧答案
我为那些使用旧版本 Docker/Docker compose 的人保留了以下信息,并希望获得这些信息。
当我试图找到这个问题的答案时,我一直在 google 上遇到这个问题,但由于 Q/A 强调 docker-compose(在撰写本文时仅对 docker 网络功能提供实验性支持)。所以这是我对所学知识的看法。
Docker 最近 deprecated its link feature in favour of its networks feature
因此,使用 Docker 网络功能,您可以按照以下步骤 link 容器。有关选项的完整解释,请阅读之前 link 编辑的文档。
首先创建你的网络
docker network create --driver bridge mynetwork
接下来 运行 您的 PHP-FPM 容器确保您打开端口 9000 并分配给您的新网络 (mynetwork
)。
docker run -d -p 9000 --net mynetwork --name php-fpm php:fpm
这里重要的一点是命令末尾的 --name php-fpm
,它是名称,我们稍后会用到它。
下一步 运行 你的 Nginx 容器再次分配给你创建的网络。
docker run --net mynetwork --name nginx -d -p 80:80 nginx:latest
对于 PHP 和 Nginx 容器,您还可以根据需要添加 --volumes-from
命令等。
Nginx 配置来了。应该看起来像这样:
server {
listen 80;
server_name localhost;
root /path/to/my/webroot;
index index.html index.htm index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass php-fpm:9000;
fastcgi_index index.php;
include fastcgi_params;
}
}
注意位置块中的 fastcgi_pass php-fpm:9000;
。那就是在端口 9000
上联系容器 php-fpm
。当您将容器添加到 Docker 桥接网络时,它们都会自动获得一个主机文件更新,将容器名称与 IP 地址相对应。因此,当 Nginx 看到它会知道联系您之前命名为 php-fpm
并分配给您的 mynetwork
Docker 网络的 PHP-FPM 容器。
您可以在 Docker 容器的构建过程中或之后添加该 Nginx 配置,这由您决定。
如前所述,问题是文件对 fpm 容器不可见。然而,要在容器之间共享数据,推荐的模式是使用 仅数据容器 (如 this article 中所述)。
长话短说:创建一个仅保存您的数据的容器,与卷共享,然后 link 在您的应用程序中 volumes_from
使用此卷 volumes_from
。
使用 compose(在我的机器上是 1.6.2),docker-compose.yml
文件将显示为:
version: "2"
services:
nginx:
build:
context: .
dockerfile: nginx/Dockerfile
ports:
- "80:80"
links:
- fpm
volumes_from:
- data
fpm:
image: php:fpm
volumes_from:
- data
data:
build:
context: .
dockerfile: data/Dockerfile
volumes:
- /var/www/html
请注意,data
发布了一个 linked 到 nginx
和 fpm
服务的卷。然后是 data 服务的 Dockerfile
,其中包含您的源代码:
FROM busybox
# content
ADD path/to/source /var/www/html
和 nginx 的 Dockerfile
,它只是替换了默认配置:
FROM nginx
# config
ADD config/default.conf /etc/nginx/conf.d
为了完成,这里是示例运行所需的配置文件:
server {
listen 0.0.0.0:80;
root /var/www/html;
location / {
index index.php index.html;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass fpm:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
}
}
这只是告诉 nginx 使用共享卷作为文档根目录,并为 nginx 设置正确的配置以便能够与 fpm 容器通信(即:正确的 HOST:PORT
,即 fpm:9000
感谢 compose 定义的主机名,以及 SCRIPT_FILENAME
).
正如前面的答案已经解决的,但应该非常明确地说明:php 代码需要存在于 php-fpm 容器中,而静态文件需要存在于 nginx 中容器。为简单起见,大多数人只是将所有代码附加到两者,正如我在下面所做的那样。如果将来,我可能会在自己的项目中将代码的这些不同部分分开,以尽量减少哪些容器可以访问哪些部分。
用这个最新的启示更新了我下面的示例文件(谢谢@alkaline)
这似乎是 docker 2.0 forward 的最低设置
(因为在 docker 2.0 中事情变得容易多了)
docker-compose.yml:
version: '2'
services:
php:
container_name: test-php
image: php:fpm
volumes:
- ./code:/var/www/html/site
nginx:
container_name: test-nginx
image: nginx:latest
volumes:
- ./code:/var/www/html/site
- ./site.conf:/etc/nginx/conf.d/site.conf:ro
ports:
- 80:80
(更新了上面的 docker-compose.yml:对于具有 css、javascript、静态文件的站点,等等,你将需要 nginx 容器可以访问的那些文件。同时仍然让 fpm 容器可以访问所有 php 代码。同样,因为我的基本代码是 css、js 和php,本例只是将所有代码附加到两个容器)
在同一文件夹中:
site.conf:
server
{
listen 80;
server_name site.local.[YOUR URL].com;
root /var/www/html/site;
index index.php;
location /
{
try_files $uri =404;
}
location ~ \.php$ {
fastcgi_pass test-php:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
在文件夹代码中:
./code/index.php:
<?php
phpinfo();
并且不要忘记更新您的主机文件:
127.0.0.1 site.local.[YOUR URL].com
和运行你的docker-组成
$docker-compose up -d
并尝试使用您最喜欢的浏览器URL
site.local.[YOUR URL].com/index.php
对于获得
的任何其他人
Nginx 403 error: directory index of [folder] is forbidden
当使用 index.php
而 index.html
工作完美并且在 sites-enabled
的站点配置的服务器块中的索引中包含 index.php
server {
listen 80;
# this path MUST be exactly as docker-compose php volumes
root /usr/share/nginx/html;
index index.php
...
}
确保您位于 /etc/nginx/nginx.conf
的 nginx.conf 文件实际在 http
块中加载您的站点配置...
http {
...
include /etc/nginx/conf.d/*.conf;
# Load our websites config
include /etc/nginx/sites-enabled/*;
}
我正在尝试 link 2 个单独的容器:
问题是 php 脚本不起作用。也许 php-fpm 配置不正确。
这是源代码,在我的 repository 中。这是文件 docker-compose.yml
:
nginx:
build: .
ports:
- "80:80"
- "443:443"
volumes:
- ./:/var/www/test/
links:
- fpm
fpm:
image: php:fpm
ports:
- "9000:9000"
和 Dockerfile
我用它来构建一个基于 nginx 的自定义图像:
FROM nginx
# Change Nginx config here...
RUN rm /etc/nginx/conf.d/default.conf
ADD ./default.conf /etc/nginx/conf.d/
最后,这是我自定义的 Nginx 虚拟主机配置:
server {
listen 80;
server_name localhost;
root /var/www/test;
error_log /var/log/nginx/localhost.error.log;
access_log /var/log/nginx/localhost.access.log;
location / {
# try to serve file directly, fallback to app.php
try_files $uri /index.php$is_args$args;
}
location ~ ^/.+\.php(/|$) {
fastcgi_pass 192.168.59.103:9000;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param HTTPS off;
}
}
谁能帮我正确配置这些容器以执行 php 脚本?
P.S. 我 运行 容器通过 docker-composer 像这样:
docker-compose up
从项目根目录。
不要在 nginx 配置中硬编码容器的 ip,docker link 将 linked 机器的主机名添加到容器的主机文件中,你应该可以按主机名 ping。
编辑:Docker 1.9 网络不再需要 link 容器,当多个容器连接到同一个网络时,它们的主机文件将被更新,以便它们可以通过主机名相互访问.
每次 docker 容器从映像(甚至 stop/start-ing 现有容器)旋转起来时,容器都会获得由 docker 主机分配的新 IP。这些 ip 与您的实际机器不在同一子网中。
see docker linking docs(这是 compose 在后台使用的)
but more clearly explained in the docker-compose
docs on links & expose
links
links: - db - db:database - redis
An entry with the alias' name will be created in /etc/hosts inside containers for this service, e.g:
172.17.2.186 db 172.17.2.186 database 172.17.2.187 redis
expose
Expose ports without publishing them to the host machine - they'll only be accessible to linked services. Only the internal port can be specified.
如果您将项目设置为通过环境变量获取端口和其他凭据,links automatically set a bunch of system variables:
To see what environment variables are available to a service, run
docker-compose run SERVICE env
.
name_PORT
Full URL, e.g. DB_PORT=tcp://172.17.0.5:5432
name_PORT_num_protocol
Full URL, e.g.
DB_PORT_5432_TCP=tcp://172.17.0.5:5432
name_PORT_num_protocol_ADDR
Container's IP address, e.g.
DB_PORT_5432_TCP_ADDR=172.17.0.5
name_PORT_num_protocol_PORT
Exposed port number, e.g.
DB_PORT_5432_TCP_PORT=5432
name_PORT_num_protocol_PROTO
Protocol (tcp or udp), e.g.
DB_PORT_5432_TCP_PROTO=tcp
name_NAME
Fully qualified container name, e.g.
DB_1_NAME=/myapp_web_1/myapp_db_1
我认为我们还需要为 fpm 容器提供音量,不是吗?所以=>
fpm:
image: php:fpm
volumes:
- ./:/var/www/test/
如果我不这样做,我会在发出请求时 运行 进入这个异常,因为 fpm 找不到请求的文件:
[error] 6#6: *4 FastCGI sent in stderr: "Primary script unknown" while reading response header from upstream, client: 172.17.42.1, server: localhost, request: "GET / HTTP/1.1", upstream: "fastcgi://172.17.0.81:9000", host: "localhost"
我知道它有点老 post,但我遇到了同样的问题,无法理解为什么您的代码不起作用。 经过大量测试后,我发现了原因。
fpm 似乎从 nginx 接收到完整路径并尝试在 fpm 容器中查找文件,因此它必须与 nginx 配置中的 server.root
完全相同,即使它不是'不存在于 nginx 容器中。
演示:
docker-compose.yml
nginx:
build: .
ports:
- "80:80"
links:
- fpm
fpm:
image: php:fpm
ports:
- ":9000"
# seems like fpm receives the full path from nginx
# and tries to find the files in this dock, so it must
# be the same as nginx.root
volumes:
- ./:/complex/path/to/files/
/etc/nginx/conf.d/default.conf
server {
listen 80;
# this path MUST be exactly as docker-compose.fpm.volumes,
# even if it doesn't exist in this dock.
root /complex/path/to/files;
location / {
try_files $uri /index.php$is_args$args;
}
location ~ ^/.+\.php(/|$) {
fastcgi_pass fpm:9000;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
Dockerfile
FROM nginx:latest
COPY ./default.conf /etc/nginx/conf.d/
新答案
Docker Compose 已更新。他们现在有 version 2 file format。
Version 2 files are supported by Compose 1.6.0+ and require a Docker Engine of version 1.10.0+.
他们现在支持 Docker 的网络功能,当 运行 设置一个名为 myapp_default
的默认网络时来自 their documentation 您的文件将如下所示:
version: '2'
services:
web:
build: .
ports:
- "8000:8000"
fpm:
image: phpfpm
nginx
image: nginx
由于这些容器会自动添加到默认的 myapp_default 网络中,因此它们可以相互通信。然后,您将在 Nginx 配置中拥有:
fastcgi_pass fpm:9000;
另外正如@treeface 在评论中提到的,请记住确保 PHP-FPM 正在侦听端口 9000,这可以通过在您需要 listen = 9000
的地方编辑 /etc/php5/fpm/pool.d/www.conf
来完成.
旧答案
我为那些使用旧版本 Docker/Docker compose 的人保留了以下信息,并希望获得这些信息。
当我试图找到这个问题的答案时,我一直在 google 上遇到这个问题,但由于 Q/A 强调 docker-compose(在撰写本文时仅对 docker 网络功能提供实验性支持)。所以这是我对所学知识的看法。
Docker 最近 deprecated its link feature in favour of its networks feature
因此,使用 Docker 网络功能,您可以按照以下步骤 link 容器。有关选项的完整解释,请阅读之前 link 编辑的文档。
首先创建你的网络
docker network create --driver bridge mynetwork
接下来 运行 您的 PHP-FPM 容器确保您打开端口 9000 并分配给您的新网络 (mynetwork
)。
docker run -d -p 9000 --net mynetwork --name php-fpm php:fpm
这里重要的一点是命令末尾的 --name php-fpm
,它是名称,我们稍后会用到它。
下一步 运行 你的 Nginx 容器再次分配给你创建的网络。
docker run --net mynetwork --name nginx -d -p 80:80 nginx:latest
对于 PHP 和 Nginx 容器,您还可以根据需要添加 --volumes-from
命令等。
Nginx 配置来了。应该看起来像这样:
server {
listen 80;
server_name localhost;
root /path/to/my/webroot;
index index.html index.htm index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass php-fpm:9000;
fastcgi_index index.php;
include fastcgi_params;
}
}
注意位置块中的 fastcgi_pass php-fpm:9000;
。那就是在端口 9000
上联系容器 php-fpm
。当您将容器添加到 Docker 桥接网络时,它们都会自动获得一个主机文件更新,将容器名称与 IP 地址相对应。因此,当 Nginx 看到它会知道联系您之前命名为 php-fpm
并分配给您的 mynetwork
Docker 网络的 PHP-FPM 容器。
您可以在 Docker 容器的构建过程中或之后添加该 Nginx 配置,这由您决定。
如前所述,问题是文件对 fpm 容器不可见。然而,要在容器之间共享数据,推荐的模式是使用 仅数据容器 (如 this article 中所述)。
长话短说:创建一个仅保存您的数据的容器,与卷共享,然后 link 在您的应用程序中 volumes_from
使用此卷 volumes_from
。
使用 compose(在我的机器上是 1.6.2),docker-compose.yml
文件将显示为:
version: "2"
services:
nginx:
build:
context: .
dockerfile: nginx/Dockerfile
ports:
- "80:80"
links:
- fpm
volumes_from:
- data
fpm:
image: php:fpm
volumes_from:
- data
data:
build:
context: .
dockerfile: data/Dockerfile
volumes:
- /var/www/html
请注意,data
发布了一个 linked 到 nginx
和 fpm
服务的卷。然后是 data 服务的 Dockerfile
,其中包含您的源代码:
FROM busybox
# content
ADD path/to/source /var/www/html
和 nginx 的 Dockerfile
,它只是替换了默认配置:
FROM nginx
# config
ADD config/default.conf /etc/nginx/conf.d
为了完成,这里是示例运行所需的配置文件:
server {
listen 0.0.0.0:80;
root /var/www/html;
location / {
index index.php index.html;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass fpm:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
}
}
这只是告诉 nginx 使用共享卷作为文档根目录,并为 nginx 设置正确的配置以便能够与 fpm 容器通信(即:正确的 HOST:PORT
,即 fpm:9000
感谢 compose 定义的主机名,以及 SCRIPT_FILENAME
).
正如前面的答案已经解决的,但应该非常明确地说明:php 代码需要存在于 php-fpm 容器中,而静态文件需要存在于 nginx 中容器。为简单起见,大多数人只是将所有代码附加到两者,正如我在下面所做的那样。如果将来,我可能会在自己的项目中将代码的这些不同部分分开,以尽量减少哪些容器可以访问哪些部分。
用这个最新的启示更新了我下面的示例文件(谢谢@alkaline)
这似乎是 docker 2.0 forward 的最低设置 (因为在 docker 2.0 中事情变得容易多了)
docker-compose.yml:
version: '2'
services:
php:
container_name: test-php
image: php:fpm
volumes:
- ./code:/var/www/html/site
nginx:
container_name: test-nginx
image: nginx:latest
volumes:
- ./code:/var/www/html/site
- ./site.conf:/etc/nginx/conf.d/site.conf:ro
ports:
- 80:80
(更新了上面的 docker-compose.yml:对于具有 css、javascript、静态文件的站点,等等,你将需要 nginx 容器可以访问的那些文件。同时仍然让 fpm 容器可以访问所有 php 代码。同样,因为我的基本代码是 css、js 和php,本例只是将所有代码附加到两个容器)
在同一文件夹中:
site.conf:
server
{
listen 80;
server_name site.local.[YOUR URL].com;
root /var/www/html/site;
index index.php;
location /
{
try_files $uri =404;
}
location ~ \.php$ {
fastcgi_pass test-php:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
在文件夹代码中:
./code/index.php:
<?php
phpinfo();
并且不要忘记更新您的主机文件:
127.0.0.1 site.local.[YOUR URL].com
和运行你的docker-组成
$docker-compose up -d
并尝试使用您最喜欢的浏览器URL
site.local.[YOUR URL].com/index.php
对于获得
的任何其他人Nginx 403 error: directory index of [folder] is forbidden
当使用 index.php
而 index.html
工作完美并且在 sites-enabled
index.php
server {
listen 80;
# this path MUST be exactly as docker-compose php volumes
root /usr/share/nginx/html;
index index.php
...
}
确保您位于 /etc/nginx/nginx.conf
的 nginx.conf 文件实际在 http
块中加载您的站点配置...
http {
...
include /etc/nginx/conf.d/*.conf;
# Load our websites config
include /etc/nginx/sites-enabled/*;
}