Docker本身的镜像是构建在其本身的文件系统之上的,Docker有很多种类的文件系统,Docker所支持的文件系统有以下几种:AufsdevicemapperbtrfsVFS,其中前三种是联合文件系统,可以支持分层,可以快速迭代,可以回滚。VFS 不支持。平时用的最多的是aufs devicemapperAufs(advanced multilayered unification filesystem), 直译过来就是高级分层联合文件系统,做为一种Union FS ,它支持将不同的目录挂载到同一个虚拟文件系统下。对于分层来说,也就是我们构建一个文件系统或者Image,在这之上再次构建一层文件系统或者p_w_picpath,是一层一层的迭代关系。除了最上层的内容,下面每一层的内容都是制只读的镜像,不同层次的镜像堆叠起来,加上最上层的可读写的container(在Docker文件系统中,可读写的最上面一层称为container),就构成了整个分层的文件系统。AufsDocker最初采用的文件系统,由于Aufs未能加入到Linux内核,考虑到兼容性问题,加入了Devicemapper的支持。目前,除少数版本如UbuntuDocker基本运行在Devicemapper基础上。这里我们是介绍性质的说明,不做深入详细的讲解。

Docker本身为我们提供很多的标准的p_w_picpath,这些标准的p_w_picpath都是Docker Hub上,国内的厂商本身自己也有一些自己的镜像库,比如daoCloud在国内建立的Docker Hub镜像站点,有容云的企业级私有镜像库可以提供客户在自己的本地内网内构建私有镜像库。Docker Hub是全球最出名和最大的Docker镜像库,在上面我们可以找到全球各式各样的开发者所制作的标准镜像,基本上现有的开源的产品都有Docker的镜像版本。这是镜像我们如果需要使用的时候,都可以通过Docker search命令默认在Docker Hub上搜索我们所需要的Docker Image,然后通过Docker Pull命令直接下载下来,并在本地进行使用。需要注意的是如果想Docker Pull的时候如果不指定具体的版本,那么所有搜索到的符合条件的p_w_picpath都会下载下来,所有在下载的时候,还需要指定tag来指定所需的版本。同样,如果我们需要将我们自己制作的p_w_picpath打包上传到Docker Hub的话,我们首先需要有一个Docker Hub的账户,需要登录该账户,然后把Container通过Docker commit的命令转化成一个p_w_picpath,然后通过Docker tag来命名新生成的p_w_picpath,并使用命令Docker push上传到Docker Hub中。

我们本身基于镜像可以启动运行一个或多个容器,当我们运行容器的时候,我们怎么去连接到或访问到容器本身?在Docker中我们可以使用Docker run来启动运行一个Docker容器,我们运行一个Docker容器,该容器就是一个独立的空间。同时,在运行一个Docker容器的时候,必须指定一个p_w_picpath作为初始化的文件系统。那么在运行Docker的时候p_w_picpath是怎么获取的呢?我们在运行Dockerrun的时候,Docker会从三个方面去查询当前的这个启动的p_w_picpath,首先是Docker自己当前的存储区有没有就这个p_w_picpath,如果有这个p_w_picpath,那么就在hi家启动运行这个p_w_picpath;其次是,如果我们构建了本地的私有镜像库,而本地的私有镜像库存在这个p_w_picpath,那么就将从本地私有的这个镜像库中抓取除该p_w_picpath并运行;第三,如果本地私有镜像库中也没有这个p_w_picpath的话,那么就只剩下去DockerHub上去抓取相应的p_w_picpath了。

