天天看点

docker核心原理

简单记录了一下关于docker的学习,从概念到使用,到研究和心原理。日后每个步骤详细分解。

容器概念。

docker是一种容器,应用沙箱机制实现虚拟化。能在一台宿主机里面独立多个虚拟环境,互不影响。在这个容器里面可以运行着我饿们的业务,输入输出。可以和宿主机交互。

使用方法。

拉取镜像

默认是从官网的docker仓库上面获取,其中pull的命令是拉取,与之对应的是push命令,日后有能力自己创建镜像并且上传到docker仓库的时候用到。registry是镜像名,docker官方维护有很多基础镜像,可以直接下载来用。同时在公共仓库也有很多共享的镜像,自己可以视情况来下载使用。

启动容器

第一步把镜像下载到本地,现在可以在镜像的基础上启动容器。run命令就是启动的意思,后面可以加一下附属参数。命令中-d就是daemon的意思,以守护进程方式运行。--name就是制定容器的名字,方便日后管理,不然每次都要使用容器的ID,12位16进制数。后面就是我们的镜像名。容器启动后就会返回一串数字,作为sha256,这一串是我们的长ID。长ID和短ID的区别如下:

一般操作使用短ID,下面说到的文件目录用长ID标记。

这是最基本的用法。

核心原理。

这是用方式不是一个镜像一台提供不同服务的虚拟机吗?

应用不同,这是两个完全不一样的产品。docker可以快速部署相同的和不同的环境,虚拟机只是节省资源,在同一台宿主机安装多个系统。

容器是共用镜像,独立的服务,属于云计算中的paas服务,在统一的基础环境延伸。虚拟机提供的是iaas,从底层开始。

docker的实现方法是共享和隔离。虚拟机的只是虚拟硬件,虚拟机间没有共享成分。

容器的实现原理很炫吗?

它是通过利用内核自带的namespace和cgroup功能隔离系统必须的六个模块,以完成一个独立的系统环境。用namespace隔离一个小空间,UTS、IPC、PID、Network、mount、User六个环境的新隔离,用cgroup作为系统资源的控制。

UTC实现主机名和域名的隔离,使容器有独立的主机名;IPC隔离信号量和消息队列,使之有独立的工作通讯;Network隔离网络,使之有独立的网络设备;mount隔离文件系统,使之有独立的存储;PID隔离进程号,使之有独立的进程编排。运行起来就像一个独立的计算机环境。

除了文件,其他一些隔离都可以调用接口实现。每个容器里面有整个系统的文件,而且都是一秒钟生成。就很难理解了。

    上面说了,容器操作的时候,用名字就不用每次都找ID。exec命令是发送命令给容器执行,-i是使用交互模式保持输入流的开放,-t是创建虚拟终端,smokeping是指定的容器名,bash是容器要执行的命令。我们看看容器的这些文件存放在宿主机的什么地方  。

运行目录。

容器是虚拟的,文件总不能也是虚拟的。查找了相关资料发现全部都是保存在docker容器的运行目录。/var/lib/docker

看了一下,还真有。

为了能看到文件的变化,我们把docker的运行目录都删掉了。

重启docker daemon,一切都是新的。

Docker运行目录的变化

拉取第一个镜像开始,/var/lib/docker开始建立,包含了下下目录。

删除镜像。

展开目录。

全部目录为空,打开image/aufs/repositories.json的记录文件也为空。

重新拉取镜像,再展开对比

Aufs目录下的三个文件夹的首层子目录或者文件名都是各层ID。

diff目录保存着只读层和可读写层的数据。每层只保存历史当次读写。

Layer目录下的文件名和diff子目录名一一对应,里面的内容是层依赖。

Mnt目录下的子目录名和iff子目录名一一对应,里面的内容暂时为空。

Container的目录为空。

Image目录下的distribution、imagedb、layerdb。

Distribution下的diffid-by-digest和v2metadata-by-diffid都有sha256。

Imagedb就只有一个sha256。这个正是image的ID。

Layerdb的sha256下面也有同数量子目录。每个目录里面有相同名字的配置文件。

repositories.json记录的也是镜像的sha256。

Network文件夹为空,本来是用来存放容器内网络相关文件。

Volumes文件夹为空,是存放容器里面挂载的目录,有真实可读写的文件。

小结:在上面“使用方法”演示的“docker pull”过程中,docker下载的镜像文件全部分层保存在aufs目录下的diff目录,目录名是sha256,和层ID无关。

启动容器时的目录变化。

