Docker官方用户指南(中文)——关于Docker镜像、Docker容器和Docker存储驱动

关于 images、containers、storage drivers


英语原文:(https://docs.docker.com/engine/userguide/storagedriver/imagesandcontainers/#data-volumes-and-the-storage-driver

为了用好Docker存储驱动,先要了解:

  • docker如何来制作和保存docker镜像(Image)
  • docker镜像是如何被docker容器使用
  • docker镜像、容器的操作技能

了解Docker如何管理docker镜像和docker容器内部的数据,有助于你了解docker容器设计、应用软件“docker化”的最佳方式,并据此来避免性能问题。

镜像和层(Images and layers):

Docker镜像建立在一系列的docker层之上。每一层表现为镜像dockerfile中的一个指令。除了最后一个,每一层都是只读的。思考一下这个dockerfile:

FROM ubuntu:15.10
COPY . /app
RUN make /app
CMD python /app/app.py

这个dockerfile包含四条命令,每一条命令创建一个层。
“FROM”-声明开始从创建一个层从Ubuntu:15.10镜像。
“COPY”-命令从您的DOCKER客户端的当前目录导入一些文件。
"RUN"-命令使用make命令构建应用程序。
最后,最后一层指定要在容器中运行的命令。


每层只有一组不同的层之前。这些层叠在彼此的顶部。创建新容器时,在底层上添加新的可写层。这个层通常被称为“容器层”。对正在运行的容器所做的所有更改,如写入新文件、修改现有文件和删除文件都将写入该薄可写容器层。下图显示了基于Ubuntu 15.04镜像的容器。

下图显示了一个包含Ubuntu 15.04镜像的容器,该镜像包含了4个层叠的镜像层,以及顶部的可写容器层。

存储驱动程序处理有关这些层交互方式的详细信息。不同的存储驱动程序,在不同的情况下有优缺点。

Content addressable storage(可寻址存储)

如果你已运行Docker 1.10或更高版本,可跳过这一节直接阅读[Docker容器和层]。Docker 1.10引入了一个新的内容寻址存储模型。这是一种全新的方式,以解决磁盘上的Image镜像和Layer层数据。此前,镜像和层的数据参考和使用随机生成的UUID储存。在新的模型中,这将被一个[安全内容散列]替换。

新的模式提高了安全性,并提供了一种内置的方式来避免ID冲突,并保证pull, push, load, save后的数据完整性。通过允许多个镜像任意共享它们的层来也使得共享性增强,即使他们没有来自相同的构建。

下图是前一个示意图的更新版本,突出了Docker1.10之后的变化。

所有的镜像层的ID是加密的哈希散列,而容器的ID仍然是一个随机生成的UUID。

新的模式需要对从现有Docker1.9版本和1.9以下的镜像进行升级,包括镜像和层文件系统结构也发生了一些变化。如果你已经运行Docker 1.10及以上版本,你可以跳过这一节。

在Docker1.9或更早版本创建的镜像需要升级之前,他们可以用新的模型。这种升级涉及到计算新的安全校验和,当新版Docker守护进程第一次启动的时候会自动执行。升级完成后,所有镜像和TAG标签将拥有全新的安全ID。

虽然迁移是自动和透明的,但它是计算密集型的。这可能需要一些时间来完成,而在这段时间,Docker无法响应其他请求。如果这会是一个问题,你可以在升级Docker前,使用迁移工具迁移现有的镜像到新的格式。这避免了停机时间,并允许您将已迁移的镜像分发到已经升级的系统。更多信息,见https://github.com/docker/v1.10-migrator/releases。

在迁移过程中,你需要让你的Docker主机的数据目录迁移容器。如果你使用的是默认的Docker数据路径,使用命令如下:

$ sudo docker run --rm -v /var/lib/docker:/var/lib/docker docker/v1.10-migrator

 

如果你使用devicemapper驱动程序,你需要包括--privileged选项使容器可以访问您的存储设备。

容器和层(Container   layers)

容器和镜像的主要区别是顶层可写层。所有写入新添加或修改现有数据的容器都存储在此可写层中。当容器被删除时,可写层也被删除。基础镜像保持不变。

由于每个容器都有自己的可写容器层,所有的更改都存储在这个容器层中,多个容器可以共享对同一基础镜像的访问,但也有自己的数据状态。下图显示了多个容器共用相同的Ubuntu 15.04镜像。

注意:如果你需要多个镜像可以共享访问完全相同的数据,应该把这些数据保存到一个Docker Volume中,并装载(Mount)到各个容器。

Docker使用存储驱动程序来管理镜像层内容、可写容器层。每个存储驱动程序处理的实现方式不同,但所有的驱动都使用可堆叠的镜像层和即写即拷copy-on-write (CoW) 策略。

CoW即写即拷(copy-on-write)策略

写副本是一种共享和复制文件的策略,以达到最大的效率。如果文件或目录存在于Docker镜像的较低层中,而另一层(包括可写层)需要对其进行读访问,则只使用现有文件。当另一个层在第一次需要修改文件(在生成Docker镜像或运行Docker容器时),文件被复制到该层并修改。这样可最大限度地减少I / O和每个后续层的大小。这些优点在下面解释得更为深入。

共享机制使Docker镜像较小

当你使用docker pull从Docker仓库下载一个镜像,或当你创建一个本地还不存在的镜像容器时,每个镜像层是分别下载的,并存储在Docker的本地存储区,在Linux主机中通常是/var/lib/docker/。看一下这个镜像层下载的例子:

$ docker pull ubuntu:15.04

15.04: Pulling from library/ubuntu
1ba8ac955b97: Pull complete
f157c4e5ede7: Pull complete
0b7e98f84c4c: Pull complete
a3ed95caeb02: Pull complete
Digest: sha256:5e279a9df07990286cce22e1b0f5b0490629ca6d187698746ae5e28e604a640e
Status: Downloaded newer image for ubuntu:15.04

每一个镜像层被保存在Docker宿主机本地存储区的自有文件夹内。可以通过查看 /var/lib/docker/<storage-driver>/layers/来观察一下文件系统中的Docker镜像层,这个示例采用了 aufs  这是默认的存储驱动。

$ ls /var/lib/docker/aufs/layers
1d6674ff835b10f76e354806e16b950f91a191d3b471236609ab13a930275e24
5dbb0cbe0148cf447b9464a358c1587be586058d9a4c9ce079320265e2bb94e7
bef7199f2ed8e86fa4ada1309cfad3089e0542fec8894690529e4c04a7ca2d73
ebf814eccfe98f2704660ca1d844e4348db3b5ccc637eb905d4818fbfb00a06a

文件夹名称与镜像层的ID并不是一一对应的 (Docker 1.10之后修复了?).

现在假设你有不同的Dockerfiles. 你用其中第1个构建docker镜像: acme/my-base-image:1.0.

FROM ubuntu:16.10
COPY . /app

第2个基于 acme/my-base-image:1.0, 但还有一些另外的镜像层:

FROM acme/my-base-image:1.0
CMD /app/hello.sh

第2个镜像包含了第1个镜像里所有的层,外加一个带有 RUN 指令的新层,一个可读写的镜像层。Docker已经有了来自第一个镜像的所有层, 所以不再需要下载(pull)了。这两个Docker镜像会共享两者共有的任何镜像层。

如果用两个Dockerfile构建镜像,你可以使用 docker images 和 docker history 命令来验证共享镜像层的ID是否相同。

  1. 新建 cow-test/ 文件夹,并切换到该文件夹内。

  2. 在 cow-test/文件夹内新建一个文件,内容为:

    #!/bin/sh
    echo "Hello world"
    

    保存,并使其可执行:

    chmod +x hello.sh
    
  3. 将上文第1个Dockerfile的内容复制到一个名为 Dockerfile.base 的新文件中。

  4. 将上文第2个Dockerfile的内容复制到一个名为 Dockerfile 的新文件中。

  5. 转到 cow-test/ 目录下,构建第一个Docker镜像:

    $ docker build -t acme/my-base-image:1.0 -f Dockerfile.base .
    
    Sending build context to Docker daemon  4.096kB
    Step 1/2 : FROM ubuntu:16.10
     ---> 31005225a745
    Step 2/2 : COPY . /app
     ---> Using cache
     ---> bd09118bcef6
    Successfully built bd09118bcef6
    Successfully tagged acme/my-base-image:1.0
    
  6. 构建第2个:

    $ docker build -t acme/my-final-image:1.0 -f Dockerfile .
    
    Sending build context to Docker daemon  4.096kB
    Step 1/2 : FROM acme/my-base-image:1.0
     ---> bd09118bcef6
    Step 2/2 : CMD /app/hello.sh
     ---> Running in a07b694759ba
     ---> dbf995fc07ff
    Removing intermediate container a07b694759ba
    Successfully built dbf995fc07ff
    Successfully tagged acme/my-final-image:1.0
    
  7. 检查Docker镜像的文件大小:

    $ docker images
    
    REPOSITORY                                            TAG                          IMAGE ID            CREATED             SIZE
    acme/my-final-image                                   1.0                          dbf995fc07ff        58 seconds ago      103MB
    acme/my-base-image                                    1.0                          bd09118bcef6        3 minutes ago       103MB
    
  8. 查看每个Docker镜像的层:

    $ docker history bd09118bcef6
    IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
    bd09118bcef6        4 minutes ago       /bin/sh -c #(nop) COPY dir:35a7eb158c1504e...   100B                
    31005225a745        3 months ago        /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B                  
    <missing>           3 months ago        /bin/sh -c mkdir -p /run/systemd && echo '...   7B                  
    <missing>           3 months ago        /bin/sh -c sed -i 's/^#\s*\(deb.*universe\...   2.78kB              
    <missing>           3 months ago        /bin/sh -c rm -rf /var/lib/apt/lists/*          0B                  
    <missing>           3 months ago        /bin/sh -c set -xe   && echo '#!/bin/sh' >...   745B                
    <missing>           3 months ago        /bin/sh -c #(nop) ADD file:eef57983bd66e3a...   103MB      
    
    $ docker history dbf995fc07ff
    
    IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
    dbf995fc07ff        3 minutes ago       /bin/sh -c #(nop)  CMD ["/bin/sh" "-c" "/a...   0B                  
    bd09118bcef6        5 minutes ago       /bin/sh -c #(nop) COPY dir:35a7eb158c1504e...   100B                
    31005225a745        3 months ago        /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B                  
    <missing>           3 months ago        /bin/sh -c mkdir -p /run/systemd && echo '...   7B                  
    <missing>           3 months ago        /bin/sh -c sed -i 's/^#\s*\(deb.*universe\...   2.78kB              
    <missing>           3 months ago        /bin/sh -c rm -rf /var/lib/apt/lists/*          0B                  
    <missing>           3 months ago        /bin/sh -c set -xe   && echo '#!/bin/sh' >...   745B                
    <missing>           3 months ago        /bin/sh -c #(nop) ADD file:eef57983bd66e3a...   103MB  
    

    注意所有的镜像都是一致的,除了第2个镜像的顶层。 所有的层在两个镜像间共享, 并只在 /var/lib/docker/ 中保存一次。 这个新的层其实并没有占用多少空间,因为它并没有改变任何文件, 只是执行了一条命令。

    Note: The <missing> lines in the docker history output indicate that those layers were built on another system and are not available locally. This can be ignored.

Copying 使Docker容器更高效

启动容器时,在其他层的顶部添加一个薄可写容器层。存储在文件系统中的任何更改都存储在这里。容器不更改的任何文件都不会复制到该可写层。这意味着可写层越小越好。

当容器中的现有文件被修改时,存储驱动程序在写操作上执行一个副本。具体步骤依赖于特定的存储驱动程序。

对于默认的 aufs 驱动和 overlay 、 overlay2 格式,“即拷即写” copy-on-write 操作依次为以下四步:

  • 通过镜像层搜索需要更新的文件。这个过程会从最新的层开始,一直到最底层,每次一个层。当找到结果时,它们会被添加到高速缓存中以加速其后的操作。

  • 执行 copy_up 命令,在找到的第一个副本, 将文件复制到容器的可写层。

  • 任何修改只针对该文件的副本,容器无法看到存在于低层文件的只读副本。

Btrfs, ZFS, 和其他驱动对于处理 copy-on-write 是不同的。 可以在详细描述中读到更多关于这些驱动方法。

写大量数据的容器将比其它容器耗费更多的空间,这是因为大多数写操作会在容器顶部的薄可写层中,占用更多的空间。

Note: 对于大量写操作的应用程序,不应该将数据 保存在容器中。而是使用Docker volumes,它独立于运行中的容器,且被设计为高效的I/O。另外,docker卷可以在容器间共享,不会增加你的容器读写层的空间大小。

copy_up operation can incur a noticeable performance overhead. This overhead is different depending on which storage driver is in use. Large files, lots of layers, and deep directory trees can make the impact more noticeable. This is mitigated by the fact that each copy_up operation only occurs the first time a given file is modified.

To verify the way that copy-on-write works, the following procedures spins up 5 containers based on the acme/my-final-image:1.0 image we built earlier and examines how much room they take up.

Note: This procedure won’t work on Docker for Mac or Docker for Windows.

  1. 在Docker主机中,执行 docker run 命令。执行结果(底部的字符串)返回每个容器的ID:

    $ docker run -dit --name my_container_1 acme/my-final-image:1.0 bash \
      && docker run -dit --name my_container_2 acme/my-final-image:1.0 bash \
      && docker run -dit --name my_container_3 acme/my-final-image:1.0 bash \
      && docker run -dit --name my_container_4 acme/my-final-image:1.0 bash \
      && docker run -dit --name my_container_5 acme/my-final-image:1.0 bash
    
      c36785c423ec7e0422b2af7364a7ba4da6146cbba7981a0951fcc3fa0430c409
      dcad7101795e4206e637d9358a818e5c32e13b349e62b00bf05cd5a4343ea513
      1e7264576d78a3134fbaf7829bc24b1d96017cf2bc046b7cd8b08b5775c33d0c
      38fa94212a419a082e6a6b87a8e2ec4a44dd327d7069b85892a707e3fc818544
      1a174fc216cccf18ec7d4fe14e008e30130b11ede0f0f94a87982e310cf2e765
    
  2. 执行 docker ps 命令,验证5个容器是否正在运行:

    CONTAINER ID        IMAGE                     COMMAND                  CREATED              STATUS              PORTS               NAMES
    1a174fc216cc        acme/my-final-image:1.0   "bash"                   About a minute ago   Up About a minute                       my_container_5
    38fa94212a41        acme/my-final-image:1.0   "bash"                   About a minute ago   Up About a minute                       my_container_4
    1e7264576d78        acme/my-final-image:1.0   "bash"                   About a minute ago   Up About a minute                       my_container_3
    dcad7101795e        acme/my-final-image:1.0   "bash"                   About a minute ago   Up About a minute                       my_container_2
    c36785c423ec        acme/my-final-image:1.0   "bash"                   About a minute ago   Up About a minute                       my_container_1
    
  3. 列出本地存储区的内容:

    $ sudo ls /var/lib/docker/containers
    
    1a174fc216cccf18ec7d4fe14e008e30130b11ede0f0f94a87982e310cf2e765
    1e7264576d78a3134fbaf7829bc24b1d96017cf2bc046b7cd8b08b5775c33d0c
    38fa94212a419a082e6a6b87a8e2ec4a44dd327d7069b85892a707e3fc818544
    c36785c423ec7e0422b2af7364a7ba4da6146cbba7981a0951fcc3fa0430c409
    dcad7101795e4206e637d9358a818e5c32e13b349e62b00bf05cd5a4343ea513
    
  4. 检查docker镜像的大小:

    $ sudo du -sh /var/lib/docker/containers/*
    
    32K  /var/lib/docker/containers/1a174fc216cccf18ec7d4fe14e008e30130b11ede0f0f94a87982e310cf2e765
    32K  /var/lib/docker/containers/1e7264576d78a3134fbaf7829bc24b1d96017cf2bc046b7cd8b08b5775c33d0c
    32K  /var/lib/docker/containers/38fa94212a419a082e6a6b87a8e2ec4a44dd327d7069b85892a707e3fc818544
    32K  /var/lib/docker/containers/c36785c423ec7e0422b2af7364a7ba4da6146cbba7981a0951fcc3fa0430c409
    32K  /var/lib/docker/containers/dcad7101795e4206e637d9358a818e5c32e13b349e62b00bf05cd5a4343ea513
    

    每个容器只占用文件系统32k空间大小。

即写即拷 copy-on-write 不仅节省磁盘空间,它还减少启动时间。当你启动一个容器(或基于同一个镜像的多个容器), Docker 仅需生成很“薄”的可写容器层。

如果Docker在每次启动一个新的容器时,不得不制作一个底层镜像的完整副本,容器的启动时间和磁盘空间将大大增加。这将类似于虚拟机的工作方式,每个虚拟机都有一个或多个虚拟磁盘。

数据卷(Data volumes) 和存储驱动

当Docker容器被删除后,任何写入Docker容器、而不是保存到data volume 的数据,将随着容器一起删除。
数据卷(Data Volume)是Docker主机文件系统中被直接加载进Docker容器的目录或文件。Data Volume不是由存储驱动程序控制的。读取和写入数据卷绕过存储驱动程序并以本地主机速度运行。你可以将任意数量的数据卷装入容器。多个容器也可以共享一个或多个数据卷。

下图展示了单个Docker host主机如何运行两个docker容器。每一个容器在Docker宿主机本地存储区的自有地址空间中存在 (/var/lib/docker/...)。有一个单独共享的Data Volume数据卷位于Docker主机的 /data 目录。它被直接mount到两个镜像中:

Data volumes 驻扎在 Dock Host的本地存储区之外。这使得更多的主机目录增加了独立性。 from the storage driver’s control. When a container is deleted, any data stored in data volumes persists on the Docker host.

Data volumes 驻于Docker host本地存储区外,使其相对存储驱动程序控制的独立性进一步增强。当容器被删除,存储数据量的任何数据在Docker主机上被保存。

 

有关数据卷的详细信息,请参见容器中的数据管理。

For detailed information about data volumes, see Managing data in containers.

 

待续:

Related information

 

转载于:https://my.oschina.net/yfun/blog/911437

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值