个人主力机是windows,windows下面有太多提高效率的软件.可是开发的时候不得不使用linux.就单单开发而言.我仍是喜欢使用linux.因此就形成了我得在windows下面使用虚拟机.这是最开始的办法.后面得知有vagrant这个东西以后,用了一阵子感受还不错.可是我使用的时候动不动就会出现一些问题,因此一怒之下决定学学docker.而后使用docker来做为开发环境.php
使用docker做为开发环境大概我有这几点要求html
部署快,不要换台机子装了一天的环境linux
稳定...nginx
轻轻轻!git
container得能够访问本机所在局域网github
能够实现文件共享docker
在我接触了一阵子docker以后,发现docker能够知足我大部分意淫出来的美好开发环境.折腾一番以后终于搞定,因而祭出本文.但愿能够帮助到须要的人.shell
学习本篇以前但愿你对docker有一丢丢的了解,一丢丢就能够了.apache
我通常不喜欢讲如何安装一个软件,可是介于docker的一些问题.仍是讲讲.ubuntu
若是是windows10以前的用户,那么安装docker比较麻烦. 你可能须要一个Docker Toolbox
的东西,具体安装方式请自行google.由于个人机子是Windows10的.
若是你是Windows10的用户,恭喜你.你只要点这里下载一个exe文件,而后就能够无脑安装了.可是要保证开启Hyper-V
功能.如何开启看这里.注意,这个开启以后就不能使用virtualbox虚拟机了.
安装好以后,启动docker在左下角就能够看到docker的logo了.以后咱们的操做都是在PowerShell/CMD下面执行的了.执行docker info
会看到下面的内容
PS C:\Windows\system32\WindowsPowerShell\v1.0> docker info Containers: 1 Running: 1 Paused: 0 Stopped: 0 Images: 4 ........
因为docker主机在外国,安装好以后咱们须要更改下源,否则下载image的时候会很慢.这里使用daoCloud提供的镜像,你须要注册登陆以后,获取到每一个人独一无二的url.而后粘贴要下面就能够了.记得重启啊喂...
在使用docker以前你要明白两个概念,两个学docker过程当中必定会一直强调的概念
image
container (这种术语直接使用英文,不作翻译)
这两个是整个docker的基础概念,这里本着不负责任的侥幸心理大概的说一下这两个的区别.
image是静态的,类比为面向对象就是一个类
container是动态运行的,类比为面向对象就是一个实例化的对象.
通常,container是可运行的,咱们启动一个container以后,这个container里面就是咱们的linux环境.
懂得了上面的意思,你就明白了咱们要作的事情很简单:找一个合适的image,这个image里面应该包含一切开发时候所须要的东西, 而后启动它,咱们就能够在这个container环境上工做了.固然这个时候container应该能够跟宿主共享文件.而且能够在本局域网内能够被访问到.
在继续搭建咱们的开发环境以前,咱们仍是要先学一点docker的命令和概念的.
每一个image都有一个惟一的id来标识,一样container也有.这个惟一的id通常很长,好比:c59dc2dfad95
,可是通常咱们输入的时候只要输入若干位能标识当前系统内惟一标识某一个image就能够了.好比只要输入c59d
可能就能够标识这个image.除了id,还能够给一个image起名字,这样子也能够经过name来操做一个image.
经过docker run image_name
能够直接启动本地的一个image.这个命令后面能够加不少子参数来开启其余功能.若是本地不存在这个image,那么docker会去官方的仓库去下载,这个仓库你能够理解为github同样的网站,上面存放了许多别人push上去的image.
每一个image都有一个名称.除了名称以外还有一个叫作tag的东西,这个称之为标签的东西能够用来标识同一个image的不一样版本.若是你没有给一个image指定一个tag,那么docker会默认为这个iamge添加一个名为:latest的tag.若是你使用docker run ubuntu
,那么就会默认运行ubuntu:latest
.若是本地没有这个image,那么就会去从仓库下载ubuntu:latest
的iamge.不少时候你会看到ubuntu:14.04
的image.这个14.04就是表明这个image的tag.只是不少时候image制做者把tag用来标记version了而已.
这个命令会列出本地全部的images.每一个image都会有一个独一无二的id.以下面 IMAGE ID
字段.
PS C:\Windows\system32\WindowsPowerShell\v1.0> docker images REPOSITORY TAG IMAGE ID CREATED SIZE ubuntu-ok latest 5f93b91bc208 26 hours ago 423.7 MB ubuntu latest a421b4d8494d 27 hours ago 423.1 MB ubuntu 14.04 3f755ca42730 2 days ago 188 MB
这个命令会列出全部在运行的container.当运行docker ps -a
就会列出全部的container.包括已经退出的container.
这个命令能够把一个container制做成一个image.
docker rm container_id
能够用来删除一个container.docker rmi image_id/image_name
能够用来删除一个image.
不少文章讲docker都会把这个放到后面一点讲.反正不会在相似"使用docker作开发环境"的文章里面讲. 可是我以为这个东西是理解docker的关键.因此必定要讲.
AUFS比不是docker独有的,不少Linux的发行版中都用到了这个特性.提及AUFS,这个东西是UFS的升级版,前面的A就是表明advanced的意思.那AUFS/UFS究竟是个什么东西?
所谓AUFS,Advanced Union File System 就是把不一样物理位置的目录合并mount到同一个目录中.这种技术有一点典型的应用:有些linux发行版只要插入一个光盘就能够直接运行.不用进行安装.你对系统文件进行的增删改只是反映在电脑的硬盘上面,不会影响到光盘的内容.即对光盘只读不写.那么docker是如何使把这个技术应用到docker上?
docker把一个镜像分红了不少层layer.这些层合并在一块儿才成为了一个完整的image.这样子有什么好处?最直观的一点就是,ubuntu15.04跟ubuntu16.04的image可能只有一点点差异.这点差异体如今第四层layer上.那么ubuntu15.04跟16.04就能够共享前三层layer.这样子若是你本地有了ubuntu15.04的image.那么再pull ubuntu16.06的时候只要把第四层的pull下来就能够了.
并且,image的全部层都是只读的,当你启动一个image当作container运行的时候,docker会在image的只读层上加一层薄薄的可写层.你在container里面作的全部操做都是反映在可写层.当你退出container以后,下次启动同一个image,以前操做的全部东西都会没有掉.一个从新作人的image.
这个时候有一个问题就来了,咱们pull一个image,启动了container.好不容易把该安装的软件都安装好了,而后退出了container.以前安装的软件就都没有了!这个时候咱们就要使用commit命令了.commit命令能够把当前的可写层合并到image的只读层里面.这样子这个image又多了一层.下次咱们启动这个image的时候安装的软件就都还在了.
一个image由好几层layer构成.每一个layer都是一个只读层
当启动一个container以后,就会在iamge的只读层基础上添加一个可写层.全部对container执行的操做都反映在container上.(以上图片都来自docker文档.)
这里提一点,当使用docker images
命令查看iamge信息的时候,后面的SIZE是表示当前iamge所占用的大小,可是不意味着全部SIZE相加起来就是占用磁盘空间的总大小.必定要注意,可能有image共享若干层layer.这些layer在相加的时候被计算了好几遍.
PS C:\Windows\system32\WindowsPowerShell\v1.0> docker images REPOSITORY TAG IMAGE ID CREATED SIZE <none> <none> 12e32b701daa 25 minutes ago 188 MB ubuntu 14.04 3f755ca42730 3 days ago 188 MB centos 6 8315978ceaaa 6 weeks ago 194.6 MB
上面的命令提到删除有rm跟rmi两个,rm是用来删除一个已经退出的container.rmi是用来删除一个image的.有了上面AUFS的概念以后,要明白的是咱们使用docker rm container_id
的时候,其实只是删除掉了一层可写层的数据.由于只读层是container跟image共享的.只要iamge没有被删掉,那么只读层的数据必定也不会被删除掉.
一样,当多个image共享若干层只读层的时候,删除掉一个image.只是删除掉了这个image独有的一层只读层数据.其余共享的数据并无被删除掉,只有当删除掉全部的image以后,共享的layer层才会被删掉.
执行删除命令的时候会看到以下的信息,这里每一次deleted都是表明删除掉了一层layer.
PS C:\Windows\system32\WindowsPowerShell\v1.0> docker rmi ubuntu-fin Untagged: ubuntu-fin:latest Deleted: sha256:9e0728e8edbaf72846c43c629590fba5f46b1d705111d3fb1d79b9cf03a6c50c Deleted: sha256:d53e457ca7161cd6f2d1b6678ecaafd19043dcaeb1363471867e1047819268fa Deleted: sha256:496ef4fa137e03d80cf821745f875860d3d3120447326b8609938aa70f2edbd9 Deleted: sha256:12e32b701daa90c435176a273b2b41b4bfb219523c1ae396dc2f7068bbb6c088 Deleted: sha256:e8f29656cf54ad60a17d4b38362d9207b52a846cce3cc13e245fc3b799ff53e9 Deleted: sha256:48f6b521c809e40468886b0a159040503d00a0abb1eabf310451edfea562b459 Deleted: sha256:e94abc94ab1aff00280016eaf0649a75270886a2b60c8fe862ca549a0601949f Deleted: sha256:3f755ca4273009a8b9d08aa156fbb5e601ed69dc698860938f36b2109c19cc39 Deleted: sha256:565903b66233d5576592815ca4d499bd6fe09a9b4baf83f345aaf64544f1cd78 Deleted: sha256:b653e4373a4b35aa760ff67cfa3de2c9fe3c089823b63ec797eb04de256f86ba Deleted: sha256:362e536c4e530b94ce4204461e4f8c998705bcb98c91be54dd40b22f50531f3a Deleted: sha256:b69ad682d83af6a6962b4a60a0b5f033c8d39efcd20dbdf320b6dd8136e50aae Deleted: sha256:bc224b1b676d12be2a49f99778dda08b90d22747244d0a0afcdf4cfeb7db5d89
咱们再删除iamge的时候有时候不能成功删除.大概缘由有一下几点:
container正在运行,你删除这个container会失败.应该使用docker stop container_id
退出当前container再尝试删除.
container退出了,删除当前image也会失败.由于container虽然退出,当前container保存着运行环境等数据.container是在iamge的基础上添加了一层可写层.因此他们是共享只读层的.
删除一个iamge会有Untagged: ubuntu:14.04
.这个不是没有删除成功.这个是由于有其余image跟这个ubuntu:14.04共享layer层.因此删除时候并无真正删除掉layer层的数据.
ok,有了上面的预备知识,咱们如今能够开始准备咱们的环境了.刚刚说过,咱们退出一个container以后在container所安装的软件,添加的文件等等数据都会丢失掉,因此正确的办法应该是:在一个container环境中配置好全部开发要用到的东西以后,使用docker commit
命令来把当前这个container制做成一个image.而后下次咱们启动这个image的时候环境就是咱们所须要的了.可是这样子会存在三个问题:
当别人给你一个image以后,你知道这个image里面安装了哪些文件,修改了哪些数据么?
每次commit都会造成一个新的只读层.commit次数多了会使得image变得愈来愈臃肿.
再着,一个image动辄2,3G.带着这么大个文件跑也不优雅.
要解决上面的这些问题,就要使用Dockerfile了.因此咱们开始以前还要作点功课.
Dockerfile是用来描述如何构建一个image的,Dockerfile由一些指令构成,所有指令大概有20个左右,这里不所有讲解.只讲一些咱们下面会用到的.具体Dockerfile的所有用法参考Docker官方出的最佳实践.
咱们要制做的image必然是基于某个现有image的基础,from命令就是用来指定使用哪一个基础iamge的.像不少ubuntu官方在Docker Hub上维护由官方的image.咱们下面开发环境的搭建就是基于ubuntu:14.04的环境下完成的.
copy命令是把宿主机上的文件拷贝到image中.add能够是copy的高级版.
copy要求拷贝的文件在宿主机上存在
add能够指定一个url座位源文件,docker会自动去下载这个url的文件, 而后拷贝到image中.
咱们待会儿就会用到add指令,由于咱们须要使用163的ubuntu源来替换ubuntu原生的apt-get
源.因此咱们的Dockerfile会有相似的指令 : ADD http://mirrors.163.com/.help/sources.list.trusty /etc/apt/sources.list
.
这个是指定启动一个container以后,默认执行的命令.咱们执行docker run ubuntu:14.04
启动一个container以后,默认就进入了bash界面.这就说明这个ubuntu:14.04的CMD就是bash.
这里要澄清一个概念.使用docker run
以后默认进入了bash会让不少人觉得启动container跟启动一个虚拟机没什么区别.其实不是的.docker的container就是为了某个进程而存在的,这个进程就是CMD所指定的程序.好比:CMD /bin/bash
就是启动了bash.当咱们退出了bash以后,整个container也就退出了.若是你的CMD写成:CMD service nginx start
.你会发现container执行以后就立刻结束了.这是由于整个container只是为了service nginx start
这条命令而存在的,它不会管你这条命令启动了什么.默认启动的bash正好是一直在前台运行,只有你使用exit命令退出bash的时候才结束bash进程.这个时候container才结束.才会让人有container跟虚拟机差很少的错觉.
上面的这个概念很重要,必定要理解透彻.若是没有搞清楚这点.你会一直以为docker跟虚拟机没有什么区别.
这个命令指定了在构建image时候image中药执行的命令.这么说可能有点蹩脚.举个例子,咱们但愿咱们的镜像构建好的时候就安装好了git.那么咱们就能够在Dockerfile里面写RUN apt-get -y install git
.这样子在构建镜像的时候就会去安装git了.待会儿咱们要安装的软件都是经过这个命令指定的.也是有了RUN指令,咱们就能够知道一个image构建过程当中作了一些什么操做.
好了.Dockerfile咱们目前只须要这些指令.下面咱们就根据上面学到的东西来快速的搭建咱们所须要的开发环境.
我知道,上面那样子好像很随意的讲了一下Dockerfile,确定也不会写.因此,这里我给出我构建iamge使用的Dockerfile做为参考.
FROM ubuntu:14.04 ADD http://mirrors.163.com/.help/sources.list.trusty /etc/apt/sources.list COPY install.sh /usr/local/src/install.sh COPY supervisord.conf /usr/local/src/supervisord.conf RUN apt-get update && \ apt-get -y install build-essential && \ apt-get -y install supervisor && \ cp /usr/local/src/supervisord.conf /etc/supervisor/supervisord.conf && \ apt-get -y install openssh-server && \ apt-get -y install git && \ apt-get -y install vim && \ apt-get -y install lrzsz && \ apt-get -y install libxml2-dev && \ apt-get -y install pkg-config libssl-dev libsslcommon2-dev && \ apt-get -y install libbz2-dev && \ apt-get -y install libcurl4-gnutls-dev && \ apt-get -y install libjpeg8-dev && \ apt-get -y install libpng-dev && \ apt-get -y install libfreetype6-dev && \ apt-get -y install libmcrypt-dev && \ apt-get -y install libxslt-dev && \ apt-get -y install libgmp-dev && \ apt-get -y install libreadline-dev && \ ln -s /usr/include/x86_64-linux-gnu/gmp.h /usr/include/gmp.h && \ bash /usr/local/src/install.sh && \ adduser --gecos '' --disabled-password chenjiayao && \ echo -e '1111\n1111' | passwd chenjiayao && \ echo -e '11\n11' | passwd root CMD supervisord -n
上面的Dockerfile其实至关的简单,指令都是咱们上面用到的,这里再解释一下每一行的做用.
第一行FROM ubuntu:14.04
指明了使用ubuntu官方维护的14.04的image做为基础image来构建本身的image.执行这条指令以后,若是你的本地没有ubuntu:14.04
这个image的话, 那么就会去hub docker下载
第二行ADD指令上面提到了,这里就是使用163的源代替ubuntu内置的源,这样子下载软件的速度就会比较快.
接着是两个copy指令.这里从宿主机拷贝了两个文件到镜像中.其中install.sh是我本身写的编译安装php+apache的脚本文件,这里根据本身须要来决定.后面的supervisord是linux下面用来管理进程的软件.你会发现CMD启动的就是supervisord
.后面-n
参数说明是之前台的方式启动.而不是后台启动.这样子就避免了container运行一下就退出了.
RUN 里面都是在安装软件.执行一些必要的操做.你会发现我把全部的软件安装都写成了一个RUN指令.你可能会有疑问为何不使用不少个RUN来编写.为何要再一个RUN里面安装所有软件.这里就要说明一点 : 每执行一个Dockerfile的指令都会让咱们的image增长一层只读层.因此,写不少指令的话,咱们的image就会有太多的layer.因此尽可能要克制命令的个数.
CMD命令.这里我没有使用默认的bash做为启动命令是由于:若是使用bash做为默认的启动进程以后,当前container就只会有一个进程bash.那么其余的apache.ssh等服务都不会自动启动.*每次运行container都得手动启动这些服务很麻烦.因此这里使用supervisor来管理.配置好supervisor以后,只要启动了supervisor,supervisor就会自动帮咱们启动其余进程.好比apache.ssh等等.这样就比较方便.因此若是还不知道supervisor的童鞋,赶忙学起来,并且至关的简单.若是就是不学的同窗,也不要急,后面我会给出个人Dockerfile和其余配置文件.能够直接clone个人.
好了,Dockerfile咱们已经准备好了,下面使用docker build -t ubuntu-php .
来构建本身的image了.可是在开始以前要强调一下build的命令.
build命令 接着 -t ubuntu-php
表示构建好的image的名称.注意后面的.
,这参数表示的是当前目录.不少时候咱们在一个目录下建立了Dockerfile,编写好以后.使用powershell进入这个目录. 而后执行docker build -t image_name .
就开始编译.很容易就觉得最后一个参数是指定Dockerfile所在的目录.其实不是这样子的.这个目录指定的是当前docker编译这个image的工做目录.
要先明白,docker是一个C/S的软件,咱们使用powerShell输入命令 .以后命令是被发送到服务端执行,而后返回结果的.这跟MySQL同样.只是咱们把客户端和服务端安装在一台主机上.
当咱们构建image的时候,执行相似COPY指令,那么把文件拷贝到image中,可是构建文件是在服务端完成的,如何让docker服务端获得拷贝的文件?这里咱们就要指定一个docker构建的工做目录了.当构建开始的时候,docker会把工做目录下的全部文件都发送到服务端.而后开始构建.这样子他就能够获得咱们要copy到image的文件了.
因此咱们构建的时候指定.
是想把当前目录下的文件等发送到docker服务端进行构建.只是在上面,咱们的Dockerfile正好是放在了docker构建image的工做目录中了.
那么,既然上面的参数不是指定Dockerfile所在的目录.那若是个人机子上有多个Dockerfile的话,那么docker会使用哪一个?我编写这个Dockerfile的目的就是但愿使用这个Dockerfile.这个不用担忧. 若是你在build的时候没有指定使用哪一个Dockerfile.默认会使用构建iamge的工做目录下名字为Dockerfile的那个Dockerfile....听着有点晕...若是不想理清楚这些问题.每次构建的时候使用powerShell进入Dockerfile所在的目录下,而后执行docker build image_name .
就能够了.
在构建过程当中会输出相似下面的内容
PS D:\code\docker\ubuntu> docker build -t ubuntu-fin . Sending build context to Docker daemon 8.192 kB Step 1 : FROM ubuntu:14.04 ---> 3f755ca42730 Step 2 : ADD http://mirrors.163.com/.help/sources.list.trusty /etc/apt/sources.list Downloading [==================================================>] 872 B/872 B ---> 386d7ab302b9 Removing intermediate container f183c42cf864 Step 3 : COPY supervisord.conf /usr/local/src/supervisord.conf ---> 8ce5250f8498 Removing intermediate container 2c6d89b3be22 Step 4 : COPY install.sh /usr/local/src ---> efa055e7d1b3 Removing intermediate container e0c7dacd9136 Step 5 : RUN apt-get update && apt-get -y install build-essential && apt-get -y install supervisor && cp /usr/local/src/supervisord.conf /etc/supervisor/supervisord.conf && apt-get -y install openssh-server && apt-get -y install git && apt-get -y install vim && apt-get -y install lszrz && apt-get -y install libxml2-dev && apt-get -y install pkg-config libssl-dev libsslcommon2-dev && apt-get -y install libbz2-dev && apt-get -y install libcurl4-gnutls-dev && apt-get -y install libjpeg8-dev && apt-get -y install libpng-dev && apt-get -y install libfreetype6-dev && apt-get -y install libmcrypt-dev && apt-get -y install libxslt-dev && apt-get -y install libgmp-dev && apt-get -y install libreadline-dev && ln -s /usr/include/x86_64-linux-gnu/gmp.h /usr/include/gmp.h && bash /usr/local/src/install.sh && adduser --gecos '' --disabled-password chenjiayao && echo -e '1111\n1111' | passwd chenjiayao && echo -e '11\n11' | passwd root ---> Running in 1dd5ade41249
发现,每个Step其实就是执行Dockerfile中的每个指令.好了,构建已经开始,等待构建结束以后,咱们的环境也就搭建好了,建议把Dockerfile等构建必须的文件放到github上面,之后换一个环境.只要下载文件.而后就能够构建了.
这里我放出我构建环境时写的Dockerfile,有须要自取.传送门.
最后咱们还有三个问题须要解决:
文件共享
端口映射
commit制做镜像
这些问题,考虑到文章篇幅应该够多,因此将再开一篇文章简介.