写点什么

《containerd 系列》了解 containerd 中的 snapshotter,先从 native 开始

  • 2024-04-29
    浙江
  • 本文字数:3623 字

    阅读完需:约 12 分钟

本文内容节选自 《containerd 原理剖析与实战》,本书正参加限时优惠内购,点击阅读原文,限时 69.9 元购买


上一篇文章一文了解 containerd 中的 snapshot》****中,介绍了 containerd 的 snapshot 机制,了解到 containerd 通过内置的 snapshotter 比如aufsbtrfsdevmappernativeoverlayfszfs 等,来完成 snapshot 生命周期的管理。


接下来我们从最简单的 native snapshotter 开始,带领大家了解 snapshotter 的实现。

native snapshotter

native snapshotter 是 containerd 中最早实现的 snapshotter,native snapshotter 使用的是原生的文件系统保存 snapshot,假如一个镜像有四层 layer,每层镜像 layer 有 10 MB 的未压缩文件,那么 snapshotter 将会创建四个 snapshot,分别是 10MB20MB30MB40MB,总共有 100MB大小。


换句话说,我们的镜像有 40MB,却占用了 100MB 的存储空间,存储效率确实有点低。不过对于其他 snapshotter (如 overlaydevmapper 等)来说,将会通过使用不同的策略来消除这种存储效率低下的问题。


下面通过一个镜像示例介绍 native snapshotter 原理,首先基于下面的 Dockerfile 构建一个镜像,代码如下。


# alpine image 占用存储空间比较小FROM alpine:latest# 每层分别创建 10MB 大小的文件RUN dd if=/dev/zero of=file_a bs=1024 count=10240RUN dd if=/dev/zero of=file_b bs=1024 count=10240RUN dd if=/dev/zero of=file_c bs=1024 count=10240
复制代码


基于 nerdctl 构建镜像,代码如下。


[root@zjz ~]# nerdctl build -t zhaojizhuang66/snapshots-test .
复制代码


推送镜像,代码如下。


[root@zjz ~]# nerdctl push zhaojizhuang66/snapshots-test
复制代码


通过 nerdctl 指定 native snapshotter 拉取镜像,代码如下。


[root@zjz ~/containerd]# nerdctl --snapshotter native pull zhaojizhuang66/testsnapshotter
复制代码


进入 native snapshots 对应的路径查看,代码如下。


[root@zjz ~/containerd]# cd /var/lib/containerd/io.containerd.snapshotter.v1.native/snapshots[root@zjz ~/containerd]# ls 1  2  3  4
复制代码


总共有 4 个 snapshot,查看每个 snapshot 的大小,可以看到每个 snapshot 的大小依次增加 10MB 左右。


[root@zjz /var/lib/containerd/io.containerd.snapshotter.v1.native/snapshots]# ls -lh 1 |head -n 1total 68K# 第 2 个 snapshots 为 alpine + 10MB[root@zjz /var/lib/containerd/io.containerd.snapshotter.v1.native/snapshots]# ls -lh 2 |head -n 1total 11M# 第 3 个 snapshots 为 alpine + 10MB + 10MB[root@zjz /var/lib/containerd/io.containerd.snapshotter.v1.native/snapshots]# ls -lh 3 |head -n 1total 21M# 第 4 个 snapshots 为 alpine + 10MB + 10MB + 10MB[root@zjz /var/lib/containerd/io.containerd.snapshotter.v1.native/snapshots]# ls -lh 4 |head -n 1total 31M
复制代码


接下来查看每个 snapshot 中的内容。


第 1 个 snapshot,代码如下。


[root@zjz /var/lib/containerd/io.containerd.snapshotter.v1.native/snapshots]# ll 1total 76drwxr-xr-x 19 root root 4096 Mar  7 14:56 .drwx------  6 root root 4096 Mar  7 14:56 ..drwxr-xr-x  2 root root 4096 Feb 11 00:45 bindrwxr-xr-x  2 root root 4096 Feb 11 00:45 devdrwxr-xr-x 17 root root 4096 Feb 11 00:45 etc... 省略 ...
复制代码


第 2 个 snapshot,代码如下。


[root@zjz /var/lib/containerd/io.containerd.snapshotter.v1.native/snapshots]#  ll 2total 10316drwxr-xr-x 19 root root     4096 Mar  7 14:56 .drwx------  6 root root     4096 Mar  7 14:56 ..drwxr-xr-x  2 root root     4096 Mar  7 14:56 bindrwxr-xr-x  2 root root     4096 Feb 11 00:45 devdrwxr-xr-x 17 root root     4096 Mar  7 14:56 etc-rw-r--r--  1 root root 10485760 Mar  7 14:47 file_a... 省略 ...
复制代码


第 3 个 snapshot,代码如下。


[root@zjz /var/lib/containerd/io.containerd.snapshotter.v1.native/snapshots]# ll 3total 20556drwxr-xr-x 19 root root     4096 Mar  7 14:56 .drwx------  6 root root     4096 Mar  7 14:56 ..drwxr-xr-x  2 root root     4096 Mar  7 14:56 bindrwxr-xr-x  2 root root     4096 Feb 11 00:45 devdrwxr-xr-x 17 root root     4096 Mar  7 14:56 etc-rw-r--r--  1 root root 10485760 Mar  7 14:47 file_a-rw-r--r--  1 root root 10485760 Mar  7 14:47 file_b... 省略 ...
复制代码


