在一个 dockerfile 中结合 PHP-fpm 和 nginx

2022-01-14 00:00:00 docker nginx dockerfile php fpm

我需要将 php-fpm 与 nginx 结合在一个 dockerfile 中以进行生产部署.

I have a need to combine the php-fpm with nginx in one dockerfile for production deployment.

那么是不是更好:

(1) 使用 php:7.1.8-fpm 启动 dockerfile,然后在上面安装 nginx 镜像层?

(1) Start the dockerfile using php:7.1.8-fpm and then install nginx image layer on top of it ?

(2) 或者你推荐使用 nginx 镜像,然后使用 apt-get 安装 php-fpm ?

(2) Or do you recommend using nginx image and then installing php-fpm using apt-get ?

PS:我没有用于生产部署的 docker-compose 构建选项.在我的开发环境中,我已经使用 docker-compose 并从两个图像轻松构建多容器应用程序.我们的组织 devops 不支持基于 docker-compose 的 prod 环境部署.

PS: I do not have a docker-compose build option for production deployment. On my development environment, I already use docker-compose and build multi-container app easily from two images. Our organization devops do not support docker-compose based deployment for prod environment.

推荐答案

Nginx 的安装比 PHP 容易得多,所以你应该更容易将 Nginx 安装到一个现成的官方 PHP 镜像中.下面是一个 Dockerfile 示例,展示了如何通过安装一些 PHP 扩展的示例来实现您的目标:

Nginx installation is much easier than PHP so it should be easier for you to install Nginx into a ready-to-use official PHP image. Here is an example of a Dockerfile showing how your goal can be reached with an example of installing a few PHP extensions:

FROM php:7.2-fpm

RUN apt-get update -y 
    && apt-get install -y nginx

# PHP_CPPFLAGS are used by the docker-php-ext-* scripts
ENV PHP_CPPFLAGS="$PHP_CPPFLAGS -std=c++11"

RUN docker-php-ext-install pdo_mysql 
    && docker-php-ext-install opcache 
    && apt-get install libicu-dev -y 
    && docker-php-ext-configure intl 
    && docker-php-ext-install intl 
    && apt-get remove libicu-dev icu-devtools -y
RUN { 
        echo 'opcache.memory_consumption=128'; 
        echo 'opcache.interned_strings_buffer=8'; 
        echo 'opcache.max_accelerated_files=4000'; 
        echo 'opcache.revalidate_freq=2'; 
        echo 'opcache.fast_shutdown=1'; 
        echo 'opcache.enable_cli=1'; 
    } > /usr/local/etc/php/conf.d/php-opocache-cfg.ini

COPY nginx-site.conf /etc/nginx/sites-enabled/default
COPY entrypoint.sh /etc/entrypoint.sh

COPY --chown=www-data:www-data . /var/www/mysite

WORKDIR /var/www/mysite

EXPOSE 80 443

ENTRYPOINT ["/etc/entrypoint.sh"]

nginx-site.conf 文件包含您的 Nginx http 主机配置.以下示例适用于 Symfony 应用程序:

The nginx-site.conf file contains your Nginx http host configuration. The example below is for a Symfony app:

server {
    root    /var/www/mysite/web;

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

    index app.php index.php index.html index.htm;

    client_max_body_size 30m;

    location / {
        try_files $uri $uri/ /app.php$is_args$args;
    }

    location ~ [^/].php(/|$) {
        fastcgi_split_path_info ^(.+?.php)(/.*)$;
        # Mitigate https://httpoxy.org/ vulnerabilities
        fastcgi_param HTTP_PROXY "";
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_index app.php;
        include fastcgi.conf;
    }
}

entrypoint.sh会在容器启动时运行Nginx和php-fpm(否则只会启动php-fpm作为官方PHP镜像的默认动作):

The entrypoint.sh will run Nginx and php-fpm on container startup (otherwise only php-fpm will be started as the default action of the official PHP image):

#!/usr/bin/env bash
service nginx start
php-fpm

当然,从最佳实践的角度来看,这不是最好的方法,但我希望这是您问题的答案.

Of course, this is not the best way from the best practice perspective, but I hope this is the answer to your question.

更新:

如果您在 entrypoint.sh 文件上收到权限被拒绝错误,如果您是在 Linux 下构建,请检查此文件是否具有可执行权限,或者添加 RUN chmod +x/etc/entrypoint.sh 如果您在 Windows 下,则复制到 Dockerfile(Windows 下的所有文件都在没有容器可执行权限的情况下复制).

If you get the permission denied error on the entrypoint.sh file, check that this file has the executable permission if you're building from under Linux, or add the RUN chmod +x /etc/entrypoint.sh to the Dockerfile if you're under Windows (all files from under Windows are copied without the executable permission to the container).

如果您在 Google Cloud Run 下运行,请记住 Nginx 在 PHP 之前启动,它比 PHP 快得多.这就导致了Cloud Run发送第一个请求的时候,Nginx已经初始化了,但是PHP-FPM还没有,Cloud Run请求失败.要解决这个问题,您应该将入口点更改为在 Nginx 之前启动 PHP-FPM:

If you're running under Google Cloud Run, keep in mind that Nginx startups before PHP and it does that much quicker than PHP. This leads to the issue that when Cloud Run sends the first request, it comes at the moment when Nginx is already initialized, but PHP-FPM is not yet and Cloud Run request fails. To fix that, you should change your entrypoint to startup PHP-FPM before Nginx:

#!/usr/bin/env sh
set -e

php-fpm -D
nginx -g 'daemon off;'

此脚本仅在 Alpine Linux 下进行测试.我想它也应该适用于其他图像.该脚本首先在后台运行 php-fpm,然后在不退出的情况下运行 Nginx.这样,Nginx 总是在 PHP-FPM 初始化后开始监听端口.

This script is tested under Alpine Linux only. I guess it should also work on other images. This script runs php-fpm first in the background, and then Nginx without exiting. In this way, Nginx always starts listening to ports after PHP-FPM is initialized.

相关文章