容器镜像按需加载
1、镜像知识点回顾
1.1、镜像技术
1.1.1. Images & layers
Docker image 是由一组上下有序的只读 layer 构建出来的,当用 Dockerfile 构建 image 时,Dockerfile 中每个命令就代表着一个 layer。每一个 layer 都只包含了相比于下一层的 layer 不同的内容,最后所有的 layer 堆叠起来就是一个新的 image。
以下面这个 Dockerfile 为例,它以 Ubuntu15.04 的 image 为基础 image ,加入一个 App 后构建出一个新的 image:
FROM ubuntu:15.04
COPY . /app
RUN make /app
CMD python /app/app.py
通过docker file制作镜像,每行命令产生了一个image layer。可以通过docker history {image_id}查看制作过程、命令和对应那一个layer。
$ docker build .
Sending build context to Docker daemon 3.072kB
Step 1/4 : FROM ubuntu:15.04
---> d1b55fd07600
Step 2/4 : COPY . /app
---> 31603973c835
Step 3/4 : RUN touch /app/test
---> Running in f5b9db5b7e18
Removing intermediate container f5b9db5b7e18
---> 4c95aef72941
Step 4/4 : CMD python /app/app.py
---> Running in 5a45dcd707cb
Removing intermediate container 5a45dcd707cb
---> f6509acd680a
Successfully built f6509acd680a
$ docker history f6509acd680a
IMAGE CREATED CREATED BY SIZE COMMENT
f6509acd680a About a minute ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "pyth… 0B
4c95aef72941 About a minute ago /bin/sh -c touch /app/test 0B
31603973c835 About a minute ago /bin/sh -c #(nop) COPY dir:8b4813429364f8894… 73B
d1b55fd07600 5 years ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 5 years ago /bin/sh -c sed -i 's/^#\s*\(deb.*universe\)$… 1.88kB
<missing> 5 years ago /bin/sh -c echo '#!/bin/sh' > /usr/sbin/poli… 701B
<missing> 5 years ago /bin/sh -c #(nop) ADD file:3f4708cf445dc1b53… 131MB
1.1.2. Container and layers
容器 = 镜像 + 读写层
通过镜像启动容器时,docker 将会在这些只读 layer 的顶部添加一个 writable layer ,也叫作 container layer,container 运行时的所有修改都会写到这个 writable layer 中,包括创建新文件、修改存在的文件、删除文件(Copy On Write)等。Docker 用 storage driver 负责处理各个 layer 之间的交互,docker 目前支持的 storage driver 有 overlay、aufs、zfs、btrfs、devicemapper 等,这些其实都是一些不同类型的联合文件系统。

Container 和 image 之间最大的不同就是顶层的那个 writable layer。Container 删除后,这个 writable layer也会被删除,底层的 image不会发生任何变化。因为所有的修改都写到了writable layer,所以多个container可以共享部分甚至全部的只读layer(底层image),

Copy-on-write 写时复制,顾名思义,在写数据的时候再对源数据进行拷贝,它将共享和拷贝文件的效率最大化。在 container 中,如果要修改一个在只读 layer 中已经存在的文件,这个文件会先被拷贝到writable layer,然后将修改写入 writable layer 中的文件副本。在build image的过程中,某一层要 layer 修改底层 layer 的文件,流程一样如此。
1.2、存储驱动
Docker 的存储驱动称为 graph driver 或 storage driver ,containerd 的存储驱动称为 snapshotter。不论是docker 还是 containerd 的存储驱动,作用都是将从远程存储把镜像下载下来,然后以各种存储驱动特有的方式将镜像转换为一个文件目录的形式,用作容器启动的 rootfs。
1.2.1. 基于目录Union mount的驱动:aufs, overlay, overlay2
文件级存储驱动一般使用一种 union mount 的方法,依赖文件语义实现镜像的分层和 COW,包括 aufs,overlay 和 overlays2。文件级存储驱动将多个文件系统目录根据层级关系整合为一个,特点如下:
在 image 的各个 layer 中查找目标文件;
将目标文件复制至容器的 writable layer;
在 writable layer 修改目标文件,这样镜像各个 layer 中的原文件将不会被读到。
优点:内存利用率高;
缺点:多写场景下 writable layer 会越来越大;
1.2.2. 基于块级快照的驱动: btrfs, zfs 和 devicemapper
块级存储驱动则一般基于内核对存储。比如基于btrfs、ZFS的快照特性。块级镜像方案有如下特点:
多写场景下性能更好;
btrfs 和 ZFS需要更多内存;
ZFS适用场景:高密度的PaaS集群;
下图对比了几种驱动的性能[15]:

1.2.3. 配置方法
下面以docker为例介绍使用方法[10-12]:通过docker info查看当前的存储驱动
$ docker info
Containers: 0
Images: 0
Storage Driver: overlay2
Backing Filesystem: xfs
<output truncated>
Docker所支持的存储驱动 overlay2 是当前 docker 默认的存储驱动,linux 支持度比较广泛。配置Docker 存储驱动除系统默认支持的文件系统,其他存储驱动需要将docker的工作目录/var/lib/docker/改成对应的文件系统(具体方法参考官网 https://docs.docker.com/storage/storagedriver/select-storage-driver/),然后更改 dockerd 配置文件。
# /etc/docker/daemon.json
{
"storage-driver": "overlay2"
}
Last updated
Was this helpful?