Docker Compose创建本地开发 PHP 应用的环境

Docker Compose可以让我们用一个文件来描述应用需要的环境,在上面定义应用需要的各种服务,比如web数据库脚本解释缓存等等,我们也可以配置服务需要的网络与数据卷。这篇文章会创建一个运行PHP应用的环境。

定义了下面这些服务:
* db:使用MySQL作为应用的数据库
* php:解释php脚本,使用php-fpm
* web:使用nginx作为应用的web服务器
* console:常用工具
* redis:缓存
* phpmyadmin:管理数据库的web界面

准备

安装并启动docker。为项目创建一个目录,在根目录下创建一个docker-compose.yml文件。

目录结构

你可以按自己的喜好组织项目,下面是一种方法,app目录放置应用的代码,services目录放置创建服务需要用的东西,有些服务需要我们自己去创建镜像,在一个Dockerfile文件里说明一下你想要的镜像是什么样的。在创建自定义镜像的时候也可能需要用到一些额外的文件,比如一些配置文件,一般在创建镜像的时候会把这些配置文件复制到镜像里。

├── app
│   ├── index.html
│   └── phpinfo.php
├── docker-compose.yml
└── services
    ├── console
    │   ├── Dockerfile
    │   └── composer.phar
    ├── web
    │   └── config
    │       └── default.conf
    └── php
        ├── Dockerfile
        └── config
            ├── opcache-recommended.ini
            └── php.ini

Compose 文件

Compose文件用的是yaml格式,文件一开始说明一下使用的compose版本,这个文件分成三个主要部分,services(服务)networks(网络)volumes (数据卷)。在这三部分主要的部分下面列出你需要的东西,docker会自动为compose的项目创建一个默认的网络,你可以添加自己想要的网络,然后分配给指定的服务使用。在volumes下可以列出创建的数据卷,在定义服务的时候,你可以指定使用的数据卷,还有它对应的要挂载到的容器里的位置。

version: '2'

services:
# 定义的服务

networks:
# 服务需要用的网络

volumes:
# 服务需要用的数据卷

db

db是数据服务,这里使用MySQL,先去给这个服务添加一个数据卷,然后在这个服务里使用一下这个数据卷,把数据库生成的数据放到这里,这样即使我们删除容器,数据服务里的数据也会保留在主机上,下回创建容器的时候,可以继续使用已有的数据。在volumes下面,添加一个名字是db的数据卷:

services:
# ...

volumes:
 db:
   driver: local

然后再去添加一个名字是db的服务,指定一下服务使用的镜像,这个镜像我们也可以用自己创建的 Dockerfile去创建一下,或者你不打算定制镜像,也可以直接使用现成的,这里我用了mysql:5.7.20这个镜像。注意最好设置具体要使用的版本。

这个镜像里有一些环境变量,我们可以在定义服务的时候去设置一下它们的值,这里我设置了root 用户的密码,你可以根据自己的需求去修改这些变量的值。

db服务上使用了volumes为它指定了一个db数据卷,挂载的位置是容器的/var/lib/mysql,这是存储数据库的默认的地方。

services:
  db:
    image: mysql:5.7.20
    environment:
      - MYSQL_ROOT_PASSWORD=6mBLn2f8
    volumes:
      - db:/var/lib/mysql

测试

在我们的compose文件里现在已经有了一个叫db的服务,在命令行工具下,进入到项目所在的目录,执行:

docker-compose up -d

Docker会按照compose文件里的指示,创建应用需要的网络,数据卷,还有一个名字是db的数据服务,你可以进入到这个服务容器里看一下:

docker-compose exec db bash

php

定义一个解释php的服务,在这里打算自己创建这个服务用的镜像,所以用了build,而不是image。这里告诉了docker自己要创建的这个镜像要使用的那个Dockerfile文件的位置(./services/php/Dockerfile)。

volumes下面是服务用的数据卷,我把compose文件所在目录下的app这个目录,挂载到了容器的/mnt/app这个位置上了。这样我可以直接修改项目的代码,然后立即看到结果。

services:
  db:
  # ...

  php:
    build:
      context: ./services/php
      dockerfile: Dockerfile
    volumes:
      - ./app:/mnt/app

Dockerfile

