在docker中安装php运行环境

docker基础和速查手册可以参考:gitbook或者docker官网,本博客不再复述。

平时工作会同时用到windows和mac系统,要在这些平台上开发测试php,难免要熟悉每个操作系统的命令和环境搭建,这其实是完全不必要的学习成本,因为我最终都是在使用linux平台部署,docker就是为了抹平不同操作系统带来的差异而生的。

为什么不用虚拟机呢?因为虚拟机需要模拟一套硬件环境,对机器性能要求太高,我的mac pro表示压力很大。

下面会首先尝试在docker中安装官方的centos6镜像,之后通过加载这个镜像并执行bash环境,在命令行中为其安装sshd和nginx服务并保存修改后的镜像,最终实现可以在宿主机中通过ssh登录容器以及访问容器中的nginx。

为了方便开发,我会通过数据卷技术映射一个本地目录到容器中,这样我就可以在宿主机目录下开发代码,并实时的访问容器中的nginx来验证效果。

同时,我也可以通过ssh登录容器,使用熟悉的linux操作环境来完成我想做的事情,大大提升我的工作效率和心情。

下载镜像

docker官方镜像被gfw墙的基本没法用,所以我们选择下载国内的私有镜像:

这个是从daocloud.io这个私有镜像网站下载centos的6版本镜像,下载完成后可以看一下:

为何不用centos7呢?主要是因为有一个bug导致无法使用systemctl命令,直到centos7.2才能解决,但是目前daocloud.io还没有更新到这个版本,所以我就用centos6了。

启动容器+bash

-t指定打开虚拟终端,-i采用交互模式,这样就启动了centos6镜像的一个容器,并进入了bash shell命令行环境。

安装ssh服务

启动sshd服务

添加一个普通用户work用于后续登录

安装nginx

先安装vim编辑器

添加一个nginx的yum镜像源(可以参考nginx官方文档):

安装nginx,启动并测试:

保存镜像

由于我们给原生镜像安装了ssh服务和nginx,为了以后直接使用,所以需要把当前的容器保存为一个镜像文件。

执行命令时不需要退出容器(当然退出也没关系),这个过程需要花费一点时间,我给新的镜像起名名字为centos6,Tag为v1版本,它保存的是内容来源于docker容器ID:698e8138b0e7。

重启容器并映射端口

ctrl+D退出当前的容器,然后用刚刚保存的镜像创建新的容器:

可见镜像名字发生了变化,并且ssh命令已经存在,其中-p 22:22是将容器内的22端口映射到宿主机的22端口,也就是我可以通过访问mac的22端口连接到容器的22端口,另外-p 8099:80是将容器内的80端口映射到宿主机的8099端口,端口映射的方法可以参考这里

接下来我们把sshd进程再次启动一下,因为docker启动操作系统的方式比较特殊的原因,linux的开机启动配置不会生效,因此在后面我会通过编写dockerFile的方式为容器配置启动命令,这样就可以达到一样的效果了。

接下来,可以在mac上开另外一个终端执行ssh localhost,就可以连到容器内了,这个过程就和连接一个远程的服务器没有什么体验上的差别了:

可见,我们成功登录到容器中去了,也可以通过curl localhost:8099访问到容器内的80端口nginx服务:

数据卷挂载主机目录

为了可以在mac上用IDE写代码,访问docker容器中的nginx可以立即看到改变,因此可以利用数据卷功能将宿主的一个目录映射到容器中的某个目录中,这样宿主和容器之间可以互相得到变化,即便容器退出数据也保存在宿主机上,具体可以参考这里

我们退出之前的容器,变换命令再次启动:

这次通过-v命令指定将宿主机的/Users/liangdong/Documents/docker/centos7/web目录挂载到容器的/home/work/web目录。

此后就可以在容器中看见这个目录了,也可以读写文件:

在宿主机也可以看到修改:

桥接到物理网络

前面-p/-P指定端口映射的方式,其实是通过iptables配置NAT实现的,无论是从容器访问外网还是外网访问容器,都是NAT。

但是这种方式在具体实施时还是有问题的:因为每个容器的IP都是相对于宿主机内部的虚拟IP,如果要对外服务则需要将容器端口映射到宿主机的物理IP+PORT。我们知道,服务集群的话一般是每个节点(容器)将自己注册到一个类似zookeeper的服务注册中心,但容器自己的IP是虚拟的,根本没有意义。

