Docker搭建自己的PHP开发环境
1. 前言
1.1 为什么要用docker ?
是否有这样的场景,你搞了一个项目,在本地开发时需要搭建环境,放到线上时也需要搭建环境,到公司想暗戳戳玩一下要搭建环境,不搭还不行,因为你的环境依赖还挺多。这个时候如果有了docker,只需要在机器上装个docker,放上写好的dockerfile,一行命令就自动完成这个事,方便又高效,岂不是很爽?
1.2 准备
接下来,本文介绍如何搭建一个php的开发环境,将用 zphal-dockerfiles 做为例子,这是我为我的博客系统准备的一套dockerfile。
现在不管是windows、mac还是linux,docker都可以很好支持,包括windows系统,在win 10系统下docker for windows 其实还是挺不错的,就是比较吃内存。
通过docker命令行,我们可以做很多事情,拉取镜像,运行容器,容器内执行命令等,但是现在,我们要用更加简单粗暴的方式,编写好dockerfiles文件,然后通过docker-compose管理好这些文件,简化操作流程。
什么是dockerfile?
dockerfile是由一系列命令和参数构成的脚本,这些命令应用于拉取的基础镜像并最终创建一个新的镜像,通过dockerfile我们可以创建一个你需要的镜像,里面是包含了你要安装的软件,相当于是提前定制好要安装的拓展,执行的命令等,然后一键执行,极大地简化操作流程。
按照本文来搭建环境,你需要:
首先了解一下docker以及docker的一些基本操作,还有docker-compose是什么。
然后需要安装docker和docker-compose,我将使用docker-compose来管理我的dockerfiles。
注意,编写dockerfile是活的,不是死的,每个人写出来的dockerfile都会不一样,取决于你的需求。
docker的官方文档非常清楚,虽然是英文,但是基本上什么都有,有问题上文档翻是非常明智的: docker documentation 。
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 ""
设置时区
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
第一行定义了基础镜像,这里我们用了php 7.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 timezomeenv 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 timezomeenv 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的部分:
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 使用搭建好的环境
首先,进入项目dockerfiles的目录下,这里是files目录:
cd zphal-dockerfiles/files
wget -o php/pkg/redis.tgz
wget -o php/pkg/cphalcon.tar.gz
然后下载我们会用到的php拓展包。
执行命令:
docker-compose up
docker会自动通过编写好的docker-compose.yml内容构建镜像,并且启动容器。
如果没问题,下次启动时可以以守护模式启用,所有容器将后台运行:
docker-compose up -d
关闭容器:
可以这样关闭容器并删除服务:
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. 注意事项
注意挂载路径。
构建失败时,注意容器内是否报错。
加速镜像。如果过程下载镜像很慢,可以使用国内的加速镜像服务。