第 4 个 snapshot,代码如下。


[root@zjz /var/lib/containerd/io.containerd.snapshotter.v1.native/snapshots]# ll 4total 30796drwxr-xr-x 19 root root     4096 Mar  7 14:56 .drwx------  6 root root     4096 Mar  7 14:56 ..drwxr-xr-x  2 root root     4096 Mar  7 14:56 bindrwxr-xr-x  2 root root     4096 Feb 11 00:45 devdrwxr-xr-x 17 root root     4096 Mar  7 14:56 etc-rw-r--r--  1 root root 10485760 Mar  7 14:47 file_a-rw-r--r--  1 root root 10485760 Mar  7 14:47 file_b-rw-r--r--  1 root root 10485760 Mar  7 14:47 file_c... 省略 ...
复制代码


以上就是 naitve snapshotter 准备容器 rootfs 的过程。可以看到, 对于 native snapshotter 来说,多层 snapshotter 对于镜像存储来说又有些浪费的,总共 30MB 的镜像,经过 native snapshotter 解压之后,总共占用了 60MB 的存储空间。


下面看 native snapshotter 的源码可以具体实现,代码如下。


// 版本 v1.7.0// containerd/snapshots/native/native.gofunc (o *snapshotter) Prepare(ctx context.Context, key, parent string, opts ...snapshots.Opt) ([]mount.Mount, error) {   return o.createSnapshot(ctx, snapshots.KindActive, key, parent, opts)}

func (o *snapshotter) createSnapshot(ctx context.Context, kind snapshots.Kind, key, parent string, opts []snapshots.Opt) (_ []mount.Mount, err error) {

// 1. 获取 parent snapshot 的目录 parent := o.getSnapshotDir(s.ParentIDs[0])

// 2. 直接 copy parent snapshot 目录中的内容到新的 snapshot 目录 s.CopyDir(dst-snapshot-path, parent, ...); // 3. 返回的挂载信息为 return []mount.Mount{ { Source: dst-snapshot-path, Type: "bind", Options: []string{"rbind","ro"}, }, }}
复制代码


查看 snapshot 对应的挂载信息,代码如下。


# 启动容器,创建 active 状态的 snapshotroot@zjz:~# ctr run --snapshotter native -d docker.io/zhaojizhuang66/testsnapshotter:latest zjz
复制代码


# 看到多了一层 名为 zjz 的 active 的 snapshotroot@zjz:~# ctr snapshot --snapshotter native lsKEY                                                                     PARENT                                                                  KINDsha256:7cd52847ad775a5ddc4b58326cf884beee34544296402c6292ed76474c686d39                                                                         Committedsha256:db7e45c34c1fd60255055131918550259be8d7a83e0ac953df15d9410dc07b07 sha256:7cd52847ad775a5ddc4b58326cf884beee34544296402c6292ed76474c686d39 Committedsha256:a937f098cfdf05ea5f262cbba031de305649a102fabc47014d2b062428573d42 sha256:db7e45c34c1fd60255055131918550259be8d7a83e0ac953df15d9410dc07b07 Committedsha256:77297b225cd30d2ace7f5591a7e9208263428b291fd44aac95af92f7337b342a sha256:a937f098cfdf05ea5f262cbba031de305649a102fabc47014d2b062428573d42 Committedzjz                                                                     sha256:77297b225cd30d2ace7f5591a7e9208263428b291fd44aac95af92f7337b342a Active
复制代码


# 查看该 snapshot 的挂载信息root@zjz:~# ctr snapshot --snapshotter native mount /tmp zjzmount -t bind /data00/lib/containerd/io.containerd.snapshotter.v1.native/snapshots/20 /tmp -o rbind,rw
复制代码


可以看到 native snapshotter 只是通过简单的 Copy 调用,将父 snapshot 中的内容拷贝到子 snapshot 中。


native snapshotter 对于相同的内容进行了多重保存,还是有些浪费的,那么有没有其他更高效的存储方式呢?答案是肯定的。


_同时也欢迎同学们留言,__可以通过利用哪些技术,能够有效解决镜像层重复占用存储空间的问题。_后续的文章将继续介绍社区是怎么进行高效存储的。


以上内容节选自新书 《containerd 原理剖析与实战》


最后,附上本书的购买链接,新书刚刚上架原价 109,限时优惠内购 69.9 元,感兴趣的朋友可以尽快入手。内购链接点击左下角**“阅读原文**”,或者长按下面的二维码进行购买



本文使用 文章同步助手 同步

发布于: 刚刚阅读数: 4
用户头像

just do it 2018-09-25 加入

赵吉壮,《containerd 原理剖析与实战》作者,曾就职于华为 Cloud BU,字节跳动 Data 团队,专注于 k8s, Serverless, Go 云原生

评论

发布
暂无评论
《containerd 系列》了解 containerd 中的 snapshotter,先从 native 开始_云原生_公众号:云原生Serverless_InfoQ写作社区