前言
前一阵的工作,大多数是与部署相关的,通过将项目打包,然后利用docker,部署在AWS上去。就在这一段时间里面我便对docker产生了浓厚的兴趣,这也就激发我去深入研究的一个动力。
Docker的出现是一种历史趋势,因为微服务的出现,导致了使用的技术更加多元化,所以在部署上的难度也变得更大。而docker可以减轻我们在部署上面的复杂性。
docker相当于起到了承上启下的桥梁作用:将App连带环境一同打包直接部署到服务器上。
如果是使用Mac或者windows系统使用docker的话,建议使用Vagrant,它是不需要使用iso镜像就可以创建虚拟机的,这样的好处是方便我们的使用以及删除。
那么下面开始我们的docker之旅。
虚拟机与docker之间的区别
我们可以看到,传统的虚拟机是在宿主机之上,又添加了一个新的操作系统,这就直接导致了虚拟机的臃肿,与
不适合迁移。而docker这是直接寄存在宿主机上,完全就会避免大部分虚拟机带来的困扰。
另外Docker其实是一个黑盒的进程,为什么说是黑盒的,区别于传统的进程,Docker可以独立出一个自己的空间,不会使得在docker中的行为以及变量溢出到宿主机上。
为什么用docker
在没出现docker的时候,我们完成的java web项目需要打成一个war。然后得在服务器中配置各种各样的参数,例如说jdk,tomcat,数据库等。配置的周期相当的冗杂并且繁琐。但是现在有了docker,我们不但可以使用一个空的镜像,从头开始构建,还可以使用之前各种大牛已经build好的镜像,直接使用。而且在项目需要迁移的时候,我们只需要在需要部署的地方,直接使用之前项目使用的docker放置好自己的项目即可,方便快捷。
docker底层技术支持
- NameSpaces:用于做进程之间的隔离
- Control Groups: 用于做资源控制,根据需求划分资源的核心数,内存,硬盘等等,例如我们之前新建一个虚拟机一样
- Union file systems(UFS,联合文件系统):Container和image的分层
docker的基本概念
docker最重要的三个概念是:镜像(image),容器(container),仓库(repository),在这三个概念中,镜像是最重要的概念。
镜像
- 镜像是文件与meta data的集合
- 分层的,并且每一层都可以添加删除文件,从而形成新的镜像
- 不同的镜像可以共享相同的层(layout)
- 只读的
镜像我们可以理解为树状结构,每一个镜像都会依赖于另一个镜像,这个依赖关系是体现在docker镜像制作的dockerfile中的FROM指令中的。如果要是树的根,那么我们需要"FROM scratch",这个是值得注意的。如果需要对它进行修改,该怎么办呢,别着急,这个就得看我们的容器了。
容器
- 通过image创建
- 在image 的最后一层上面再添加一层,这一层比较特殊,可读写。
- image负责存储和分发,container负责运行
容器是镜像的一个运行实例,可以不准确的把镜像当作类,容器当作对象。容器其实他的结构是与镜像相类似的,底部也是一层层的只读层,只不过在最上层会存在一个存储层,我们可以在这一层定制化我们的这个容器,还可以通过build命令,把容器打包成我们自己需要的镜像。另外镜像启动后会形成一个容器,容器在计算机中是一个进程,但这个进程对其他进程并不可见。
容器的启动过程:
检查镜像是否在本地存在,如果不存在去远程仓库下载
==>利用镜像创建一个容器
==>启动刚刚创建的容器
==>分配一个文件系统给容器,并且在镜像层外挂载一个可读可写层
==>从宿主主机的网桥接口中桥接一个给容器
==>从网桥中分一个ip地址给容器
==>执行用户指定的应用程序
==>执行完毕后容器自动终止
仓库
这个理解起来就很简单了,大家应该有使用过git,他是有一个远程的仓库,这个仓库记录着我们的代码,和每一次我们提交的记录。但是在这里,把docker的仓库比做maven仓库更加恰当,就相当于我们可以去maven远程仓库取我们需要的依赖,多个依赖构成了我们的整个项目,这个思想同样适用于docker。默认情况下,我们都是从docker hub中取得的镜像(http://registry.hub.docker.com/)
使用docker
实践是检验真理最好的方式,我会在不断的实践中给大家讲解docker命令。
本人使用mac,所以所有操作都会在mac进行。如果使用docker的话,强烈不推荐搭建使用windows系统,因为docker对于windows很不友好,用起来十分不方便,那怎么办呢,win的同学,可以在电脑上安装虚拟机,然后安装一个linux系统(centos,red hat,ubantu)。
下面给出安装的链接:
Mac:mac安装docker的方法
Linux:centos安装docker的方法
在使用docker之前,如果是不熟悉linux命令的,推荐强烈去学一下,因为我自己感觉这对于docker的学习是很有帮助的。
安装好docker,那就开始我们的表演。
1.docker search <镜像名称>
当我们在对docker的镜像一无所知的时候,我们可以通过查询镜像,看看自己想要的镜像存不存在。例如说我们这里可以docker search tomcat。
2.docker pull <镜像名称>:<镜像版本>
其实我们的版本都可以不用填写,默认情况下是latest,也就是去取最新的版本。
3.docker run -d -p <外部端口>:<内部端口> <镜像名称>:<镜像的tag>
打开terminal终端,在终端中输入:docker run -d -p 8080:8080 tomcat 这里启动一个tomcat镜像作为演示。熟悉tomcat的应该知道,默认的端口号为8080,所以我这里使用了默认端口。这里解释一下外部/内部端口,这个很容易理解,docker在运行的时候,会启动一个tomcat,这个tomcat的端口为8080。外部端口指的就是宿主电脑的端口。就是我们可以通过我们的电脑访问的端口。
- -d <指的是后台运行容器>
- -p <指的是指定端口>
- --link <连接的容器名称>:<连接的别名>
可以使容器和容器之间相互连接 - -v <挂载到容器的目录>
- --volumes-from <可以挂载的数据卷容器名称> (通过启动一个新容器,使用 -v 命令挂载一个目录,然后通过这个命令把容器挂载到数据卷容器上,可以多个容器挂载同一个数据卷容器上,而且数据卷容器本身可以不是启动的状态。)
- -v <本地已有的目录>(这个路径必须是绝对路径):<容器的目录>。如下,我自己创建了一个宿主机与容器中的文件夹映射,然后创建了一个文件,宿主机相应的也产生了文件。
(Tips:其实我们本可以不使用pull进行下载镜像,因为docker镜像在启动的时候,如果放下这个镜像不存在,那么自己会去下载镜像。)如果想的话,可以添加上 -- name 作为这个启动容器的名称,如果我们不定义名称,可以通过容器的id操纵这个容器。并且docker run 是一个组合命令,实际上组合的是 create+start
4.docker create --name <容器名称> <镜像名称>:<镜像tag>
5.docker start <容器的id>
这里把create 以及start一起讲一下,create命令就是在镜像的只读层的最上面加上一个存储的可读可写层。上面的 -d 与 -p 的option 都是create命令的,创建一个容器可配的option相当的多,这里就不详细说明了。create命令后,容器是处于stop状态的,得需要start命令启动。start命令则是给容器分配一个进程,然后启动起来。
6.docker images
查看本地已经有的docker镜像。
通过 docker images - -format [.ID]:[.Repository]指定显示镜像id以及名称
7.docker rmi <容器名称>:<容器tag>
移除已经存在的指定的容器。
8.docker ps -a
查看所有的容器。
当把-a换成-qa的时候就是查看所有容器的id。
9.docker exec -it <容器Id> /bin/bash
进入docker 容器 docker exec -it(后面可以接上容器id或者容器名称) /bin/bash
- -it 是 -i以及-t。
- -i是保持标准输入打开
- -t是表示分配一个伪终端。
10.docker tag <已有镜像名>:<已有镜像版本> <新镜像名>:<新镜像版本>
我们可以使用docker tag tomcat:latest mytomcat:latest。这样就会新生成一个镜像名字叫做mytomcat,版本为latest,只有别名不同而已,但是同样指向了同一个镜像。
11.docker inspect <镜像名称>:<镜像版本>
显示镜像的详细信息,包括制作者,适应框架,各层的数字摘要。
12.docker history <镜像名称>:<镜像版本>
前面已经说过,既然镜像是一层一层构成的,那么我们肯定可以看到,各层的记录,使用此命令,可以看出镜像发生的变化。过长的信息会被自动拦截我们可以使用 --no-trunc显示信息完整的信息。
13.docker commit <进程号>/<容器名称> <镜像名称>:<版本号>
基于现有镜像,形成自己的镜像 。原理就是在原有的镜像基础上增加自己修改过的存储层,叠加为一个新的镜像,保存下来。成功后,会返回一个 sha256的码作为镜像的唯一标实。
形成新的自己的镜像,一共有三个方法:
- 通过 commit 命令
- 通过模版进行构建
我们可以通过openVZ为我们提供的模板进行创建,也可以通过自己导出的模板进行创建,openvz网址 openvz模板下载地址。
我们以ubantu的模板为例。命令为cat ubuntu-x86_64-minimal.tar.gz | docker import ubuntu:14.04。执行完以上命令,就可以通过docker images 看到我们的镜像了。
- 通过Dockerfile文件进行构建。
但是docker commit 需要慎用,因为具体做了什么修改任何人都是看到的,完全就是一个黑盒操作。而且进行的删除操作,并不是真正意义上的删除,每一次的修改都是在上一次的基础上,这使得 镜像越来越臃肿。如果我们想要定制我们的docker镜像可以使用dockerfile
13.docker save -o 导出的镜像名称(后缀为.tar) <镜像名称>:<镜像的版本>
14.docker load --input 导出的镜像名称(后缀为.tar)
15.docker export -o 导出的镜像名称(后缀为.tar) <容器Id>
16.docker import 导出的镜像名称(后缀为.tar) <镜像名称>:<镜像的版本>
前两个命令是搭配在一起的,我们通过save命令,把镜像打包成tar文件的形式,这就可以分享给别人进行使用,然后其他人可以通过 load 命令将 导出的文件,保存到本地。
后两个命令也是搭配起来的将正在运行的容器,打包成一个tar文件。然后把tar文件import进来,形成一个镜像。
我们可以看出load和import命令十分相似,那他们的区别在哪呢?
- load 命令导入镜像存储文件到本地镜像库
- import 导入一个容器快照到本地镜像库
容器快照文件将丢弃所有的历史记录以及元数据信息,仅保留快照时的状态
镜像存储文件保持完整记录,相对的体积也会更大。
在我动手实践的时候也明显感觉到了,使用export要比save的速度快的多。
这里的 load的时候犯了一个错误,在后面跟上了镜像的名称与版本,其实是并不需要的,切记下次不会再加了。
17.docker stop <容器的ID>
停止正在运行的容器实例。
18.docker rm <容器的ID>
在删除一个容器实例之前,应该先要将容器停止。
主要支持的选项:
- -f , --force = false 强制删除一个正在运行的容器
- -l , --link=false 删除容器的连接,但是保存容器
- -v , --volumes=false 删除容器挂载的数据卷
19.docker restart <容器的ID>
重新启动容器,将容器先停止,后启动。
20.docker build -t <生成镜像的名称>:<镜像的版本>
这是三种创建自己镜像的方法之一,也是最好的一种方法,很多时候我们看到Dockerfile的位置上为".",表示的是当前目录,说明我们cmd和我们的Dockerfile在同一目录下。
21.docker container prune
清理掉所有已经停止的容器,在容器失败停止后,做集体的清理特别方便。
后续
在下一篇文章,我会继续整理有关Dockerfile的编写,以及其他相关的内容。我希望这样的总结,可以给大家带来帮助,也对自己梳理知识体系有所帮助,谢谢大家
本文参考书籍:《Docker技术入门与实战》