在获取了相应的p_w_picpath之后,就启动相应的p_w_picpath,其中Command标志是容器在实际运行的首进程。如果p_w_picpath里面包含了CMD的指令,那么在启动容器的时候,不需要指定Command,否则会使用指定的Command来覆盖p_w_picpathCMD。这里说的CMD指的是我们需要执行的命令,跟我们使用Linux类似。因为我们在启动容器的时候默认是在前台运行的,所以其启动时绑定的Command进程的STDIN【标准输入】,STDOUT【标准输出】,STDERR【错误】会显示到Console界面上,在前台运行的时候,我们可以使用-a参数来指定我们先让其显示的选项:比如是显示标准输入还是标准输出或错误信息等。如果我们不需要其显示到Console界面上,即使其在后台运行,那么就需要在运行Docker容器的时候添加-d的参数。如果我们的Docker容器在后台运行,run命令会返回一个UUID,使用该UUID的唯一标识来标示这个Docker容器。因为在后台运行我们看不到容器的打印输出内容,但我们先查看后台运行的Docker容器的打印输出内容的时候,我们可以使用attach命令把后台的Docker容器重新attach到我们的Console界面上显示,并使用命令与Docker容器进行交互。同时在管理Docker容器的时候,还可以使用Dockerps来查看Docker容器的UUID和运行信息,也可以通过指定—name的方式来指定Docker容器的名字。因为在启动容器的时候我们其实会发现该容器会存在一个UUID,该UUID是一个字符串,这个字符串对于普通人来说是极度不好识别的,是无规律和无意义的字符串,如果我们命名一个友好的名称,那么我们管理还是参看容器的信息的时候,对我们来说就非常友好。如果我们先查看Docker容器具体的详细信息,可以通过命令Dockerinspect来获取容器的更多的详细信息,这其中包括更为详细的网络、存储【Volume数据卷】以及实际运行在主机上的进程ID等信息。关于Docker的网络部分信息,Docker使用bridge来创建一个网络配置提供给Docker使用,当然还有其他的模式,但是bridge是默认的模式。而Volume数据卷是Docker容器之间共享数据以及Docker与本地文件系统共享数据的一个核心技术。比如我们在本地开发一个网站系统,我们希望随时更新网站的数据,同时网站使用Docker来运行,那么这该时候就需要使用DockerVolume数据卷进行本地文件和Docker容器文件之间的绑定映射或挂载。同时我们不同的Docker容器还可以挂载到相同的Volume目录,这样我们不同的Docker就可以共享该目录下的数据了。最后,我们可以使用Logs命令查看容器中Command说指向进程的标准输入输出以及错误的数据信息。

同时这些运行的容器如果需要访问到主机的数据,比如网络资源、存储资源,那么是如何进行访问的?上述我们提到过,Docker默认使用bridge的方式来实现Docker容器之间以及和主机之间的网络通信。其实现主要是在主机上有一个veth的虚拟网卡和一个容器里面的eth0网卡进行一对一的映射,也可以借助一些第三方工具来实现这些映射关系,同时主机上的veth虚拟网卡桥接到主机上的bridge上,bridge负责把数据流在不同的veth将间进行转发,实现网络的通信。Docker1.9版本之前支持三种网络模式:

  • bridge:容器使用独立网络Namespace,并连接到docker0虚拟网卡(默认模式)

  • none:容器没有任何网卡,适合不需要与外部通过网络通信的容器。

  • host:容器与主机共享网络Namespace,拥有与主机相同的网络设备。

Docker1.9版本中引入了一整套的自定义网络命令和跨主机网络支持。在此之后,容器的网络接口就成为了一个可替换的插件模块。也就是说Docker将网络的控制能力完全开放给了用户,用户可以自由选择Docker的网络模型并且可以随意添加删除不同类型的网络模式,其实就是和虚拟机随意添加网络模型是一样的。同时在这之前,Docker容器的跨主机网络通信一直备受诟病,各个社区的开发人员之间也开发了一些工具了解决Docker的跨主机网络访问问题。在Docker1.9中版本中正式加入了官方支持的跨节点通信解决方案,而这种内置的跨节点通信技术正是使用了Overlay网络的方法。Overlay网络协议也是VMware NSX软件定义网络的技术核心。Docker内置的Overlay网络是采用IETF标准的VXLAN方式,并且是VXLAN中普遍认为最适合大规模的云计算虚拟化环境的SDN Controller模式。如果我们需要修改Docker容器的网络设置,可以通过—net参数来实现,如果是none,就表示是关闭Docker容器的网络。什么场景下我们需要关闭Docker容器的网络连接呢?有时候我们需要容器完全私有的不与外界进行通信的情况下,我们就可以关闭容器的网络连接。如果是host,就表示使用主机的网络堆栈,这个时候的host主机不会创建veth虚拟网卡映射。如果将其设置为container:容器名称,那么就表示现目前的容器将使用这个名称的容器的网络堆栈。同时Docker允许采用端口映射的方式把内部的容器服务端口映射或者暴露该外部,使用-p参数就可以指定需要暴露的容器的内部端口,在不指定特定的主机的对应端口的情况下,Docker会自动分配(49000-49900)在一个主机上的端口与其进行映射。我们在主机上使用暴露的端口就可以访问到Docker容器上运行的服务。同时通过Link参数,可以把容器端口信息暴露给另外一个容器,实现容器间的通信。

