用 Docker 构建 PHP 开发环境

原文连接php

,若是感兴趣或者对美股感兴趣能够加我微信: xiaobei060537, 一块儿交流 😝。html

1. 前言

1.1 为何要用 Docker ?

是否有这样的场景,你搞了一个项目,在本地开发时须要搭建环境,放到线上时也须要搭建环境,到公司想暗戳戳玩一下要搭建环境,不搭还不行,由于你的环境依赖还挺多。这个时候若是有了Docker,只须要在机器上装个Docker,放上写好的Dickerfile,一行命令就自动完成这个事,方便又高效,岂不是很爽?mysql

1.2 准备

接下来,本文介绍如何搭建一个PHP的开发环境,将用 zPhal-dockerfiles 作为例子,这是我为个人博客系统准备的一套Dockerfile。linux

如今不论是windows,mac仍是linux,docker均可以很好支持,包括Windows系统,在win10系统下Docker for Windows 其实仍是挺不错的,就是比较吃内存。nginx

经过Docker命令行,咱们能够作不少事情,拉取镜像,运行容器,容器内执行命令等,可是如今,咱们要用更加简单粗暴的方式,编写好dockerfiles文件,而后经过docker-compose管理好这些文件,简化操做流程。git

什么是Dockerfile?
Dockerfile是由一系列命令和参数构成的脚本,这些命令应用于拉取的基础镜像并最终建立一个新的镜像,经过Dockerfile咱们能够建立一个你须要的镜像,里面是包含了你要安装的软件,至关因而提早定制好要安装的拓展,执行的命令等,而后一键执行,极大地简化操做流程。github

按照本文来搭建环境,你须要:web

  • 首先了解一下Docker以及Docker的一些基本操做,还有docker-compose是什么。
  • 而后须要安装 Docker 和 docker-compose,我将使用 docker-compose 来管理个人 dockerfiles。

注意,编写 dockerfile 是活的,不是死的,每一个人写出来的 dockerfile 都会不同,取决于你的需求。redis

Docker的官方文档很是清楚,虽然是英文,可是基本上什么都有,有问题上文档翻是很是明智的:Docker Documentationsql

2. 开始编写

接下来都是以 zPhal-dockerfiles 为例子,完整的能够点连接进去看,下面的只是片断。

2.1 预览

首先,咱们来看一下,我建立的这个dockerfile项目,我大概分红了下面的目录(固然这个是本身定的,并非要求这么去排版你的文件):

zPhal-dockerfiles
    app/
        index.php
        phpinfo.php
    data/
        .gitignore
    files/
        mysql/
            conf.d/
                mysql-file.cnf
            Dockerfile
        nginx/
            conf.d/
                default.conf
                zphal.conf
            Dockerfile
            nginx.conf
        php/
            pkg/
                .gitignore
            Dockerfile
            php.ini
            php-dev.ini
            php-fpm.conf
        redis/
            Dockerfile
        docker-compose.yml
    logs/
    .gitgnore
    README.md
复制代码

在这个项目里,我用到PHP,MySQL,Nginx,Redis;以及Composer,Phalcon拓展等。

总的来讲,咱们作这件事有三个流程:编写好各个软件的dockerfile;编写好配置文件;经过docker-compose处理全部的dockerfile,包括将配置配置文件扔进去 dockerfile 文件将构建的镜像中。

2.2 编写 Dockerfile 文件

2.2.1 PHP

下面是PHP的Dockerfile:

FROM php:7.2-fpm
MAINTAINER goozp "gzp@goozp.com"

#  设置时区
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

