docker 化 php 项目的方式探讨

首先给出我的解决方案:

  • 在生产环境的部署中将源代码打包到镜像以 docker 镜像的方式发布,并且运行环境中同时包含 nginx 和 php-fpm 用 supervisor 管理服务进程,这样生产服务器将不需要任何依赖,只需要安装 docker-engine 即可,同时也方便使用 docker swarm 进行横向扩容。
  • 在开发环境中源代码挂载到容器,因为开发过程中源代码经常变动,打包镜像的方式效率太低。

以下是探讨过程

不知道各位有没有这样的疑问:当使用 docker 部署 php 项目时,php 代码是应该挂载到容器中还是打包到镜像中?nginx 和 fpm 应该各自运行还是打包到同一个容器中?

由于 php 是脚本语言的性质,相信不少人都选择了将代码挂载到容器中,nginx 和 php 挂载相同的源代码路径运行。不过最近准备在生产环境使用这种方式部署项目的时候遇到了以下问题:

  • 宿主机上需要安装 git 等版本管理工具下载源代码,部署成本偏高。
  • 如果在宿主机上安装 composer 包依赖,宿主机还需要一套完整的 php 环境,部署成本更高。
  • 如果在 php 容器中安装 composer 包依赖,由于有私有包的存在,php 容器中仍然需要安装 openssh, git 等包,甚至还需要将 ssh key 拷贝到容器中,镜像中冗余了不是很有必要的软件包。
  • php 容器 recreate 之后 nginx 也必须要 recreate 虽然这是一个可以通过 docker-compose 自动完成的流程,但是nginx 以及 fpm 上的其他应用也会因此重启,不优雅。

当我试着把源代码打包到 fpm 的镜像中来启动时,新的问题出现了:

  • nginx 无法访问代码目录。
  • 在打包 fpm 的时候声明代码目录为 volume 并且在启动 nginx 时通过 --volumes-from 参数挂载 fpm 的代码目录可以解决上面的问题。
  • 但是 fpm recreate 之后 nginx 挂载的目录跟新创建的 fpm 容器不再关联了,这是 docker 的 volume 机制,不可破,就如同在 docker-compose 文件中声明了 link 一样,被依赖容易会被一并 recreate。

其实导致上面问题只有一个原因,那就是 php 没有像 python, go 等语言那样有一个自己的 http 服务器,必须要依赖 fpm 以及 nginx,那么就需要一种方案能让 php 自己提供 http 服务。

找到如下几种可选方案

  • php-pm 只需要安装 php 官方扩展以及 composer 包即可,但是我没能成功在 lumen 项目中运行。
  • hhvm hhvm 有一个 server mode 似乎可以解决我的问题,但是我没能成功写出项目的 rewrite 规则,网上搜集到的文章多数是用 fcgi 搭配 nginx 的部署方式。
  • swoole 因为 swoole 安装成本太高,没做过多尝试,但是有 issue 提到可能和某些 php 框架机制冲突。

总得来说上面几种方案都不太成熟,而且都有学习成本和未知的风险。那么到此就有了文章开头的方案了,应用镜像中打包源代码、nginx 以及 fpm 对外只暴露出 http 端口