构建php服务的镜像用的Dockerfile./services/php/DockerfileFROM,设置了一下这个镜像要基于哪个镜像去创建,这里使用php:7.1-fpm这个官方提供的镜像。MAINTAINER设置了一下维护的作者。RUN了几行命令,主要是去安装一些软件包,比如一些额外的php扩展。在安装这些扩展之前 ,你需要先安装扩展依赖的一些其它的软件包,这里的libpng-devlibjpeg-dev,就是gd这个php扩展需要的软件包。然后我又COPY了两个配置文件到镜像里面。

FROM php:7.1-fpm
MAINTAINER lobber <lobber@gmail.com>

RUN apt-get update && apt-get install -y libpng-dev libjpeg-dev \
 && rm -rf /var/lib/apt/lists/* \
 && docker-php-ext-configure gd --with-png-dir=/usr --with-jpeg-dir=/usr \
 && docker-php-ext-install gd mysqli pdo_mysql zip opcache redis


COPY ./config/php.ini /usr/local/etc/php/conf.d/
COPY ./config/opcache-recommended.ini /usr/local/etc/php/conf.d/

php.ini

自定义的php配置。把你想要的php配置放到这个文件里,重新build镜像,然后再次启动php服务的时候会用新的镜像重新创建容器,这样配置就会生效了。

memory_limit = 256M
post_max_size = 100M
upload_max_filesize = 100M

opcache-recommended.ini

这是phpopcache扩展的配置文件。所以在开发阶段可以关掉opcache功能,在生产环境打开它,会提高代码的执行效率。

opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=4000
opcache.revalidate_freq=60
opcache.fast_shutdown=1
opcache.enable_cli=1
# 关闭 opcache 可以让 opcache.enable=0,打开 opcache 可以让 opcache.enable=1
# 在开发应用的时候可以关掉 opcache 。
opcache.enable=0

web

web服务在这里使用nginx,用image指定了要使用的镜像,ports设置了发布的端口号,让主机上的80对应容器的80depends_on可以设置服务的依赖,我让web服务依赖之前定义的 php,这样会先启动php,然后再启动webvolumes_from设置了让这个web服务使用在 php服务里定义的数据卷。

另外这里又用了volumes自己设置了一个数据卷,让主机上包含nginx配置文件的目录,对应容器里的nginx服务的配置文件目录,这样我就可以直接在本机上修改nginx的配置文件,然后重新启动一下web服务,配置就可以生效了。这种方法适用在开发环境上,因为修改了配置不需要重新 build镜像,在生产环境中,你需要自己build这个nginx镜像,把想要的配置直接复制到镜像里去。

services:
  db:
  # ...

  php:
  # ...

  web:
    image: nginx:stable
    ports:
      - "80:80"
    depends_on:
      - php
    volumes_from:
      - php
    volumes:
      - ./services/web/config:/etc/nginx/conf.d

default.conf

./services/web/config/default.conf,这是一个最基本的nginx配置文件,设置了应用的 root是在/mnt/app这里,我们已经把主机上的app目录挂载到了容器里的/mnt/app这个地方。

注意这条指令:fastcgi_pass php:9000; 这里的php是我们定义的php这个服务的名字,在 compose文件里定义的服务,它们之间可以使用服务的名字相互进行沟通,因为docker内置了 DNS 功能。

server {
  listen       80;
  server_name  localhost;
  root         /mnt/app;
  index        index.php index.html index.htm;

  location / {
     try_files $uri $uri/ /index.php?$query_string;
   }

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

console

在开发应用的时候还会用到一些小工具,比如php包管理composer等等。我想把它们单独放到一个服务里面,这个服务用的镜像我需要自己去创建。

services:
  db:
  # ...

  php:
  # ...

  web:
  # ...

  console:
    build:
      context: ./services/console
      dockerfile: Dockerfile
    volumes_from:
      - php
    tty: true

Dockerfile

./services/console/Dockerfile,这是为 console 服务准备的创建镜像用的文件。

FROM php:7.1
MAINTAINER wanghao <wanghao@ninghao.net>

WORKDIR /mnt/app

# 常用工具
RUN apt-get update && apt-get install -y git curl wget cron vim locales libzip-dev libfreetype6-dev mysql-client \
  && rm -rf /var/lib/apt/lists/* \
  && pecl install zip \
  && docker-php-ext-enable zip \
  && docker-php-ext-install mysqli pdo_mysql

# 把语言设置成简体中文
RUN dpkg-reconfigure locales && \
  locale-gen C.UTF-8 && \
  /usr/sbin/update-locale LANG=C.UTF-8
RUN echo 'zh_CN.UTF-8 UTF-8' >> /etc/locale.gen && \
  locale-gen
ENV LC_ALL C.UTF-8
ENV LANG zh_CN.UTF-8
ENV LANGUAGE zh_CN.UTF-8

# 配置 git
RUN git config --global user.name "lobber" \
  && git config --global user.email "lobber1987@gmail.com"

# 安装与配置 composer
# Composer 官方安装脚本 https://getcomposer.org/download/
# 因为在国内下载 composer 太慢,我们直接把下载下来的 composer 复制到容器里使用。
COPY ./composer.phar /usr/local/bin/composer
RUN chmod +x /usr/local/bin/composer
RUN echo 'export PATH="$PATH:$HOME/.composer/vendor/bin"' >> ~/.bashrc \
  && . ~/.bashrc \
  && composer config -g repo.packagist composer https://packagist.phpcomposer.com

redis

redis服务可以当缓存用,WordPressDrupalLaravel这些应用都会用到它。定义这个服务只是简单的用了一个官方的镜像,设置了一个公布的端口号。

services:
  db:
  # ...

  php:
  # ...

  web:
  # ...

  console:
  # ...

  redis:
    image: redis:3.2.1
    ports:
      - "6379:6379"

phpredis

要使用Redis,除了需要redis服务以外,还需要安装phpredis这个php扩展。打开创建php服务镜像用的Dockerfile(./services/php/Dockerfile),下面这段代码是去下载指定版本的phpredis扩展:

ENV PHPREDIS_VERSION 3.0.0
RUN curl -L -o /tmp/redis.tar.gz https://github.com/phpredis/phpredis/archive/$PHPREDIS_VERSION.tar.gz \
 && tar xfz /tmp/redis.tar.gz \
 && rm -r /tmp/redis.tar.gz \
 && mkdir -p /usr/src/php/ext \
 && mv phpredis-$PHPREDIS_VERSION /usr/src/php/ext/redis

然后再用php官方镜像里的docker-php-ext-install这个脚本去安装php扩展

&& docker-php-ext-install ... redis

现在这个 Dockerfile 看起来是这样的:

FROM php:7.1-fpm
MAINTAINER wanghao <wanghao@ninghao.net>

ENV PHPREDIS_VERSION 3.0.0
RUN curl -L -o /tmp/redis.tar.gz https://github.com/phpredis/phpredis/archive/$PHPREDIS_VERSION.tar.gz \
 && tar xfz /tmp/redis.tar.gz \
 && rm -r /tmp/redis.tar.gz \
 && mkdir -p /usr/src/php/ext \
 && mv phpredis-$PHPREDIS_VERSION /usr/src/php/ext/redis

RUN apt-get update && apt-get install -y libpng-dev libjpeg-dev \
 && rm -rf /var/lib/apt/lists/* \
 && docker-php-ext-configure gd --with-png-dir=/usr --with-jpeg-dir=/usr \
 && docker-php-ext-install gd mysqli pdo_mysql zip opcache redis


COPY ./config/php.ini /usr/local/etc/php/conf.d/
COPY ./config/opcache-recommended.ini /usr/local/etc/php/conf.d/

phpmyadmin

phpmyadmin是管理mysqlmariadb数据库的web界面。这个服务用了 phpmyadmin/phpmyadmin这个镜像,发布了端口,让主机的8081对应容器的80端口,这样访问主机:8081的时候就会打开这个管理数据库的web界面,镜像里设置了一些环境变量,在这个服务里用了一些,主要是告诉phpmyadmin数据库的主机是谁,管理数据库使用的用户是谁,密码是什么。

services:
  db:
  # ...
  php:
  # ...
  web:
  # ...  
  console:
  # ...
  redis:
  # ...
  phpmyadmin:
    image: phpmyadmin/phpmyadmin
    ports:
      - "8081:80"
    environment:
      PMA_HOST: "db"
      PMA_USER: "root"
      PMA_PASSWORD: "6mBLn2f8"

启动

app目录下创建一个phpinfo.php,内容是:

<?php phpinfo(); ?>

进入到项目下面,执行:

docker-compose up -d

打开浏览器,访问:http://localhost/phpinfo.php
你会看到 php 信息页面。

发表评论