理解镜像,容器,存储引擎

为了很好的使用存储引擎,必须明白Dcoker是如何建立和存储images的。
而后要明白这些images是如何让containers使用的。最后简短介绍一些images和containers操做。
 
Images and layers

 
每一个Docker image都会参照只读列表里的layers,这些layers表明不一样的文件系统。
layers被堆放在顶端,以造成容器根文件系统的基础。
下图,Ubuntu15.04 image包含了4 个堆image layers.

 

Docker 存储引擎负责堆放这些layers,而后提供一个统一的展现。
 
当建立一个新的容器,将底层堆的最上层设置成可写,一般这层叫作“containers layer”
对正在运行的容器作全部的修改,好比建立新文件,修改已存在的文件,删除文件。这些操做都是在可写的容器层。
下图,展现了以Ubuntu15.04为基础的容器。
 

 

Containers and layers

 
容器和镜像主要的不一样就是,顶层可写层。全部的修改操做都是存储在这个可写层的。
当容器被删除的时候这个可写层也就被删除了。而底层的image没有改变。
 
由于每一个容器有本身的可写容器层,全部的更改都存储在这容器层中,这意味着多个容器能够同时使用同一个底层image。
下图显示多个容器共用Ubuntu15.04image:

 

docker存储引擎负责开启和管理 image 层 和可写的container 层。
存储引擎的两个关键技术:堆栈image层和copy-on-write.
 
Copy-on-write 策略

 
共享是提高资源利用率的很好途径。
copy-on-write是个很想共享和复制的策略。
  • 须要相同数据的系统进程共享该数据的同一实例,而不是拥有本身的副本。
  • 若是一个进程须要修改和写数据,操做系统会给这个进程复制须要的数据给它使用。
    • 只有须要写数据的进程才有权访问复制的数据。
  • 其余全部的进程继续使用原共享数据
 
docker在image和containers中都是用了copy-on-write技术。CoW策略优化了image 磁盘空间使用以及 容器的启动时间性能。
 
共享减小images空间

 
全部image和containers 层存在与Dcoker本地的存储空间中,都被存储引擎管理。
通常在/var/lib/docker/. 目录中。
zane@zane-V:~$ docker pull ubuntu:15.04
15.04: Pulling from library/ubuntu
 
9502adfba7f1: Pull complete
4332ffb06e4b: Pull complete
2f937cc07b5f: Pull complete
a3ed95caeb02: Pull complete
Digest: sha256:2fb27e433b3ecccea2a14e794875b086711f5d49953ef173d8a03e8707f1510f
Status: Downloaded newer image for ubuntu:15.04
 
从上面的输出来看,实际上pull 了4个image layer.这四个image layers 组成了ubuntu:15.04 这个Dcokerimage。
这4个image layer 分别存在本身目录中,固然这些目录在Dcoker 本地的存储空间中
 
在docker1.10以前,存储每一个layer都是以image layer ID为目录名称存储的。docker1.10以后就不是这样了。
尽管显示的方式不一样,可是1.10先后都是能够共享image层的。
 
作些实验来讲明共享image:
1.在空目录中写Dockerfile,以15.04为基础
FROM ubuntu:15.04
 
2.在image中 /tmp目录增长newfile,内容为"Hello world"
FROM ubuntu:15.04
 
RUN echo "Hello world" > /tmp/newfile
 
3.保存并退出文件
 
4.在Dockerfile文件的目录下启动命令行,并建立新image
zane@zane-V:~/mydockerbuild$ docker build -t changed-ubuntu .
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM ubuntu:15.04
 ---> d1b55fd07600
Step 2 : RUN echo "Hello world" > /tmp/newfile
 ---> Running in 914cfa4e2724
 ---> 3a3a082982a2
Removing intermediate container 914cfa4e2724
Successfully built 3a3a082982a2
 
上面的输出显示新的image被建立ID:3a3a082982a2
 
5.运行docker image命令验证新changed-ubuntu image 已经在docker本地存储空间
zane@zane-V:~/mydockerbuild$ docker images
REPOSITORY              TAG                 IMAGE ID            CREATED             SIZE
changed-ubuntu          latest              3a3a082982a2        3 minutes ago       131.3 MB
zane/aiapple            007                 d2f5f84bec87        12 days ago         275.1 MB
zane/aiapple            009                 d2f5f84bec87        12 days ago         275.1 MB
ziapple                 latest              d2f5f84bec87        12 days ago         275.1 MB
 
