【问题标题】:How to correctly link php-fpm and Nginx Docker containers?如何正确链接 php-fpm 和 Nginx Docker 容器?
【发布时间】:2015-07-06 12:12:48
【问题描述】:

我正在尝试链接 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 脚本?

附言 我像这样通过 docker-composer 运行容器:

docker-compose up

来自项目根目录。

【问题讨论】:

  • 到目前为止,您是如何尝试配置它们的,或者您使用了哪些代码。请不要让我猜我猜的很垃圾。
  • @MatthewBrown Huh,我将我的代码公开repository on GitHub 并认为这就足够了,但你是对的,最好在我的问题中也显示代码。
  • 当图像启动时,你能docker exec 进入正在运行的容器并 ping fpm 吗?
  • @MatthewBrown 是的,我赢了,谢谢
  • P.S. 我还实现了将NginxPHP-FPM 与 Vagrant 和 Ansible 链接在一起的工作解决方案。如果需要,请查看我的仓库 github.com/bocharsky-bw/vagrant-ansible-docker

标签: php nginx docker dockerfile docker-compose


【解决方案1】:

我知道这是一篇旧帖子,但我遇到了同样的问题,不明白为什么您的代码不起作用。 经过大量测试,我找到了原因。

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/

【讨论】:

  • 干得好!!!这就是重点!我将 nginx 根目录设置为 /var/www/html 以外的替代路径,但失败了。
  • 另外,请注意:9000 是容器中使用的端口,而不是暴露给主机的端口。我花了 2 个小时才弄清楚这一点。希望您不必这样做。
  • services.fpm.ports is invalid: Invalid port ":9000", should be [[remote_ip:]remote_port[-remote_port]:]port[/protocol]
  • 您实际上根本不需要在此处包含ports 部分。如果它不在图像中(可能是),您可能只需要expose 它。如果你在进行容器间通信,你不应该暴露 PHP-FPM 端口。
  • 正在为 AH01071: Got error 'Primary script unknown\n' 寻找解决方案,而 php-fpm 容器必须与 Web 节点共享同一目录是解决方案!
【解决方案2】:

不要在 nginx 配置中硬编码容器的 ip,docker link 会将链接机器的主机名添加到容器的主机文件中,您应该可以通过主机名 ping。

编辑:Docker 1.9 网络不再需要您链接容器,当多个容器连接到同一个网络时,它们的主机文件将被更新,以便它们可以通过主机名相互访问。

每次 docker 容器从映像启动(甚至停止/启动现有容器)时,容器都会获得 docker 主机分配的新 IP。这些 ip 与您的实际机器不在同一个子网中。

see docker linking docs(这是compose在后台使用的)

but more clearly explained in the docker-compose docs on links & expose

链接

links:
 - db
 - db:database
 - redis

将在 /etc/hosts 中为此服务的容器内创建一个具有别名名称的条目,例如:

172.17.2.186  db
172.17.2.186  database
172.17.2.187  redis

公开

公开端口而不将它们发布到主机 - 它们只能链接服务访问。只能指定内部端口。

如果您将项目设置为通过环境变量获取端口和其他凭据,links automatically set a bunch of system variables:

要查看服务可以使用哪些环境变量,请运行 docker-compose run SERVICE env

name_PORT

完整的网址,例如DB_PORT=tcp://172.17.0.5:5432

name_PORT_num_protocol

完整的网址,例如DB_PORT_5432_TCP=tcp://172.17.0.5:5432

name_PORT_num_protocol_ADDR

容器的 IP 地址,例如DB_PORT_5432_TCP_ADDR=172.17.0.5

name_PORT_num_protocol_PORT

公开的端口号,例如DB_PORT_5432_TCP_PORT=5432

name_PORT_num_protocol_PROTO

协议(tcp 或 udp),例如DB_PORT_5432_TCP_PROTO=tcp

name_NAME

完全限定的容器名称,例如DB_1_NAME=/myapp_web_1/myapp_db_1

