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

为了很好的使用存储引擎,必须明白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/. 目录中。

[email protected]:~$ 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

[email protected]:~/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本地存储空间

[email protected]:~/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 层。

[email protected]:~/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:

[email protected]:~/mydockerbuild$ cat Dockerfile
FROM changed-ubuntu
RUN echo "Based on changed-ubuntu" > /tmp/new.t

创建新的image:

[email protected]:~/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

[email protected]:~/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

[email protected]:~$ docker run  -dit changed-ubuntu bash
9635de83c668a61be7077ad309798a686fbfe29abf5f152dc84d641dd4b84a7b
[email protected]-V:~$ docker run  -dit changed-ubuntu bash
f92f01d11ff0d93f09ca6b94ebda3b699dc959297cf90565acf7f337d1c8af03
[email protected]-V:~$ docker run  -dit changed-ubuntu bash
a341fec4b3604a21dda733839cfea56005712335daa8344ece0a22c4df53076a
[email protected]-V:~$ docker run  -dit changed-ubuntu bash
54b836a993084953972507015515ad4a9c8c779508ad1a581a7b50fa0a496f42
[email protected]-V:~$ docker run  -dit changed-ubuntu bash
7f3ea62f1430b20ab7948281d88ef96140a688cf7bd08454c40ce1ad3d057620

以changed-ubuntu image为基础启动了5个容器。当每个容器被创建后,Dcoker 会分别增加一个可写层,并分配随机的UUID。

2.运行docker ps 命令验证5个容器正常运行中

[email protected]:~$ 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存储引擎控制。
      • 读写数据卷是绕过存储引擎的,并以本地主机速度运行
    • 容器与数据卷
      • 可挂载多个数据卷到一个容器
      • 也可多个容器共享一个数据卷
时间: 2024-10-27 05:09:40

理解镜像,容器,存储引擎的相关文章

理解镜像、容器和存储驱动

理解镜像.容器和存储驱动 为了更有效地使用存储驱动,你必须理解Docker如何创建和存储镜像.接下来,需要理解容器是如何使用这些镜像的.最后,你需要一个对镜像和容器操作者都需要的技术简介. 镜像和图层layers 每一个Docker镜像都参考了一系列的只读层,这些层代表着文件系统的区别.层级是从底层开始,逐一建立组成容器的root文件系统.下面的图显示了Ubuntu镜像有4层: Docker的存储驱动是负责堆放这些层级并且提供一个统一的视图. 当你创建一个新的容器,你会在底层栈上加入一个新的.稀

docker镜像与容器存储结构分析

注意:转载请注明出处:http://www.programfish.com/blog/?p=9 Docker是一个开源的应用容器引擎,主要利用linux内核namespace实现沙盒隔离,用cgroup实现资源限制. Docker 支持三种不同的镜像层次存储的drivers: aufs.devicemapper.btrfs ; Aufs: AUFS (AnotherUnionFS) 是一种 Union FS, 简单来说就是支持将不同目录挂载到同一个虚拟文件系统下(unite several di

自己对MyISAM存储引擎的理解

介绍 mysql中用的最多存储引擎就是innodb和myisam.做为Mysql的默认存储引擎,myisam值得我们学习一下,以下是我对<高性能MYSQL>书中提到的myisam的理解,请大家多多指教. 特点 > 不支持事务 证明如下: >> 表记录:t2表的engine是myisam. >> 操作 注意:如果你在数据库进行事务操作,但是事务无法成功,你就要看你的表引擎了,看这种引擎是否支持事务. >> 下面请看innodb中的事务操作 > 存储

10张图带你深入理解Docker容器和镜像

图解Docker容器和镜像 这篇文章希望能够帮助读者深入理解Docker的命令,还有容器(container)和镜像(image)之间的区别,并深入探讨容器和运行中的容器之间的区别. 当我对Docker技术还是一知半解的时候,我发现理解Docker的命令非常困难.于是,我花了几周的时间来学习Docker的工作原理,更确切地说,是关于Docker统一文件系统(the union file system)的知识,然后回过头来再看Docker的命令,一切变得顺理成章,简单极了. 题外话:就我个人而言,

10 张图带你深入理解 Docker 容器和镜像

这篇文章希望能够帮助读者深入理解 Docker 的命令,还有容器(container)和镜像(image)之间的区别,并深入探讨容器和运行中的容器之间的区别. 当我对 Docker 技术还是一知半解的时候,我发现理解 Docker 的命令非常困难.于是,我花了几周的时间来学习 Docker 的工作原理,更确切地说,是关于 Docker 统一文件系统(the union file system)的知识,然后回过头来再看 Docker 的命令,一切变得顺理成章,简单极了. 题外话:就我个人而言,掌握

理解存储引擎

一.什么是存储引擎 mysql中建立的库===>文件夹 库中建立的表===>文件 生活中我们用来存储数据的文件有不同的类型,每种文件类型对应各自不同的处理机制:比如处理文本用txt类型,处理表格用excel,处理图片用png等. 数据库中的表也应该有不同的类型,表的类型不同,会对应mysql不同的存取机制,表类型又称为存储引擎. 存储引擎说白了就是如何存储数据.如何为存储的数据建立索引和如何更新.查询数据等技术的实现方法.因为在关系数据库中数据的存储是以表的形式存储的,所以存储引擎也可以称为表

mysql中索引,触发器,事务,存储引擎的理解

1.索引:作用于表中的某列,并将其进行排序,有助于快速地进行查询. 索引是对数据库表中一个或多个列的值进行排序的数据结构,是用于提高在数据库表中访问数据的速度的数据库对象.其实索引相当于一本书的目录,如果没有索引,要想在数据库中查找某一特定的值就需要遍历整个数据库表,但是有了索引之后就可以在索引当中查找,有助于更快地获取信息: 索引可分为聚集索引和非聚集索引: 聚集索引:是按照数据存放的物理位置为顺序的: 非聚集索引中,表数据存储顺序与索引顺序无关:一张表上只能创建一个聚集索引,因为真实数据的物

mariadb之查询及存储引擎

在关系型数据库中,如果要设计成表机制来存储数据,必须满足基本的范式,至少满足前三个范式. 第一范式(1NF):是指在关系模型中,对域(域代表字段)添加的一个规范要求,所有的域都应该是原子性的(不可分拆),即数据库表的每一列都是不可分割的原子数据项,而不能是集合,数组,记录等非原子数据项.即实体(每一行)中的某个属性有多个值时,必须拆分为不同的属性.在符合第一范式(1NF)表中的每个域值只能是实体的一个属性或一个属性的一部分.简而言之,第一范式基本要求就是无重复的域. 第二范式(2NF):是在第一

MySQL数据库InnoDB存储引擎多版本控制(MVCC)实现原理分析

文/何登成 导读:   来自网易研究院的MySQL内核技术研究人何登成,把MySQL数据库InnoDB存储引擎的多版本控制(简称:MVCC)实现原理,做了深入的研究与详细的文字图表分析,方便大家理解InnoDB存储引擎实现的多版本控制技术(简称:MVCC). 基本知识 假设对于多版本控制(MVCC)的基础知识,有所了解.MySQL数据库InnoDB存储引擎为了实现多版本的一致性读,采用的是基于回滚段的协议. 行结构 MySQL数据库InnoDB存储引擎表数据的组织方式为主键聚簇索引.由于采用索引