6.运行docker history 命令来查看建立changed-ubuntu image 时使用了哪些image 层。
zane@zane-V:~/mydockerbuild$ docker history changed-ubuntu
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
3a3a082982a2        6 minutes ago       /bin/sh -c echo "Hello world" > /tmp/newfile    12 B               
d1b55fd07600        11 months ago       /bin/sh -c #(nop) CMD ["/bin/bash"]             0 B                 
<missing>           11 months ago       /bin/sh -c sed -i 's/^#\s*\(deb.*universe\)$/   1.879 kB           
<missing>           11 months ago       /bin/sh -c echo '#!/bin/sh' > /usr/sbin/polic   701 B               
<missing>           11 months ago       /bin/sh -c #(nop) ADD file:3f4708cf445dc1b537   131.3 MB
 
能够看到只有3a3a082982a2 是刚建立的,且执行了咱们预期的命令。而其他4个都是ubuntu.15.04 本来就有的。
能够看到他们的建立时间都是好久以前的。
 
注意:changed-ubuntu image并无本身独享的每个image层。从下图中能够看到,新image 与ubuntu15.04是共享底层的4个image的。
图片是官方例子,其实本地的3a3a082982a2,就是官方例子中的94e6b7d2c720.

 

能够看到docker history 还能够看到每一层的大小,咱们刚刚建立的3a3a082982a2 只有12B,也就是说,咱们刚刚建立了changed-ubuntu只额外占用了12B 的空间,其他都是共享的。
 
那么若是再以changed-ubuntu为基础image 再建立新的image呢?
重写Dcokerfile:
zane@zane-V:~/mydockerbuild$ cat Dockerfile
FROM changed-ubuntu
RUN echo "Based on changed-ubuntu" > /tmp/new.t
 
建立新的image:
zane@zane-V:~/mydockerbuild$ docker build -t bashed_on_changed-ubuntu .
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM changed-ubuntu
 ---> 3a3a082982a2
Step 2 : RUN echo "Based on changed-ubuntu" > /tmp/new.t
 ---> Running in 0132c351fa33
 ---> 93a447865aa6
Removing intermediate container 0132c351fa33
Successfully built 93a447865aa6
 
查看新image的history
zane@zane-V:~/mydockerbuild$ docker history bashed_on_changed-ubuntu
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
93a447865aa6        29 seconds ago      /bin/sh -c echo "Based on changed-ubuntu" > /   24 B               
3a3a082982a2        31 minutes ago      /bin/sh -c echo "Hello world" > /tmp/newfile    12 B               
d1b55fd07600        11 months ago       /bin/sh -c #(nop) CMD ["/bin/bash"]             0 B                 
<missing>           11 months ago       /bin/sh -c sed -i 's/^#\s*\(deb.*universe\)$/   1.879 kB           
<missing>           11 months ago       /bin/sh -c echo '#!/bin/sh' > /usr/sbin/polic   701 B               
<missing>           11 months ago       /bin/sh -c #(nop) ADD file:3f4708cf445dc1b537   131.3 MB
能够看到bashed_on_changed-ubuntu,共享了changed-ubuntu的全部image,在从新建立了93a447865aa6;而且只占用了额外的24B。
 
 
复制使容器更效率

只有容器层是可写的,共享image 层都是只读的。这样保证了,多个容器层底层共享image的数据安全。
每一个容器有它本身的可写层。

 

当在容器中修改一个已存在的文件,Dcoker使用存储引擎执行copy-on-write 操做。
操做的细节取决于不一样的存储引擎,对于AUFS 和 OverlayFS 存储引擎来讲copy-on-write操做很是完美:
  • 经过image 层 搜索须要更改的文件。
    • 该过程从顶部,最新层开始,并向下工做到基本层一次一层。
  • 在找到的文件第一次复制的时候执行copy-up操做。
    • copy-up 复制这个文件到容器的本身的可写层。
  • 在容器本身的可写成修改刚刚复制过来的文件。
 
若是以咱们刚刚建立的changed-ubuntu 为基础运行了5个容器会发生什么?
 
