Docker部署PHP+Nginx环境

0x00 序言

最近一直在学习容器相关的应用,决定把用的非常频繁的Nginx和PHP环境利用容器(Docker)的方式完成部署,最终实现无需考虑宿主机环境,构建PHP应用的运行环境。

之所以没有将数据库应用如Mysql或者MariaDB也用部署到相同的环境,是因为考虑到目前上云应用的解耦问题,计算资源和存储资源应当分离,就算需要用容器部署数据库服务,也应该在独立的环境中。

对于PHP应用来说,计算资源是作为Web服务的Nginx和运算服务的PHP-FPM,存储资源是各项数据,所以我将Nginx和PHP-FPM以容器的方式部署,数据库和UPLOAD数据单独部署或者利用云数据和云对象存储实现,即可增强应用的扩展性,实现动态扩容等操作。

我以CentOS7宿主机为例,部署PHP应用的运行环境。

注:本文所涉及宿主机命令,默认以root用户执行。

0x01 Docker

对于部分云主机来说,已经自带了Docker的源,直接通过命令

1
yum install docker-io

或者

1
apt install docker.io

即可完成Docker引擎和客户端的安装

安装后记得启动Docker并设置为开机服务

1
2
systemctl start docker
systemctl enable docker

如果上述命令提示未找到相关软件包,可通过Docker官方的安装方式,添加软件源并安装,以CentOS为例:

  • 卸载旧版Docker(如果存在)
1
2
3
4
5
6
7
8
yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
  • 安装yum-utils并设置Docker源
1
2
3
4
yum install -y yum-utils
yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
  • 安装Docker引擎
1
yum install docker-ce docker-ce-cli containerd.io
  • 启动Docker引擎并设置为开机服务
1
2
systemctl start docker
systemctl enable docker

到这里Docker引擎应该就安装成功了

可以通过命令

1
docker version

查看是否成功运行,也可以通过

1
docker run hello-world

启动一个HelloWorld的Docker容器。

其他系统的安装可以参考官方文档Install Docker Engine | Docker Documentation

0x02 PHP-FPM

Docker引擎准备就绪之后就可以部署PHP站点的运行环境了,那就是PHP-FPM,本文使用PHP7.4环境下的PHP-FPM,更多镜像可以查看php官方Docker Hub

  • 从官方拉取PHP-FPM镜像
1
docker pull php:7.4-fpm
  • 启动PHP-FPM
1
docker run -v [app_path]:/www/[app_name] --name php-fpm -d php:7.4-fpm

该命令表示以php-fpm为名字,挂载[app_path]到容器的/www/[app_name]路径,在后台启动一台php:7.4-fpm的容器

其中[app_path]为宿主机下的应用路径,建议可以在宿主机上建议一个www目录,用于存放所有的应用,这样的话,在有新的应用需要部署的时候,就不用因为修改映射而重新启动容器,所以可以直接写为

1
docker run -v /www/:/www/ --name php-fpm -d php:7.4-fpm

这样的话PHP-FPM容器就已经启动好了,我们可以在宿主机的/www/路径下放置我们的应用程序

  • (可选)安装Composer

部分PHP应用可能会用到Composer,我们需要进入容器中安装Composer

1
docker exec -it php-fpm /bin/bash

该命令表示在php-fpm容器中运行bash,开启一个终端,开启后我们在终端中安装Composer

1
2
3
4
5
6
apt update
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('sha384', 'composer-setup.php') === '756890a4488ce9024fc62c56153228907f1545c228516cbf63f885e036d37e9a59d27d63f46af1d4d07ee0f76181c7d3') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"
mv composer.phar /usr/local/bin/composer

部分Composer组件需要从Git安装,可以在容器内安装Git

1
apt install -y git

如此一来就可以直接在容器中执行composer命令了,如果应用需要,直接进入/www/[app_name]路径,执行composer install等命令即可。

Composer主要为composer.phar文件,也可以手动下载,可以参考Composer官方下载指引Composer (getcomposer.org)

到这里php-fpm就已经部署完毕了

  • Tips

在部署应用中可能会遇到php返回500等权限问题,建议在容器中进入应用路径,将应用文件的所有者修改为www-data

1
2
3
docker exec -it php-fpm /bin/bash
cd /www/[app_name]/
chown -R www-data:www-data *

0x03 Nginx

在开发环境或者单机生产环境下,Nginx不存在横向扩容,使用效果与直接安装Nginx类似,但使用容器来安装Nginx对于应用未来的扩展性会更好

  • 从官方拉取Nginx镜像
1
docker pull nginx

该命令默认会拉取tag为latest的Nginx镜像,官方维护的latest镜像通常为最新版本,默认使用最新版即可

  • 准备Nginx配置文件

在启动Nginx服务之前,我们需要提前写好Nginx的配置文件,建议新建一个目录用于存放Nginx配置

1
2
mkdir /etc/nginx
vim /etc/nginx/site.conf

这里给出一份我配置的PHP应用的站点配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
server {
listen 80;
listen [::]:80;
server_name [server_name];
root [root];
index index.html index.htm index.php;

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

location ~ \.php$ {
root [root];
fastcgi_pass [php-fpm_ip]:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME [root]$fastcgi_script_name;
include fastcgi_params;
}
}

该配置文件中存在三个参数需要自己填写

站点域名server_name

站点根目录root,这里的根目录为后续映射到容器内的应用路径,且需要与PHP-FPM内路径一致,否则该配置需要修改

PHP-FPM容器的IP地址php-fpm_ip,该IP地址可以通过命令docker inspect php-fpm查看php-fpm容器的元数据,找到IPAddress这项,填入即可,通常为172.17.0.x

  • 启动Nginx容器

在Nginx镜像就绪之后,我们就需要启动一台Nginx容器来运行服务

1
2
3
docker run -v [conf_path]:/etc/nginx/conf.d/ \
-v [app_path]:[app_path] \
-d -p 80:80 --name nginx nginx

这行命令表示启动一台名为nginx的Nginx容器,并将宿主机的80端口映射到容器的80端口,并且将宿主机的[conf_path]挂载到容器的/etc/nginx/conf.d/,将宿主机的[app_path]挂载到容器的[app_path]

该命令的两个参数

conf_path可以为我上述建议的新建的Nginx配置目录/etc/nginx/

[app_path]为站点文件的路径,可以与php-fpm的部署建议一样,将www路径统一映射进去,即写为

1
2
3
docker run -v /etc/nginx/:/etc/nginx/conf.d/ \
-v /www/:/www/ \
-d -p 80:80 --name nginx nginx

如此一来,PHP+Nginx的运行环境便已经部署完毕

0x04 Tips

  • 在部署和使用过程中如果出现异常,可以善用docker logs -f命令查看两个容器的日志信息,根据日志信息定位相应问题并修复

  • 如果调整了配置文件,可以通过docker restart nginx重启Nginx容器,PHP-FPM同理

  • 对于Laravel应用来说,php官方镜像没有带zip扩展和pdo_mysql扩展,需要手动进入容器安装

1
2
3
4
5
6
7
docker exec -it php-fpm /bin/bash
apt update
apt install -y libzip-dev
docker-php-ext-configure zip
docker-php-ext-install zip
docker-php-ext-configure pdo_mysql
docker-php-ext-install pdo_mysql
  • 有时候php扩展和一些程序不生效,可以进入php-fpm容器,重写php.ini
1
2
3
4
docker exec -it php-fpm /bin/bash
cd /usr/local/etc/php/
cp php.ini-development php.ini
vim php.ini