为所有PHP-FPM容器构建单独的Nginx Docker镜像

最近,原文作者一直在使用Docker容器来开发PHP微服务套件。一个问题是PHP应用已经搭建,可以和PHP-FPM和Nginx(取代了简单的Apche/PHP环境)一起工作,因此每个PHP微服务需要两个容器(以及两个Docker镜像):一个PHP-FPM容器和一个NGinx容器。 
\u0026#xD;这个应用运行了6个以上的服务,如果做个乘法,在开发和生产之间会有约30个容器。作者决定构建一个单独的NGinx Docker镜像,它可以使用PHP-FPM的主机名作为环境变量并运行单独的配置文件,而没有为每个容器构建单独的NGinx镜像。

\u0026#xD;\u0026#xD;


\u0026#xD;在本文中,原文作者简要说明从上图中的方法1到方法2的转换,最后采用的方案中采用了一种新的定制Docker镜像。该镜像的代码是开源的,如果读者碰到类似问题,可以随时签出该部分代码。

\u0026#xD;\u0026#xD;

为什么用 NGinx?

\u0026#xD;\u0026#xD;

NGinx和PHP-FPM配合使用能使PHP应用的性能更好,但不好的是和PHP Apache镜像不同,PHP-FPM Docker镜像缺省并没有和NGinx进行绑定。如果需要通过NGinx容器和PHP-FPM连接,需要在NGind配置里为该后端增加DNS记录。比如,如果名为php-fpm-api的PHP-FPM容器正在运行,NGinx配置文件应该包含下面部分:

\u0026#xD;\u0026#xD;
\u0026#xD;    location ~ .php$ {\u0026#xD;        fastcgi_split_path_info ^(.+.php)(/.+)$;\u0026#xD;        # This line passes requests through to the PHP-FPM container\u0026#xD;        fastcgi_pass php-fpm-api:9000;\u0026#xD;        fastcgi_index index.php;\u0026#xD;        include fastcgi_params;\u0026#xD;        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;\u0026#xD;        fastcgi_param PATH_INFO $fastcgi_path_info;\u0026#xD;    }
\u0026#xD;\u0026#xD;

如果只服务于单独的NGinx容器,NGinx配置中容器名字写死还可以接受,但如上所述,需要允许多个NGinx容器,每个对应于一个PHP服务。创建一个新的NGinx镜像(以后需要进行维护和升级)会有些痛苦,即使管理一批不同的数据卷,仅仅改变变量名看起来也有很多工作。

\u0026#xD;\u0026#xD;

第一种方案: 使用Docker文档中的方法

\u0026#xD;\u0026#xD;

最初,作者认为这会很简单。Docker文档中有少许的几个章节讨论如何使用envsubst来完成该工作,但不幸的是,在其NGinx配置文件中,这种方法不奏效。 
vhosts.conf

\u0026#xD;\u0026#xD;
\u0026#xD;server {\u0026#xD;    listen 80;\u0026#xD;    index index.php index.html;\u0026#xD;    root /var/www/public;\u0026#xD;    client_max_body_size 32M;\u0026#xD;    location / {\u0026#xD;        try_files $uri /index.php?$args;\u0026#xD;    }\u0026#xD;    location ~ .php$ {\u0026#xD;        fastcgi_split_path_info ^(.+.php)(/.+)$;\u0026#xD;        fastcgi_pass ${NGINX_HOST}:9000;\u0026#xD;        fastcgi_index index.php;\u0026#xD;        include fastcgi_params;\u0026#xD;        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;\u0026#xD;        fastcgi_param PATH_INFO $fastcgi_path_info;\u0026#xD;    }\u0026#xD;}
\u0026#xD;\u0026#xD;

