为什么要用Docker

说实话,如果在Windows下开发PHP程序,那么XAMPP足够了,一个安装包,什么都安装好了。但是如果要增加一些额外的依赖,比如需要的扩展的DLL不存在,需要编译安装一些扩展;或者需要同时维护不同版本的PHP,那么就需要下多个版本的XAMPP。当然也有可以切换PHP版本的工具,比如WAMP。但是依赖其实无处不在,比如切换了针对Apache的PHP版本,命令行的PHP版本也得相应的切换,或者Composer的版本。在这种情况下,很容易掉坑。

另外一方面,随着各种语言的层出不穷,工作中不可能死磕一种语言。新增一种语言,势必需要安装对应的环境,比如Python,需要2.7.x版本和3.x版本,Node,各种全局安装,Go的安装也是一通配置。而随着这些语言版本的升级,卸载又安装,配置又更改,时间一长,真的很容易一团乱。

针对开发环境问题,爱折腾的程序员开发了Vagrant这个工具。一个环境一个虚拟机,然后用自动化方法去管理这些虚拟机。这样一来,可以把各个项目的开发环境相隔离,一个项目一个镜像。这样不管多么复杂的依赖,都被隔离在镜像中,不会对其他的项目的环境造成影响。要删除这些安装程序和配置,只要把镜像删除掉就行了。非常干净,保证了操作系统不会留下各种垃圾文件。

到这里其实没有Docker什么事了,Vagrant把问题解决的差不多了。但是有两个痛点:

  1. Vagrant镜像大,一个项目一个镜像,SSD空间本来就吃紧,很快就空间不够啦
  2. 性能。Vagrant启动其实就是虚拟机启动,几分钟内也就搞定了,等等其实无所谓。通常开发的时候,会把代码所在的文件夹挂载到虚拟机上,对于文件多的项目,风扇就会狂转。

虽然有上面说的两个问题,但是由于有环境隔离的好处,选择Vagrant的程序员也就忍了。本来Docker刚出来的时候,也不是为了干掉Vagrant,大家相安无事,在各自的领域发光发热,Docker主要目标集中在方便部署上,一个项目,打包成几个容器,往服务器一放就搞定了。但是最近的几个改进,让爱折腾的程序员发现了Docker在本地开发环境上的潜力:

  1. 使用操作系统的虚拟化技术,不在依赖于Virtualbox
  2. 提升了卷的地位,使得文件映射和持久化更方便

这样一来,是时候用Docker取代Vagrant进行本地开发环境搭建啦。

开发环境编排

为了追赶潮流,各个部分的版本计划是这样的:

应用服务器:Nginx 最新版本 PHP: 当然是最新的7.x啦 MySQL:最新版本 phpMyAdmin:好用的MySQL可视化管理工具,最新版本

由于Docker容器默认情况下,停止的时候,运行中产生的修改都会丢失,但是在开发过程中,代码和数据是要保存下来的,所以需要另外两个数据容器:MySQL数据容器和代码数据容器。

确定好了组成部分后,可以把项目的文件结构设置成如下样子:

项目文件夹
├── LICENSE
├── README.md
├── app
│   └── basic
├── docker-compose.yml
├── nginx
│   ├── Dockerfile
│   └── default.conf
└── php
    ├── Dockerfile
    ├── install-composer
    └── sources.list.jessie
  1. 代码放在app目录下,这里作为示例,放了yii创建的basic应用
  2. niginx目录下放了单独的nginx容器构建文件,以及默认的PHP站点的nginix配置文件
  3. php目录下放了单独的php容器构建文件,因为Yii2有一些扩展是必须支持的,所以官方默认的php镜像是不能满足要求,所以需要基于官方的容器再安装一些必要的扩展;另外直接使用国外的源安装由于种种原因,速度实在令人遗憾,所以sources.list.jessie里放了163的源,构建的时候把这个源也放进容器

编写docker-compose.yml

version: "1.0"

services:
    nginx:
        build: ./nginx/
        ports:
            - 80:80
        volumes:
            - "${PROJECT_ROOT}:/var/www/html:ro"
        networks:
            - server
        depends_on:
            - php

    php:
        build: ./php/
        expose:
            - 9000
        volumes:
            - "${PROJECT_ROOT}:/var/www/html"
        networks:
            - database
            - server
        depends_on:
            - mysql

    mysql:
        image: mysql:latest
        volumes:
            - data:/var/lib/mysql
        networks:
            - database
        ports:
            - "3306:3306"
        environment:
            MYSQL_ROOT_PASSWORD: "${DB_ROOT_PASSWORD}"
            MYSQL_DATABASE: "${DB_NAME}"
            MYSQL_USER: "${DB_USERNAME}"
            MYSQL_PASSWORD: "${DB_PASSWORD}"

    phpmyadmin:
        image: phpmyadmin/phpmyadmin
        ports:
            - 8080:80
        networks:
            - database
        depends_on:
            - mysql
        environment:
            PMA_HOST: mysql

volumes:
    data:

networks:
    database:
    server:

采用最新的构建语法,大部分用depends_on代替links,用volumes列出需要持久化的mysql对应的数据文件。代码已经传到码云啦。https://gitee.com/linwx/Yii2Docker.git