共计 3920 个字符,预计需要花费 10 分钟才能阅读完成。
一、背景
咱们晓得,容器运行起来的工夫是十分快的,然而如果节点上容器的镜像不存在,那么在运行容器时要先拉取镜像,拉取镜像在容器启动的过程中占用的工夫比拟长,这个过程要将容器所有的镜像层都拉取到本地磁盘中。据统计,拉镜像操作要占用容器启动工夫的 76%。这在容器数量少的状况下问题不大,但容器数量比拟多并且都是冷启动的时候会十分的慢。
如何解决容器冷启动过程中拉取镜像慢这个问题?有这样的一种解决思路:在容器启动过程中,容器要用的镜像通过高速网络按需从镜像仓库中读取,而不是将镜像所有的层都拉下来。Stargz-snapshotter 是 containerd 上面的一个子项目,以 Proxy Plugin 的形式扩大 containerd 的性能,是 Containerd 的一个 remote-snapshotter 实现。
版本变迁
二、应用
Stargz-snapshotter 在 kuberentes 中应用比较简单:应用 Containerd 作为 kuberentes 的 CRI 运行时。在本地起一个 stargz-snapshotter 的服务,作为 containerd 的一个 remote snapshotter。
镜像转换
在应用前须要将咱们的一般的镜像转换成 stargz-snapshotter 能够辨认的镜像,应用 ctr-remote 工具进行转换,上面示例是将本地一个 centos 镜像进行转换,转换实现后推送到镜像仓库中:
$ ctr-remote image optimize –plain-http –entrypoint='[“sleep”]’ –args='[“3000”]’ centos:7 centos:7-eg 复制代码
比照
应用 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} 复制代码
三、原理
上图是 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 的逻辑就是为以后层筹备文件系统并挂载到本地的一个过程。
⑤ 所有镜像层解析实现后会保留镜像的元数据
四、小结
创立容器时,拉取镜像过程在容器启动工夫的占比高,通常咱们会应用多种办法去制作尽量小一点的镜像,或者通过 P2P 网络去散发镜像。镜像 lazy pull 是另一种进步镜像散发速度的形式。
应用 stargz-snapshotter 在镜像拉取时,仅将镜像的 manifest 和 config 下载下来,并镜像每一层通过近程挂载的形式挂到以后主机上,容器运行时达到按需读取文件的成果。而传统形式是将镜像的每一层都下载到本地进行解压。相比而言前者能放慢镜像的拉取速度,放慢容器冷启动的速度。但须要留神,文件是按需加载的,它依赖于一个比拟好的网络环境。