【讨论】:

  • 您也不需要在主机上发布端口 9000,链接的 docker 容器之间的端口是开放的,除非您想直接从主机对端口进行故障排除。
  • 是的,你是对的,谢谢。在我的情况下,我应该使用 fastcgi_pass fpm:9000 而不是直接 ip。我不知道Docker自动将它添加到主机,我的错。
  • port 怎么样,最好使用 expose 而不是 ports?或者我不能使用任何这些端口并暴露指令,因为链接的容器可以访问这个端口?
  • 抱歉回复晚了-我想你可能需要使用expose,抱歉我现在不能检查
  • 根据您引用的 docker 文档,--links 现在已过时。它们仍然当前受支持,但明显的计划是淘汰它们。
【解决方案3】:

对于其他人来说

Nginx 403 错误:[文件夹] 的目录索引被禁止

当使用index.phpindex.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/*;
}

【讨论】:

  • 感谢这个,旧的但仍然信息丰富,我不得不使用 /usr/share/nginx/html ?,谢谢
【解决方案4】:

如前所述,问题在于 fpm 容器看不到文件。但是,要在容器之间共享数据,推荐的模式是使用仅数据容器(如 this article 中所述)。

长话短说:创建一个仅保存您的数据的容器,将其与卷共享,然后在您的应用中使用 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 发布了一个链接到 nginxfpm 服务的卷。然后是 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) 定义。

【讨论】:

  • 看起来数据没有从主机更新到容器,当我执行 docker ps -a 我看到数据容器停止时,这是一个问题吗?
  • 这是预期的行为。仅数据容器不运行任何命令,它只会被列为已停止。此外,数据容器的Dockerfile 正在构建时将您的源复制到容器中。这就是为什么如果您更改主机中的文件,它们将不会更新。如果要在主机和容器之间共享源,则需要挂载目录。将撰写文件中的data 服务更改为加载image: busybox,并在volumes 部分输入./sources:/var/www/html,其中./sources 是您的源在主机中的路径。
【解决方案5】:

新答案

Docker Compose 已更新。他们现在有一个version 2 file format

Compose 1.6.0+ 支持版本 2 文件,并且需要版本 1.10.0+ 的 Docker 引擎。

它们现在支持 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 在 cmets 中提到的,请记住确保 PHP-FPM 正在侦听端口 9000,这可以通过编辑 /etc/php5/fpm/pool.d/www.conf 来完成,您将需要 listen = 9000

旧答案

我为那些使用旧版本 Docker/Docker compose 并希望了解这些信息的人保留了以下信息。

当我试图找到这个问题的答案时,我一直在谷歌上偶然发现这个问题,但由于 Q/A 强调 docker-compose(在撰写本文时只有对 docker 网络功能的实验性支持)。所以这是我对我所学到的东西的看法。

Docker 最近推出了deprecated its link 功能,支持其networks feature

因此,使用 Docker 网络功能,您可以按照以下步骤链接容器。有关选项的完整说明,请阅读之前链接的文档。

首先创建你的网络

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 配置。

【讨论】:

  • 还记得确保php-fpm 正在侦听端口9000。这将是listen = 9000 in /etc/php5/fpm/pool.d/www.conf
  • 谢谢@treeface 好点。我已经更新了你的评论。
【解决方案6】:

正如以前的答案已经解决了,但应该非常明确地说明: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 容器可以访问这些文件。同时仍然拥有所有 php fpm 容器可访问的代码。同样,因为我的基本代码是 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-compose up

$docker-compose up -d

并从您喜欢的浏览器中尝试该 URL

site.local.[YOUR URL].com/index.php

【讨论】:

  • 您的 nginx 配置文件假定您的网站只有 php 文件。为静态文件(jpg、txt、svg、...)创建 nginx 位置规则并避免使用 php 解释器是最佳实践。在这种情况下,nginx 和 php 容器都需要访问网站文件。 @iKanor 上面的回答解决了这个问题。
  • 谢谢@Alkaline,静态文件是我原来的答案的问题。事实上,为了正常工作,nginx 至少需要 css 和 js 文件位于该机器的本地。
【解决方案7】:

我认为我们还需要为 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”,主机:“localhost”

【讨论】:

  • 是的,你是对的!我们必须与 fpm 和 nginx 共享文件
  • 我在GitHub 上有NginxPHP-FPM 的工作示例
猜你喜欢
  • 2021-04-08
  • 2018-03-21
  • 2021-02-23
  • 2018-07-17
  • 1970-01-01
  • 2017-04-30
  • 2017-02-13
  • 1970-01-01
相关资源
最近更新 更多