为了解决这种问题,最好能让容器直接联到物理局域网中,实现的方法就是将容器的虚拟网卡桥接到物理网卡上,然后通过物理路由器的DHCP动态分配得到物理网段的局域网IP,和传统虚拟机接入物理局域网的方式是一样的,这样就不用再去为每个容器映射什么端口了,每个容器看起来像是一台独立的物理机,拥有独立的IP和MAC地址,可以和任何一个局域网内的其他用户互相通讯。(站在容器角度看,其默认路由是网桥;站在宿主机角度看,虚拟机网卡挂在桥接上

但是问题来了,如果你是在windows或者mac上玩docker的话,要实现这个目标就要麻烦很多。这是因为docker是基于cgroup和namespace进行资源隔离的,而这些特性只有linux实现了,所以docker在windows和mac上无法原生运行,需要在一个linux虚拟机环境中安装docker才行。当前我是直接使用了官方的mac安装包,它会默默的启动vm虚拟机,并没有给我访问虚拟机linux环境的入口,所以无法给虚拟机配置网桥,原因参考这里

为了解决在windows和mac上可以给docker配置物理网桥访问物理局域网的目的,可以自己安装一个linux虚拟机(比如用virtualbox),先在宿主机中创建一个网桥并让eth0挂到上面,之后令虚拟机的eth0桥接到宿主机上的网桥,再之后在虚拟机中令docker daemon启动容器时将容器的eth0桥接到虚拟机的网桥,这样docker容器相当于跨过2个虚拟交换机连接到物理局域网,我就不在此做演示了,网桥和配置的方法可以参考另外一篇博客

使用DockerFile

前面提到,docker容器启动后并不会自启动sshd服务/nginx服务,即便在容器里配置了开机启动也是如此。

另外,除了我将开发目录通过数据卷映射到了容器内外,像nginx和php日志这样的文件是不希望容器重启就丢失的,所以需要用到匿名数据卷。

这些都可以通过DockerFile配置,都可以很简单的解决,下面我将编写一个适用于我的dockerFile,主要目的是完成sshd/nginx服务的自启动以及将nginx日志目录挂载到数据卷以便持久化,具体命令可以参考详细文档

创建一个空目录,编写Dockerfile如下:

Dockerfile是用来构建镜像的,不是启动镜像的,这是一个很容易误解的地方。这里我基于之前commit保存的centos6:v1镜像为基础,在构建时将宿主机上编写的一个entry.sh添加到镜像的/root目录下,并且配置镜像被容器加载的时候执行这个entry.sh脚本。

那么看一下entry.sh脚本做了什么:

启动ssh和nginx服务,最后exec执行/bin/bash这个二进制程序。现在,我们来基于Dockerfile的配置,构建一个新的镜像,它支持服务自启动,并且最终进入bash执行环境。

可见,这个新的镜像经过了3层的构建,最终生成为centos6:latest:

现在万事俱备,我们修改一下容器的启动命令,让它进入后台运行并停留在bash环境不要退出:

这里删除了-i参数,多了-d参数,意思是让容器进入后台运行而不是在前台显示,保持-t参数是令其启动伪终端,这样bash程序才不会退出,容器才能保持运行。当然也有其他办法,比如:在entry.sh的末尾添加一个while true;do sleep 1; done的死循环,也是没有问题的。

查看容器处于运行状态,ssh localhost和curl localhost:8099均正常,万事大吉。

安装php和mysql

掌握了上面的方法,安装它们就没有什么复杂度了,可以再次以交互模式进入现在的容器进行安装,如果entry.sh入口文件需要修改,可以直接在容器中修改,或者先commit保存镜像,再通过Dockerfile重新build一次镜像,如果理解上面的流程,那么具体操作都可以灵活调整,这里就不做演示了,下面只记录一下遇到的问题。

  • 安装php55环境命令:yum install php55w  php55w-bcmath php55w-cli php55w-common  php55w-devel php55w-fpm    php55w-gd php55w-imap  php55w-ldap php55w-mbstring php55w-mcrypt php55w-mysql   php55w-odbc   php55w-pdo   php55w-pear  php55w-pecl-igbinary  php55w-xml php55w-xmlrpc php55w-opcache php55w-intl php55w-pecl-memcache php55w-pecl-redis
  • 缺少libmcrypt.so可以去这里找自己操作系统对应的rpm包下载,然后rpm -ivh 包名 完成安装。

后续应该尝试分布式docker的一些开源产品,例如mesos+marathon的paas解决方案,也会看一下consul相关的服务注册&发现的解决方案。

如果文章帮助您解决了工作难题,您可以帮我点击屏幕上的任意广告,或者赞助少量费用来支持我的持续创作,谢谢~