Containerd镜像lazy-pulling解读

今天zouyee朋友段全锋童鞋为你们带来《Containerd镜像lazy-pulling解读》,其中《kuberneter调度由浅入深:框架》正在编写中,敬请期待。git

1、背景github

咱们知道,容器运行起来的时间是很是快的,可是若是节点上容器的镜像不存在,那么在运行容器时要先拉取镜像,拉取镜像在容器启动的过程当中占用的时间比较长,这个过程要将容器全部的镜像层都拉取到本地磁盘中。据统计,拉镜像操做要占用容器启动时间的76%。这在容器数量少的状况下问题不大,但容器数量比较多而且都是冷启动的时候会很是的慢。golang

如何解决容器冷启动过程当中拉取镜像慢这个问题?有这样的一种解决思路:在容器启动过程当中,容器要用的镜像经过高速网络按需从镜像仓库中读取,而不是将镜像全部的层都拉下来。Stargz-snapshotter是containerd下面的一个子项目,以Proxy Plugin的方式扩展containerd的功能,是Containerd的一个remote-snapshotter实现。docker

版本变迁centos

2、使用api

Stargz-snapshotter在kuberentes中使用比较简单:使用Containerd做为kuberentes的CRI运行时。在本地起一个stargz-snapshotter的服务,做为containerd的一个remote snapshotter。缓存

镜像转换markdown

在使用前须要将咱们的普通的镜像转换成stargz-snapshotter能够识别的镜像,使用ctr-remote工具进行转换,下面示例是将本地一个centos镜像进行转换,转换完成后推送到镜像仓库中:网络

$ ctr-remote image optimize --plain-http --entrypoint='[ "sleep" ]' --args='[ "3000" ]' centos:7 centos:7-eg
复制代码

对比session

使用crictl工具在本地拉取转换前和转换后的镜像,作一下对比,经过lazy的方式拉取镜像的速度更快:

# 正常拉取镜像
$ time crictl pull centos:7
Image is up to date for sha256:ef8f4eaacef5da519df36f0462e280c77f8125870dbb77e85c898c87fdbbea27real  
0m5.967suser  0m0.009ssys  0m0.012s
# 拉取优化后的镜像
$ time crictl pull centos:7-eg
Image is up to date for sha256:36edf0c0bb4daca572ba284057960f1441b14e0d21d5e497cb47646a22f653d6real  
0m0.624suser  0m0.012ssys  0m0.010s
复制代码

查看镜像层

使用crictl建立一个pod,进入容器中,查看/.stargz-snapshotter/目录下各个镜像层在本地缓存的状况:

$ cat /.stargz-snapshotter/*{"digest":"sha256:857949cb596c96cc9e91156bf7587a105a2e1bc1e6db1b9507596a24a80f351a","size":80005845,"fetchedSize":3055845,"fetchedPercent":3.819527185794988}{"digest":"sha256:8c57b1a6bef1480562bc27d145f6d371955e1f1901ebdea590f38bfedd6e17d0","size":33614550,"fetchedSize":64550,"fetchedPercent":0.19202993941611593}
复制代码

3、原理

上图是stargz-snapshotter的实现概览,一般的咱们在拉取镜像时,要将镜像的每一层拉取下来,而使用stargz-snapshotter后containerd再也不是拉取镜像的层,而是为存储在镜像仓库中镜像的每一层在容器运行节点上建立一个目录,经过远程挂载的方式挂到各个目录上。容器启动前再将各个目录作overlay挂载,为容器提供一个rootfs。当须要读取某个文件时,经过网络读取镜像仓库中镜像层中的文件。

下面再看一下镜像层是怎么远程挂载和如何从镜像层中按需读取文件的。

用户态文件系统

Stargz-snapshotter使用FUSE实现了用户态的文件系统。FUSE(Filesystem in userspace)框架是一个内核模块,可以让用户在用户空间实现文件系统而且挂载到某个目录,就像在内核实现文件系统同样。

如上图所示,stargz-snapshotter是一个实现了用户态文件系统的程序(golang语言,使用go-fuse做为实现的依赖)。当有拉取镜像的操做发生时,stargz-snapshotter会为镜像的每一层在${stargz-root}/snapshotter/snapshots/下建立一个目录,执行实现一个文件系统的逻辑,并将这个文件系统挂载到刚建立的目录上,例如图中的/dcos/snapshotter/snapshots/1。当有用户读取目录下的文件时,请求的流向是这样的:

① 操做请求经VFS到FUSE

② FUSE内核模块根据请求类型,调用stargz-snapshotter的逻辑,stargz-snapshotter从镜像仓库中读取该层中的文件

③ Stargz-snapshotter将文件的内容经过VFS返回给系统调用

(e)stargz格式

a. stargz格式

一般存放在镜像仓库中的镜像层都是使用gzip压缩过的,咱们不能从这个压缩后的文件中提取单个文件。那stargz-snapshotter是怎么作到从单个镜像层中读取单个文件的呢?

Stargz-snapshotter使用了另外一种压缩镜像层的格式,它也是gzip包,一种可seekable的gzip包,图3是targz和stargz的对比。压缩包里的文件能够被检索和抽取,但还是zip格式的文件;镜像层中的每一个文件都会被打成一个zip包,最后再组成一个大的zip包;整个zip包中有一个TOC文件,它记录了包中每一个文件的偏移量;Footer占最后47个字节,记录了TOC在整个zip包中的偏移量。

这样就能够经过镜像层最后47个字节的Footer,找到TOC的偏移量,而后读取TOC的内容就能获得整个镜像层中有哪些文件,每一个文件的偏移量是多少。Stargz-snapshotter就是经过这个TOC文件去按需检索整个镜像层中文件的。

b. estartgz格式

默认状况下,将镜像的某一层远程挂载到目标主机后,stargz-snapshotter默认会建立一个后台任务去缓存镜像层。在容器启动过程当中,若是容器启动须要的文件没有在本地缓存那么stargz-snapshotter就须要经过网络去镜像仓库中读取,这会致使容器启动速度比较慢。

estargz是对stargz格式进行了优化,如上图所示。它多了一个landmark文件,这个文件将镜像层中的文件分红了两类:一类是容器运行时最有可能用到的,另外一类是可能用不到的。这样后台任务会优先去缓存那些容器运行时须要的文件,这样会增长本地缓存的命中率,加快容器的启动速度。

下图是三种镜像层的对比状况,legacy是普通镜像层,stargz是stargz格式的镜像层,estargz是优化后stargz格式的镜像层。

c. 分层拉取镜像

镜像层使用estargz格式能够作到从压缩包中检索文件,那stargz是如何从镜像仓库中按照分片获取文件所有或者部分数据的?

在OCI规范中有关于如何从仓库中获取部分数据的描述,而docker registry也有对应接口实现。

Registry中获取镜像层部署数据的接口以下:

其中,name就是目标repository的名称,digest就是镜像层blob的digest的值,Host就是镜像仓库的地址,Range描述的就是要获取的blob分片。

返回的响应以下:

其中,start是开始的字节,end是结束的字节,size是层大小,length是本次请求的层分片。

lazy-pulling流程

Containerd使用stargz-snapshotter拉取镜像的流程以下:

① 根据镜像名称和tag解析出镜像manifest的digest的值

② 根据镜像manifest的digest的值,从镜像仓库中下载manifest,保存在content store中

③ 根据manifest的内容获取镜像config的digest的值,从镜像仓库中下载config,保存在content store中

④ 解析镜像的每一层,建立snapshot,若是containerd使用的stargz-snapshotter,它会返回一个snapshot已经存在的错误。Stargz-snapshotter PrepareSnapshot的逻辑就是为当前层准备文件系统并挂载到本地的一个过程。

⑤ 全部镜像层解析完成后会保存镜像的元数据

4、小结

建立容器时,拉取镜像过程在容器启动时间的占比高,一般咱们会使用多种方法去制做尽可能小一点的镜像,或者经过P2P网络去分发镜像。镜像lazy pull是另外一种提升镜像分发速度的方式。

使用stargz-snapshotter在镜像拉取时,仅将镜像的manifest和config下载下来,并镜像每一层经过远程挂载的方式挂到当前主机上,容器运行时达到按需读取文件的效果。而传统方式是将镜像的每一层都下载到本地进行解压。相比而言前者能加快镜像的拉取速度,加快容器冷启动的速度。但须要注意,文件是按需加载的,它依赖于一个比较好的网络环境。

后续相关内容,请查看公众号:

参考资料

1. www.usenix.org/conference/…

2. github.com/google/crfs

3. github.com/opencontain…

4. docs.docker.com/registry/sp…

相关文章
相关标签/搜索