Docker化PHP环境

Docker的一个重要用途就是标准化开发-测试-部署环境,避免因环境的不同而产生的问题。

本文以Laravel框架为例子来搭建Docker化的PHP环境。


最佳实践

最佳实践是Docker官方给出的指导方针和推荐。其中有一点很重要:Run only one process per container,即在一个容器中只运行一个进程,这样就可以很方便的重用及更换容器(镜像)。

按照这个理念,我们将建3个容器分别运行Nginx、PHP-FPM、MySQL。

PHP-FPM

使用PHP-FPM官方镜像,版本方面使用稳定的5.6版。

由于Laravel需要一些PHP扩展,可能需要对这个镜像做一些修改,先用原始版本试一下。

docker run --name php-fpm -v `pwd`/wwwroot:/var/wwwroot -d php:5.6-fpm

通过其Dockerfile可知,php-fpm将运行在容器的9000端口上,无须将其映射到宿主机,容器间可以通过--link连接。

通过命令可以看到,我们把当前目录下的wwwroot映射到了/var/wwwroot,php-fpm将在这个目录下进行脚本解析。

Nginx

对PHP而言,Nginx(或其它web server)是必要的。对于全栈后端语言(比如Node.js),Nginx不是必要的,但通常也会用其做反向代理。

这里使用官方Nginx镜像,使用稳定的1.10版。

docker run --name nginx --link php-fpm -p 80:80 -v `pwd`/nginx/conf/nginx.conf:/etc/nginx/nginx.conf:ro -v `pwd`/nginx/conf/conf.d:/etc/nginx/conf.d:ro -v `pwd`/wwwroot:/var/wwwroot -d nginx:1.10

从命令可以看到,我把容器的80端口映射到可宿主机的80端口。该容器连接到了php-fpm,可以以此为主机名通过它做fastcgi。为方便起见,与php-fpm一样,把网站源码映射到了/var/wwwroot,另外还把nginx配置做了一下映射(相应的文件都是事先从容器里拷出来的)。

phpinfo()测试

在wwwroot下创建index.php,内容为:

<?php

phpinfo();  

在nginx/conf/conf.d下创建php.conf,内容如下:

server {  
    listen       80;
    server_name  laravel.docker;

    index index.html index.php;
    root /var/wwwroot;
    location / {
        try_files $uri $uri/ /index.php$is_args$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass php-fpm:9000;
        include fastcgi_params;
        fastcgi_index index.php;
        fastcgi_param  SCRIPT_FILENAME    $document_root$fastcgi_script_name;
    }

}

注意server_name字段,我是在Mac上做测试,所有是用hosts设置的域名关联的虚拟机的ip地址,如果在线上,还可以直接绑域名。

检查nginx配置并重载配置:

docker exec nginx nginx -t  
docker exec nginx nginx -s reload  

可以看到,这是进入容器并执行了相关命令。

好了,访问laravel.docker,如果没出什么问题的话,就可以如下图看到phpinfo()的输出。经检查,Laravel需要的OpenSSL、PDO、Mbstring、Tokenizer扩展都是有的,但是没有pdo_mysql,这个我们一会再看。 phpinfo -wide

MySQL

MySQL同样采用官方镜像,使用5.7版本。查看其Dockerfile,数据库存储在Volume里,容器路径为/var/lib/mysql,我会把本地路径挂载到这里用于存储。命令如下:

docker run --name mysql -v `pwd`/db:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:5.7

命令中设置了root密码,为了安全,容器运行后注意更改。在Mac上,会有文件权限问题,参照这里解决。

Laravel测试

在这之前,你需要重启(stop、rm然后重新启动,docker不允许对已经创建的容器更改参数)php-fpm并link上mysql,然后重启nginx。

在wwwroot目录下执行下面的命令安装Laravel:

composer create-project --prefer-dist laravel/laravel

更改之前的php.conf:

...
root /var/wwwroot/laravel/public;  
...

用前面提到的方法挂载nginx配置,按laravel的要求设置好storage目录的权限问题。再访问laravel.docker,就能看到Laravel的初始界面了。 laravel -wide

按照Laravel quickstart guide来跑一遍基本功能,并使用MySQL,.env文件可以这样设置:

DB_CONNECTION=mysql  
DB_HOST=mysql  
DB_PORT=3306  
DB_DATABASE=laravel  
DB_USERNAME=root  
DB_PASSWORD=my-secret-pw  

MySQL的操作可以这样进行:

docker exec -ti mysql mysql -uroot -p

Laravel artisan命令最好在容器中进行,不然有权限问题,用下面的命令进入容器,再在对应的位置执行命令:

docker exec -ti php-fpm bash

果然,就像前面提到的,找不到PDO驱动,先安装解决手头的问题(后面会把它放到镜像中):

docker exec php-fpm docker-php-ext-install  pdo_mysql

quickstart guide可以顺利完成。至此,所有环境都搭好了。

工程化--Docker Compose

前面所有容器都是在命令行上用docker run来创建并运行的,由于本文涉及到3个容器,涉及到多个Volume的挂载,涉及到容器间的连接,命令比较长,很不方便。

这时就需要Docker Compose了,它是用来管理有多个容器的应用的。上面的命令汇成了下面这个配置文件docker-compose.yml:

version: '2'  
services:  
    nginx:
        image: nginx:1.10
        volumes:
            - ./nginx/conf/nginx.conf:/etc/nginx/nginx.conf:ro
            - ./nginx/conf/conf.d:/etc/nginx/conf.d:ro
            - ./wwwroot:/var/wwwroot:ro
        ports:
            - "80:80"
        links:
            - php-fpm
    php-fpm:
        build:
            context: .
            dockerfile: Dockerfile-php-fpm
        volumes:
            - ./wwwroot:/var/wwwroot
        links:
            - mysql
    mysql:
        #Enable not in MAC
        image: mysql:5.7
        #Enable in MAC
        #build:
        #    context: .
        #    dockerfile: Dockerfile-mysql-mac
        volumes:
            - ./db:/var/lib/mysql
        environment:
            - MYSQL_ROOT_PASSWORD=my-secret-pw

其中用到了两个Dockerfile,分别是用来解决Mac上权限问题的Dockerfile-mysql-mac:

FROM mysql:5.7

RUN usermod -u 1000 mysql  
RUN mkdir -p /var/run/mysqld  
RUN chmod -R 777 /var/run/mysqld  

以及包含了pdo-mysql的php-fpm:

FROM php:5.6-fpm

RUN docker-php-ext-install  pdo_mysql  

现在你只要执行以下命令,三个容器都运行起来了:

docker-compose up -d

上面的代码已经放到GitHub中,如果需要,可以直接clone。

---全文完---