#  更新安装依赖包和PHP核心拓展
RUN apt-get update && apt-get install -y \
        git \
        libfreetype6-dev \
        libjpeg62-turbo-dev \
        libpng-dev \
    && docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \
    && docker-php-ext-install -j$(nproc) gd \
        && docker-php-ext-install zip \
        && docker-php-ext-install pdo_mysql \
        && docker-php-ext-install opcache \
        && docker-php-ext-install mysqli \
        && rm -r /var/lib/apt/lists/* # 将预先下载好的拓展包从宿主机拷贝进去 COPY ./pkg/redis.tgz /home/redis.tgz COPY ./pkg/cphalcon.tar.gz /home/cphalcon.tar.gz # 安装 PECL 拓展,这里咱们安装的是Redis RUN pecl install /home/redis.tgz && echo "extension=redis.so" > /usr/local/etc/php/conf.d/redis.ini # 安装第三方拓展,这里是 Phalcon 拓展 RUN cd /home \ && tar -zxvf cphalcon.tar.gz \ && mv cphalcon-* phalcon \ && cd phalcon/build \ && ./install \ && echo "extension=phalcon.so" > /usr/local/etc/php/conf.d/phalcon.ini # 安装 Composer ENV COMPOSER_HOME /root/composer RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer ENV PATH $COMPOSER_HOME/vendor/bin:$PATH RUN rm -f /home/redis.tgz \ rm -f /home/cphalcon.tar.gz WORKDIR /data # Write Permission RUN usermod -u 1000 www-data 复制代码

第一行定义了基础镜像,这里咱们用了PHP7.2的fpm版本,这里第二行定义了一个维护者。

接下来定义了时区,在每个dockerfile都定义了这一句,主要是为了使全部的容器的时间都与宿主机同步,其实咱们能够在docker-composer.yml文件中这么定义:

services:
  php-fpm:
    volumes:
      - /etc/localtime:/etc/localtime:ro
复制代码

可是在非linux系统,好比Windows中运行时,咱们不能取到/etc/localtime,为了更大兼容全部平台,我把时间同步写到dockerfile中。

接下来安装一些拓展,其实安装拓展的过程相似于咱们徒手在linux中安装PHP拓展,值得一提的是composer。我将Composer直接安装在了php-fpm的镜像中,其实官方也提供了Composer的镜像,拉取composer镜像执行也能够达到目的,由于咱们使用composer只是为了执行composer命令来管理咱们的包,若是composer单独是一个容器的话,咱们在不用时,还能够将容器关掉;可是在这里,我直接将composer装进php-fpm镜像中,主要是个人项目安装了一些PHP拓展,在编写composer.json文件时,我定义了extension的依赖,这样composer执行时会检查环境是否安装了这些依赖,全部若是我直接用composer镜像的话,还须要把我用的拓展安装到镜像里,就麻烦多了,因此我直接在php镜像中就把这个事作了,其实没什么区别,取决于你怎么用。

2.2.2 Nginx

下面是 Nginx 的 dockerfile:

FROM nginx:1.12

#  set timezome
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
复制代码

这个就简单多了,我只设置了一个时间。由于我不须要安装其它的东西,能够直接使用官方的镜像。

固然,咱们须要修改配置文件,只要事先写好配置文件就行,最后在 docker-compose.yml 文件中,将配置文件扔进去,这个下面会讲,包括PHP的配置文件,MySQL的配置文件,都是同样的。

2.2.3 MySQL

下面是 MySQL 的 dockerfile:

FROM mysql:5.7

#  set timezome
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
复制代码

MySQL也没有什么特别之处,直接使用官方的镜像。

2.2.4 Redis

下面是 Redis 的,也直接使用官方镜像:

FROM redis:3.2

#  set timezome
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
复制代码

2.3 编写配置文件

如何处理配置文件呢,我将配置文件进行归类,PHP的配置文件放在PHP目录下,Nginx的配置放在Nginx目录下,至于要不要再新建一个子文件夹就看状况了,好比conf.d文件夹。

下面以Nginx配置文件为例,首先nginx目录是这样的:

nginx/
    conf.d/
        default.conf
        zphal.conf
    Dockerfile
    nginx.conf
复制代码

除了nginx.conf外,还有一个子文件夹conf.d用来存放全部的域名配置文件,在linux下搭建过php环境的应该都比较熟悉。这些配置文件就是咱们到时候要传进去容器中的文件,咱们并不会在宿主机使用这些文件。

因此须要注意的最重要一点就是,配置文件中出现的路径是容器内环境的路径,而不是宿主机的路径,每个容器内都有一个运行环境,都是一台微型小系统,这些路径都是容器内的路径。咱们能够经过挂载与容器内通信来同步文件,在命令行启动容器也须要挂载文件路径,而如今挂载这一步咱们也用docker-compose来解决。

下面是一个配置文件示例:

server {
    listen 80 default;
    index index.html index.htm;
    server_name localhost docker;

    root /data/www;
    index index.php index.html index.htm;
    location / {
        try_files $uri $uri/ /index.html;
    }

    location ~ \.php {
        include fastcgi_params;
        fastcgi_pass php-fpm:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME /data/www/$fastcgi_script_name;
    }
}
复制代码

root /data/www中,/data/www路径,是到时候nginx容器的路径,而不是当前在操做的宿主机的路径,因此到时候咱们要挂载web程序放的位置到这个路径。

2.4 编写 docker-compose.yml

在php,nginx等目录的同级,咱们建立一个 docker-compose.yml,咱们在执行 docker-compose 相关命令时,会自动找到这个文件,并根据里面的内容来执行。

接上面 nginx 的例子,咱们先谈挂载,由于这是最重要的一步。在 docker-compose.yml 中,nginx 的部分:

nginx:
    build: ./nginx
    depends_on:
      - php-fpm
    links:
      - php-fpm:php-fpm
    volumes:
      - ../app:/data/www:rw
      - ./nginx/conf.d:/etc/nginx/conf.d:ro
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ../logs/nginx:/var/log/nginx
    ports:
      - "80:80"
      - "8080:8080"
      - "443:443"
    restart: always
    command: nginx -g 'daemon off;'
复制代码

有一个 volumes 参数,这里就是咱们要挂载的目录的相关配置,第一条咱们将../app挂载到/data/www之中,也是咱们配置文件中定义的默认监听的root,而app目录是咱们宿主机中的一个目录,经过这样挂载咱们能够直接将咱们的项目文件放到app中,docker会帮你传输到容器内的/data/www目录下。

其它的参数:

  • build 定义了你的 dockerfile 在哪里,若是没有写 dockerfile 能够不用 build,能够用 images参数定义官方镜像,好比image:mysql:5.7
  • depends_on表示将依赖其它镜像,好比nginx依赖php-fpm,没有它我nginx无法玩;
  • links定义链接,好比要链接到php-fpm容器,就是php-fpm:php-fpm,后面是别名;
  • ports表示端口映射,80:80表示将80端口映射到宿主机的80端口
  • restart重启,restart: always表示将自动重启
  • command是自动执行的命令
  • ……

参数不少,更多的能够参考官方文档。

下面是一个完整的 docker-compose.yml 文件:

version: '3.2'
services:
  php-fpm:
    build: ./php/
    ports:
      - "9000:9000"
    links:
      - mysql-db:mysql-db
      - redis-db:redis-db
    volumes:
      - ../app:/data/www:rw
      - ./php/php-dev.ini:/usr/local/etc/php/php.ini:ro
      - ./php/php-fpm.conf:/usr/local/etc/php-fpm.conf:ro
      - ../logs/php-fpm:/var/log/php-fpm:rw
    restart: always
    command: php-fpm

  nginx:
    build: ./nginx
    depends_on:
      - php-fpm
    links:
      - php-fpm:php-fpm
    volumes:
      - ../app:/data/www:rw
      - ./nginx/conf.d:/etc/nginx/conf.d:ro
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ../logs/nginx:/var/log/nginx
    ports:
      - "80:80"
      - "8080:8080"
      - "443:443"
    restart: always
    command: nginx -g 'daemon off;'

  mysql-db:
      build: ./mysql
      ports:
        - "3306:3306"
      volumes:
        - ../data/mysql:/var/lib/mysql:rw
        - ../logs/mysql:/var/lib/mysql-logs:rw
        - ./mysql/conf.d:/etc/mysql/conf.d:ro
      environment:
        MYSQL_ROOT_PASSWORD: 123456
        MYSQL_DATABASE: zphaldb
        MYSQL_USER: zphal
        MYSQL_PASSWORD: zphal123
      restart: always
      command: "--character-set-server=utf8"

  redis-db:
      build: ./redis
      ports:
        - "6379:6379"
      volumes:
        - ../data/redis:/data
      restart: always
复制代码

3. 使用

这一套编写下来,咱们怎么用呢?

3.1 使用搭建好的环境

  1. 首先,进入项目dockerfiles的目录下,这里是files目录:
cd zPhal-dockerfiles/files

 wget https://pecl.php.net/get/redis-3.1.6.tgz -O php/pkg/redis.tgz
 wget https://codeload.github.com/phalcon/cphalcon/tar.gz/v3.3.1 -O php/pkg/cphalcon.tar.gz
复制代码

而后下载咱们会用到的PHP拓展包。

  1. 执行命令:
docker-compose up
复制代码

docker会自动经过编写好的docker-compose.yml内容构建镜像,而且启动容器。
若是没问题,下次启动时能够以守护模式启用,全部容器将后台运行:

docker-compose up -d
复制代码
  1. 关闭容器
    能够这样关闭容器并删除服务:
docker-compose down
复制代码

使用 docker-compose 基本上就这么简单,用 stop,start 等这些命令来操纵容器服务。而更多的工做是在于编写 dockerfile 和 docker-compose.yml 文件。

3.2 使用 Composer

当咱们要使用composer时怎么作呢? 咱们已经在php-fpm里安装了composer。

用 docker-compose 进行操做:

docker-compose run --rm -w /data/www/zPhal php-fpm composer update
复制代码

-w /data/www/zPhal为在php-fpm的工做区域,zPhal项目也是挂载在里面,全部咱们能够直接在容器里运行composer。

或者进入宿主机app目录下用docker命令:

cd zPhal-dockerfiles/app

docker run -it --rm -v `pwd`:/data/www/ -w /data/www/zPhal files_php-fpm composer update
复制代码

4. 注意事项

  • 注意挂载路径
  • 构建失败时,注意容器内是否报错
  • 加速镜像。若是过程下载镜像很慢,可使用国内的加速镜像服务,好比阿里云,Daocloud。