1.在命令行运行5次docker run
zane@zane-V:~$ docker run  -dit changed-ubuntu bash
9635de83c668a61be7077ad309798a686fbfe29abf5f152dc84d641dd4b84a7b
zane@zane-V:~$ docker run  -dit changed-ubuntu bash
f92f01d11ff0d93f09ca6b94ebda3b699dc959297cf90565acf7f337d1c8af03
zane@zane-V:~$ docker run  -dit changed-ubuntu bash
a341fec4b3604a21dda733839cfea56005712335daa8344ece0a22c4df53076a
zane@zane-V:~$ docker run  -dit changed-ubuntu bash
54b836a993084953972507015515ad4a9c8c779508ad1a581a7b50fa0a496f42
zane@zane-V:~$ docker run  -dit changed-ubuntu bash
7f3ea62f1430b20ab7948281d88ef96140a688cf7bd08454c40ce1ad3d057620
 
以changed-ubuntu image为基础启动了5个容器。当每一个容器被建立后,Dcoker 会分别增长一个可写层,并分配随机的UUID。
 
2.运行docker ps 命令验证5个容器正常运行中
zane@zane-V:~$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
7f3ea62f1430        changed-ubuntu      "bash"              3 minutes ago       Up 3 minutes                            berserk_borg
54b836a99308        changed-ubuntu      "bash"              3 minutes ago       Up 3 minutes                            condescending_mccarthy
a341fec4b360        changed-ubuntu      "bash"              3 minutes ago       Up 3 minutes                            ecstatic_saha
f92f01d11ff0        changed-ubuntu      "bash"              3 minutes ago       Up 3 minutes                            hungry_mestorf
9635de83c668        changed-ubuntu      "bash"              3 minutes ago       Up 3 minutes
上面5个运行的容器,都共享changed-ubuntu镜像。CONTAINER ID 是由 UUID所派生的。
 
3.列出本地存储的内容
$ sudo ls /var/lib/docker/containers
 
copy-on-write
  • 不只仅减小了容器使用的空间,
  • 并且减小了容器启动须要的时间。
由于在启动的时候,Dcoker仅须要为每一个容器建立一个可写层。
 
数据卷和存储引擎

当删除容器时,任何没有存到数据卷里的容器修改内容,也会被删除。
 
数据卷是docker 本地主机的文件系统里的一个目录或者文件,这个目录或文件是直接挂载到容器里的。
数据卷是不被docker的存储引擎所控制的。读写数据卷是绕过存储引擎的,并以本机主机速度运行。
 
能够挂载多个数据卷到容器。多个容器也能够共享一个或者多个数据卷。
数据卷驻留在Docker主机上的本地存储区域以外,进一步加强了它们与存储驱动程序控制的独立性。
当容器被删除时,任何存储到数据卷的数据都会持久化在docker 主机上。
 
总结

  • 新建容器,将底层堆的最上层设置为可写,叫作“containers layer”。对运行容器的修改,都是保存在这一层,而不会对底层image(不可写)作任何改变
  • docker存储引擎
    • 任务
      • 开启和管理image层和container层
    • 关键技术
      • 堆栈image层
      • copy-on-write
  • copy-on-write
    • 共享是提高资源利用率的很好途径
    • copy-on-write是很好的共享和复制的策略
      • 系统进程共享同一实例,不是拥有本身的副本
      • 须要修改时,给这进程复制须要的数据
    • 优势
      • 优化了image磁盘空间使用
        • 由于共享了公用数据
      • 容器的启动时间性能
        • 启动时,仅须要为每一个容器建立一个可写层便可
  • 当容器须要修改文件,docker存储引擎执行copy-on-write操做
    • 不一样存储引擎操做细节不一样,AUFS,OverlayFS 执行过程
      • 经过image 层 搜索须要更改的文件
        • 该过程从顶部,最新层开始,并向下工做到基本层一次一层
      • 在找到的文件第一次复制的时候执行copy-up操做
        • copy-up 复制这个文件到容器的本身的可写层
      • 在容器本身的可写成修改刚刚复制过来的文件
  • 数据卷
    • 删除容器时,任何存到数据卷的数据都会持久化到docker主机上
    • 数据卷是本地主机的一个目录或文件,可直接挂载到容器里
    • 数据卷是不被docker存储引擎控制。
      • 读写数据卷是绕过存储引擎的,并以本地主机速度运行
    • 容器与数据卷
      • 可挂载多个数据卷到一个容器
      • 也可多个容器共享一个数据卷
相关文章
相关标签/搜索