生成容器的ID:

  bc893c0031db98114a48ebb924cf5d0b9b2c77968839696e735528648a1a531b

aufs的三个目录。

Diff没有变化,但是多了一层把diff的内容都挂载上去。

Layer增加了一个目录,目录文件是个别层实现可读写。

Mnt中前面的目录没变,但是多了一层把diff的内容都挂载上去。

Container的目录不再为空,首先是以容器ID为目录名新建了个目录。然后在目录下存放hostname、hosts等配置文件。

Image目录完全没有改动。

Volumes下面出现了容器定义挂载的数据文件。此数据文件在mnt目录没有挂载。

小结:在上面“使用方法”演示的“docker run”过程中,aufs下的diff和mnt目录,自动生成两个目录,名为sha256的一串数字ID,另一个是ID-init。diff下也是这两个目录,此ID是在diff是最新一层,在mnt中是当前容器所有文件;aufs/mnt/ID的文件由diff下的镜像目录通过aufs系统整合挂载而来,aufs系统是实现多目录挂载在同一个目录的工具。aufs/mnt/ID-init目录为空;aufs/diff/ID下保存的是最新一层也就是当前可读写文件,aufs/diff/ID-init是最新一层也就是当前的只读文件hostname、host等。一句话,数据都是储存在diff,mnt是打酱油的。过程就是aufs整合镜像存放在diff下的内容,一起挂载在aufs/mnt/ID下,连最新一层aufs/diff/ID(有几个可读写文件)和aufs/diff/ID-init(只有只读文件)都挂载上去。

运行目录生成文件时的变化。

在root目录touch ken.txt

其他目录都没有变化。

aufs下的diff和mnt的那个容器新生成的ID目录下随地生成了ken.txt文件,应该是数据库文件记录了此文件在容器中的位置。

Mnt目录和diff的关系。

Mnt目录是被挂载。

在root目录mv rc.local rc.loca

其他目录没有变化。

aufs下的diff的那个容器新生成的ID目录下etc中生成rc.loca文件。

diff下镜像层的文件没改。

修改的文件存在于diff和mnt中。

新增的文件直接放在aufs/diff/e36b1bd8e430d4731211af3984aed0fd3fa6fa62c57f5c877a013856ba32abc4目录这里,修改的文件连带目录生成到这里。

小结:容器启动之后,新建目录ID,和ID-init目录;容器需要更改的文件从镜像文件复制到diff下的ID目录,经过容器的操作,ID目录就拥有了最新的变更,ID-init是亘古不变的只读文件;新增文件出现在新建层级diff的ID的储存方式是直接随地放在根目录下,修改文件出现在新建层级diff的ID的储存方式是连带目录首先复制到diff的新建层级,然后修改。最终挂载到aufs/mnt/ID展示在容器内的正确位置。

运行目录在停止容器后的变化。

Aufs目录下的三个文件夹。

Diff完全没有变化,说明一旦容器停止的时候,容器必须的配置文件时复制的。

Layer文件完全没有变化。

Mnt那个新建作为挂载diff各层文件的目录为空,说明已经卸载。

Images目录不变。

Container目录不变,保留着diff时刻准备着的文件。

小结:容器停止后,在mnt上面的挂载马上卸掉,但是目录不变。而在diff上面的文件状态保持不变,期待下次容器启动的时候一次挂载到mnt下面。

运行目录在删除容器的变化。

Aufs目录下的三个子目录。

Diff在容器启动时所生成的那个ID的文件夹消失。

Layer在容器启动时所生成的那个带ID的文件夹消失。

Mnt在容器启动时所生成的那个ID的文件夹消失.

Container目录下容器启动时所生成的那个ID的文件夹消失。

Volumes目录下的文件存活下来。

小结:当容器删除,镜像文件不变,其他全部删除。如果有定义volume的,会有所保留。

总结:容器在宿主机上运行,无非是围绕只读层和可读写,利用复制和挂载,灵活操作;来得快的文件时通过挂载,如果在只读层无法修改文件就可以先复制出来再说;整个过程就是,容器一启动,diff就新建可读写的新ID目录和ID-init目录,ID-init目录一开始就加载了只读文件。然后和其他各层数据挂在mnt的分别挂在ID一一对应的mnt下,aufs系统把全部数据整合嫁接到mnt的新ID下。此时mnt中其他的目录为空,新ID拥有容器的全部数据。当容器产生动态数据,就作用于那个可读写并且挂在mnt新ID下的diff新ID文件夹。