那么上述我们说的很多配置都需要通过很多的参数配置实现,而Docker最正统的开发或者说构建Docker的配置最好的方式是使用DockerfileDockerfile是一种被Docker程序解释的脚本,Dockerfile由一条一条的指令组成,每条指令对应Linux下面的一条命令。Docker程序将这些Dockerfile指令翻译真正的Linux命令。Dockerfile有自己书写格式和支持的命令,Dockerfile的指令是忽略大小写的,建议使用大写。Dockerfile的功能就是封装我们p_w_picpath的创建、配置、更新等等各方面的内容。我们可以使用build指令基于Dockerfile来构建一个p_w_picpathDockerfile的运行机制是:Dockerfile内部是所有对Docker操作指令的集合,这个文件要被编译的时候就是通过build的方式,而编译的时候会把这个文件传递给Docker DaemonDocker DaemonDocker架构中运行在后台的守护进程。这个守护进程作为Docker容器的引擎而存在,接受到客户端传递进入的这个文件之后,根据文件中的指令然后一条一条的解析,并执行指令来构建、配置或者更新一个Image。使用Dockerfile的好处就是构建了一个标准的Dockerfile之后,我可以在任何机器上执行,而且我们基于同一个Dockerfile生成的内容是完全一样的。Build镜像有两种方式获取Dockerfile文件:第一是通过源代码路径的方式:在构建的时候,Docker Client会把整个Context打包发送到Docker Server端,然后由Server端负责build镜像,在构建成功后,会删除Context目录。构建的命令也很简单:Docker build –t【镜像名称】【路径】。第二种是通过标准输入流的方式:这种方式Client不会打包上传Context目录,因此对于一些ADDCOPY等涉及主机本地文件复制的操作本能够支持。构建的命令是:Docker build –t【镜像名称】-<Dockerfile路径。当选择用源代码的方式来构建p_w_picpath的时候,通过在根目录下放置.dockerignore文件来过滤不需要发送到Server端的文件,类似于排除列表。同时Dockerfile中的每一个指令执行完毕后,都会提交为一个p_w_picpath,这样保证了指令之间不会有影响。而且Docker也会尽可能的尝试重用之前已经构建的镜像。

Dockerfile的指令根据作用可以分为两种,构建指令和设置指令。构建指令用于构建p_w_picpath,其指定的操作不会在运行p_w_picpath的容器上执行;设置指令用于设置p_w_picpath的属性,其指定的操作将在运行p_w_picpath的容器中执行。我们实际在写Dockerfile文件的时候,第一行一定的FROMbase镜像),首先指定基础的p_w_picpath,必须放在Dockerfile的第一行,表示从哪儿开始构建Base p_w_picpath。这个指令必须是第一个指令。后续的指令都依赖于该指令指定的p_w_picpath。接下来就是MAINTAINER指令,用于将p_w_picpath的制作者相关的信息写入到p_w_picpath中。接下来的run指令,这个run指令主要是按照软件使用的。每一个run指令都会是在一个新的容器里面运行,并提交为一个p_w_picpath作为下一个runbase。一个Dockerfile中可以包含多个run,并按照定义顺序执行。Run支持两种运行方式:第一种是:run <cmd>,这个会当做/bin/sh –c “cmd”运行;第二种是:run”executable”,”arg1”….,Docker把它当做Json的序列来解析,因此必须用双引号,而且executable需要的是完整路径。CMD的作用是作为执行容器的默认行为,当运行容器的时候声明了Command,则不再使用p_w_picpath中的CMD说定义的命令。一个Dockerfile中只能有一个有效的CMD,当定义多个CMD的时候,只有最后一个才会起作用。Docker还有很多其他参数命令,在这里就不一一详细介绍。

当我们的容器构建好了之后,我们可以通过SSHD的方式访问容器内部的信息。通过sshd访问就是我们开启了22端口,多了一个安全风险;这种方式需要在容器中开启sshd的服务,通过ssh协议登录到容器中,这种方式类似于把容器看成是一个虚拟机的方式来实现登录。同时我们还可以通过nsenter的方式来访问容器内部的信息,nsenter是一个小的工具,用来进入namespace(命名空间)中。技术上,它可以进入现有的命名空间,或者产生一个进程进入新的一组命名空间。简单来说:通过使用 nsenter ,我们就可以进入一个已经存在的容器当中。而且使用nsenter并不需要像sshd一样需要将sshd安装在容器中。Nsenter包含在util-linux包中,我们可以直接在主机上安装使用,然后我们直接使用他的命令进入已经安装运行的容器。现在在最常用的是exec的方式,exec命令是Docker自带的命令工具。