vhosts.conf文件使用了NGinx内置变量,因此当依照文档运行Docker命令(/bin/bash -c \"envsubst \u0026lt; /etc/nginx/conf.d/mysite.template \u0026gt; /etc/nginx/conf.d/default.conf \u0026amp;\u0026amp; nginx -g 'daemon off;'\")时,得到错误提示$uri$fastcgi_script_name没有定义。这些变量通常通过NGinx传入,因此不能简单的识别出他们是什么并传给自身,而且这使容器的动态性变差。

\u0026#xD;\u0026#xD;

用另一个Docker镜像来救急,差点成功

\u0026#xD;\u0026#xD;

接下来,作者开始研究不同的NGinx镜像。找到的两个,但它们都在随后的几年中都没有任何更新。作者开始使用martin/nginx,试图找到可以工作的原型。 
\u0026#xD;Martin镜像和其它镜像有点不一样,因为它要求特定的文件夹结构。在root下增加Dockerfile

\u0026#xD;\u0026#xD;
\u0026#xD;FROM martin/nginx
\u0026#xD;\u0026#xD;

接下来,我添加了一个app/空目录和conf/目录,conf/目录下只有一个文件vhosts.conf

\u0026#xD;\u0026#xD;
\u0026#xD;server {\u0026#xD;    listen 80;\u0026#xD;    index index.php index.html;\u0026#xD;    root /var/www/public;\u0026#xD;    client_max_body_size 32M;\u0026#xD;    location / {\u0026#xD;        try_files $uri /index.php?$args;\u0026#xD;    }\u0026#xD;    location ~ .php$ {\u0026#xD;        fastcgi_split_path_info ^(.+.php)(/.+)$;\u0026#xD;        fastcgi_pass $ENV{\"NGINX_HOST\"}:9000;\u0026#xD;        fastcgi_index index.php;\u0026#xD;        include fastcgi_params;\u0026#xD;        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;\u0026#xD;        fastcgi_param PATH_INFO $fastcgi_path_info;\u0026#xD;    }\u0026#xD;}
\u0026#xD;\u0026#xD;

这个文件和之前的配置文件几乎一样,除了有一行的改动:

\u0026#xD;\u0026#xD;

fastcgi_pass $ENV{\"NGINX_HOST\"}:9000;。现在想要启动带命名为php-fpm-api的PHP容器的NGinx容器,就可以构建一个新的镜像,让它在以下环境变量下运行: 

\u0026#xD;\u0026#xD;
\u0026#xD;docker build -t shiphp/nginx-env:test .\u0026#xD;docker run -it --rm -e NGINX_HOST=php-fpm-api shiphp/nginx-env:test\u0026#xD;
\u0026#xD;\u0026#xD;

它可以正常工作了。但是,这种方法有两个困扰的地方: 
\u0026#xD;1. 正在使用的基础镜像已经有两年了。这会引入安全和性能风险。 
\u0026#xD;2. 有个空的/app目录看起来并不必需,因为文件会被存储在一个不同的目录中。

\u0026#xD;\u0026#xD;

最终解决方案

\u0026#xD;\u0026#xD;

作者认为作为定制解决方案,从Martin镜像开始比较好,因此给项目建了分叉,创建了新的NGinx基础镜像并修复了上述两个问题。现在,如果要在NGinx容器中允许动态命名的后端,可以参照:

\u0026#xD;\u0026#xD;
\u0026#xD;# 从Docker Hub得到最新版本\u0026#xD;docker pull shiphp/nginx-env:latest\u0026#xD;# 运行名为\"php-fpm-api\"的PHP容器 \u0026#xD;docker run --name php-fpm-api -v $(pwd):/var/www php:fpm\u0026#xD;# 允许链接到PHP-FPM容器的NGinx容器\u0026#xD;docker run --link php-fpm-api -e NGINX_HOST=php-fpm-api shiphp/nginx-env\u0026#xD;
\u0026#xD;\u0026#xD;

如果想增加自己的文件或NGinx配置文件,来定制镜像,用Dockerfile来扩展它就可以:

\u0026#xD;\u0026#xD;
\u0026#xD;FROM shiphp/nginx-env\u0026#xD;\u0026#xD;ONBUILD ADD \u0026lt;PATH_TO_YOUR_CONFIGS\u0026gt; /etc/nginx/conf.d/\u0026#xD;\u0026#xD;...
\u0026#xD;\u0026#xD;

现在所有的PHP-FPM容器都使用了它们自己的Docker镜像实例,这样在升级NGinx,改变权限或做某些调整时,就变得非常轻松了。  所有的代码都在Github上,如果读者看到任何问题或有改进建议,可以直接创建一个问题单。如果有疑问或任何Docker相关的,可以在Twitter上找到我继续探讨。 

\u0026#xD;\u0026#xD;

查看英文原文:https://www.shiphp.com/blog/2018/nginx-php-fpm-with-env

\u0026#xD;\u0026#xD;

感谢张婵对本文的审校。

你可能感兴趣的:(为所有PHP-FPM容器构建单独的Nginx Docker镜像)