面向有效层共享的保证可交换性的Docker镜像重构
目录
ABSTRACT
docker images构成:应用程序 + 依赖文件,支持灵活部署和迁移(advantage)。
- 问题:
1.容器的快速发展导致上百万的容器镜像被生成及存储;
2.非本地的镜像需要从registry中拉取,导致大量网络流量。
- 解决:
CAS(Content Addressable Storage)内容可寻址存储。 通过允许不同的镜像之间共享相同的层来节省网络和存储开销。但CAS对于Docker镜像并不是百分百完美,因为在实践中,不同镜像之间很少存在完全相同的层。
- 提出
本文企图通过重构Docker镜像来增加完全相同层的数量从而节省存储和网络开销。
1.定义了“文件可交换性”来保证交换后的image仍然有效;
2.提出这个重构过程是一个整数非线性规划问题;
3.受到层相似性的启发,设计一个相似度感知的在线镜像重构算法.
1 INTRODUCTION
container与虚拟机相比:
1. 更加轻量级。通过共享操作系统内核来产生更小开销;
2. 使开发人员可以更多的关注应用逻辑,而无需担心底层基础设施。
- 问题:存储和网络开销
1.镜像数量飞速增长。如Docker Hub如今(截至2022年)存储了2百万共有镜像 + 400百万私有镜像,占据近1PB存储空间;
2.互联网移动设备的普遍使用加剧了服务的移动性和动态性。如Google平均每秒启动7000个容器来支持不断变化的请求,这种频繁的业务部署和迁移也会给存储和网络带来巨大压力。
- 应运而生:CAS
CAS(内容可寻址存储)通过在同一客户端上存储的不同镜像之间共享相同的层来缓解网络和存储开销。但据统计,CAS只节省了38%的存储开销,仍有35%是冗余的。主要原因:很少存在完全相同的层(1%)但存在很多相似的层。因此层共享技术仍然有待开发。
- 设想
在registry中,基于层共享在文件粒度上删除重复文件,只保留独有文件。存在问题:Docker镜像中的“recipe”文件记录了每一层的文件,在客户端请求时会根据recipe来构建镜像并传送给client,因此这种方式只节省了registry的空间,并不能节省网络开销和客户端的存储空间。
很少存在完全相同的层但存在很多相似的层:如上图所示,两个版本的镜像的layer1完全相同可以共享,实际上layer2和layer3仍然有很多文件是相同(复制)的,极少数是特殊独有的。统计了2200个镜像间的相似度发现:26042对层之间有近80%的相似度(80%的文件相同)!此外,同一repository中镜像相似度 > 不同repository。
registry与repository:
· registry就是像Docker Hub这样存放大量镜像的地方;
· repository就像是集装箱,将这些镜像进一步分类,拥有同一类型但tag版本不同的镜像就会被存放在同一repository中。
- 本文提出
企图通过重组各层文件来最大化相同层的数量,因此需要考虑如下问题:
(1) 重构后的镜像有多少层?
(2) 如何判断一个文件应该属于哪一层?
(3) 各层的顺序是什么?
同时,首先应该保证重构后的镜像是有效的(Docker的union mount机制将各层文件挂载到同一文件系统中,且只提供一个文件系统视图给容器,因此改变文件所属层的位置会影响最后的合并结果,这就容易导致重构后的镜像无效),因此,决定是否可以改变文件所属层的位置
是至关重要的(这在之前的研究中未得到解决)。
其次,重构镜像应权衡存储开销和操作开销
。一个直观的想法是一层一个文件,显然不切实际(一个image有成千上万个文件,20%的image有超过一万个文件),一层一文件会导致层深过深(本文研究表明,层深与操作延迟呈正相关,因此层深过深不利于容器操作)以及源数据过大。
- 解决方案
为了解决如上问题
(1) 首先弄清楚了image的层结构,提出了可交换性
来保证文件的正确顺序以及镜像的有效性;
(2) 通过数据拟合方法提出了一个数学表达式揭示了层深与操作延时的关系
;
(3) 将镜像重构操作建模为整数非线性规划(INLP)
问题,旨在降低存储开销和操作开销;
(4) 为减小镜像庞大的元数据,提出一种相似性感知的在线镜像重构算法(SOIRA)
,来解决这个INLP问题。
SOIRA重新排列已提交的层来使其与目标层相同,前文提到同一repository中的镜像间相似度更高,因此SOIRA从repository中选取目标而非从整个registry,以此大大降低了算法执行时间。
本文选取了一系列Docker镜像进行测试,其中包含1413629个唯一文件,近60GB。与目前(2022年)最先进的技术相比,SOIRA只需要多几层就可以节省近10%的存储空间;另外,20个客户端每个拉取20个镜像,可以减少近9.3%的流量。综上表明,Docker镜像重构在存储空间和网络流量的节省上拥有巨大发展空间。
- 主要贡献
(1) 通过评估CAS有效性和文件冗余性,明确了镜像层共享的优化空间,是第一个量化层间相似性的;
(2) 首个定义可交换性来保证重构后镜像的有效性;
(3) 将重构过程建模为INLP(整数非线性规划问题)。通过研究层间相似性,提出了SOIRA算法降低镜像庞大的元数据;
(4) 对Docker Hub中的镜像进行了广泛评估,验证分析了具有1413629唯一文件以验证模型和算法的有效性。
2 BACKGROUND
2.1 Docker Overview
- Repository and registry
Repository被视为是同一类型镜像不同版本的集合。例如,“java:7u121jdk-alpine”和“java:8u111-jdk-alpine”都属于repository“java”。Registry 是一个用于存储和交付Docker镜像的系统,它存储了Repository中镜像的层以及元数据(eg. manifest.json)。
- Image delivery
镜像交付常用命令:pull和push。Docker在每个host上面运行了一个本地守护进程(local daemon),当遇到“pull”命令时,Docker local daemon就从registry中拉取镜像的manifest.json(记录了配置信息configuration和层信息list of layers),各层由digest(采用SHA-256算法对内容进行哈希结果命名给层名称)标识及引用,Docker daemon正是通过检查层的digest来判断本地是否有相同的层,然后只下载未存储在本地的层。同时,CAS允许在同一host上的不同images之间共享相同的层。
“push”触发daemon上传最新的镜像和manifest到registry中。
- Image building
有两种方式创建并发布image到registry中:(1) dockerfile;(2) Docker commit。
(1)dockerfile是通过文件命令(类似shell脚本)方式创建docker镜像,其中每个命令创建一层;
(2) Docker commit是将正在运行的容器连同其环境打包成镜像的命令。
相同的库和包可以被不同的镜像引用,但由于开发人员独立的创建和发布他们的镜像,这些镜像可能会调用具有不同方法和指令的库,这就会导致产生高度相似的层。
2.2 Docker Storage Driver
Docker image的层结构就是由Docker存储驱动创建,最常用的是overlay,基于了overlayFS联合文件系统(overlayFS在linux3.18之后也被写入了linux内核)。如下图所示overlayFS的挂载机制:
注意图2(b),不管这两个idconf文件中的内容是否相同,只要他们路径和文件名相同都会上层覆盖下层。
overlay还有另外一个技术:Copy on Write(COW,写时复制)。如下图所示:
镜像层只读,容器层可读可写,所有的操作都发生在容器层而不会改变镜像层中文件的内容。
3 IMAGE ANALYSIS
settings:服务端配置:8核cpu,2.5GHz,11G主存RAM,从Docker Hub中拉取了130个常用repository中最近更新的2200个镜像文件。
- CAS 评估
为测试CAS的有效性,对比了允许使用CAS和不允许使用CAS的镜像层的数量以及所占存储对比,自变量为repository的数量,结果如下图所示:
由图可见,支持CAS永远比不支持CAS的层数少,所占存储少,且随着repository数量增加,两者之间差值逐渐增大。经测量,CAS可节省近38.8%的存储开销。
- 镜像文件冗余情况
计算了每个repository的特有层与特有文件的文件数量与所占存储大小,结果如下图所示:
在此次实验中所有的唯一层中,共有8305,000个文件,然而其中只有3721,000个文件是特有的,因此说明,有近55.2%的文件在这些唯一层中是冗余的,且独有文件所占存储空间比独有层减少了近35%。综上,共享相同层仅能实现部分的文件复用,并不能消除所有的文件冗余。
本文发现,大多数与基本操作相关的包和库形成了一个固定的层,作为image的基础。例如ubuntu是“ubuntuupstart”, “php-zendserver”的基础镜像;同为web框架的“iojs” 和 “node”共享了最底部相同的五层。其他与功能密切相关的库和包,由于开发人员对镜像的构建没有统一的规范,因此可能属于不同的层,这或许是导致镜像文件冗余的主要原因。
- 镜像层的相似性
将大部分文件相同少部分文件不同的两个层视为相似层。通过两个指标来在文件粒度上证明层的相似性:
(1) Jaccard index。这是衡量两个集合相似度的常用指标,范围[0,1],值越大代表相似度越高,在本测量层间相似度实验中,另Jaccard index = 相同的文件数量 / 两层的总文件数量
。
(2) Deduplication ratio。这是从存储角度衡量数据冗余度的常用指标,= 层数据的总的问储量 / 删除重复数据后的实际存储量
,取值[1,2],比例越大冗余越多,即越相似。
第一个实验的测量结果如下图所示(有多少层对是相似的?):
CDF:累积分布函数
对于所有实数x,累积分布函数定义如下:F(x) = P(X ≤ x)
即累积分布函数表示:对离散变量x而言,所有小于等于x的值出现的概率和。
由图可见,所有层对中(all layers,蓝色线)有近70%的Jaccard系数为0或接近0,说明这些层间有少量或没有相同文件;
跨repository的层对主要分布在0左右,同时可以看出all layers与inter-repository的层对Jaccard系数差距不大;同一repository的层对,大约10%的Jaccard系数值大于0.5,意味着层间至少有一半以上的文件是相同的。
综上,通过对Jaccard系数的测量可以发现,同一repository中层对比不同repository的更相似
。
固定一层,查看该层与同repository及不同repository中的层间有多少相似度,计算Jaccard系数和deduplication比率的最大值、平均值。实验结果如下:
首先可以发现,该固定层与同一repository的约28%的层的Jaccard指数超过0.8,即层间约有80%的相同文件;而在跨repository情况下,该比例下降到了21%左右;
同一repository的层对比中,约有30%的重复数据删除比率高于1.78;而这种情况在跨repository中30%的层对只超过1.4(Deduplication ratio越大越相似)。
总之,无论是最大值还是平均值,无论是Jaccard系数还是重复数据删除比,同repository的曲线均在跨repository曲线之上,均证实了“同一repository中层对比不同repository的更相似”这一结论。
Jaccard指数差 = 同repository的最大值(平均值) - 跨repository的最大值(平均值),结果如下图所示:可以发现,在最大Jaccard指数差的曲线中,有近68%是非负的,表明:68%的层可以在其存储库中找到最相似的层。
重复数据删除比率差 = 同repository的最大值(平均值) - 跨repository的最大值(平均值),结果如下图所示:可以发现,在最大重复数据删除比的差值中,有近63%是非负的,这说明同一repository的层间文件冗余情况较跨repository更严重。
综上实验表明了:同一repository中的层更相似,并且具有更多的冗余存储。
4 SYSTEM MODEL
这一章节主要对镜像重构问题进行建模,总目标:降低操作成本以及存储成本。定义:
- I:所有镜像的集合;
- i:某一个镜像i;
- 相同的文件:内容相同(由哈希值判断)且路径相同的文件;
- K:镜像的特有文件集合;
- k:某一文件k;
- J:所有镜像的特有层集合;
- j:某一特有层j;
- y(k,j):特有文件与特有层间的联系;
- M:某个镜像i的所有文件的集合;
- N:某个镜像i的层数;
- ξ(k,n):文件k与重构后的镜像的层的关系。若ξ(k,n)=1,表示文件k在重构后的镜像的第n层;
- L:操作时延Latency;
- D:镜像层深Depth;
- μ(·):操作时延L与层深D的关系;
- C[i,o]:镜像i的总操作成本;
- Sk:文件k的大小;
4.1 Operation Cost
创建了30层(使用Overlay),且每层包含1000个空文件,随机选择20个文件使用cold cache打开并测量其时延。实验结果(平均运行超过500次)如下图所示:
A cold cache refers to a cache that has just been initialized and contains little to no data.
如图所示,操作时延随着层深的增大而增大。定义:
- L:操作时延Latency;
- D:镜像层深Depth;
- μ(·):操作时延L与层深D的关系。
因此得到:L = μ(D) = a · D + b
,其中a = 0.001526, b = 0.03087。定义:某一镜像i的总操作成本 = 该镜像每一层操作时延的总和。记镜像i的层数为N,定义:
- C[i,o]:镜像i的总操作成本,因此等于:
4.2 Storage Cost
将存储成本定义为:由镜像i的特有文件所引起的增量存储。
如果镜像i的第n层与第j层(j∈J,所有特有层的集合)相同,代表其中所包含的文件完全相同,即:
如果在registry中存在与第n层完全相同的层,则:
否则,如果第n层是独有的(即在registry中不存在与其完全相同的层),则:即文件k在第n层但不在第j层 或 文件k在第j层但不在第n层。
定义:
- Sk:文件k的大小;
- 第n层的大小Σ(k∈M)(ξk,n ·Sk ):即镜像i的所有文件M中挑,ξk,n=1代表文件k在第n层,=0表示不在,以此来计算第n层的大小
因此镜像i的新的特有层的增量存储计算公式如下:
4.3 Commutativity Model
由于Docker的分层机制和COW,文件存在一定的顺序。定义:
- order(顺序):层差(layer diffreence);
- θ(i, k):镜像重构之前,文件k在镜像i中的第几层?θ(i, k)=1表示文件k在是最低的一层或者是第一层;θ(i, k)=0表示文件k不在镜像i中;θ(i, k)=n表示文件k在重构之前镜像i的第n层。
- δ[i, (k,k′)]:文件k和文件k′在重构之前镜像i中的顺序,因此δ[i, (k,k′)]可以表示成如下:
其中,sgn(·)的定义如下:因此δ[i, (k,k′)]只有三个值来表示两个文件的顺序。
注意:某些文件的顺序是不能被改变的,将下述文件限制定义为可交换性commutativity:
(1) 如下图所示:如果同一路径的上层文件A是下层文件A的复制或修改,则重构时必须保证它们的顺序不变;
(2) 如果文件B是文件A的依赖文件,则B所在的层级必须低于A。
定义:
- λ[i, (k,k′)]:布尔类型,文件k与k’在镜像i中是否是可交换的。λ[i, (k,k′)]=0,表示没有如上限制,k与k’可在任意相同或不同的层;λ[i, (k,k′)]=1,表示k与k’的 顺序(order) 不可以被改变;
易知,若文件处在同一层必为可交换的(λ[i, (k,k′)]=0),即δ[i, (k,k′)]=0 ==> λ[i, (k,k′)]=0
。- δ∗[i, (k,k′)]:重构后文件k与k’在镜像i中的顺序;
为保证重构后镜像的有效性,那些可交换的(commutative)δ∗[i, (k,k′)]任意,而那些不可交换(noncommutative)的文件必须保证在重构前后的顺序一致即δ[i, (k,k′)] = δ∗[i, (k,k′)]
,因此:
4.4 Problem Formulation
将镜像重构表述为加权成本最小化问题,即如下三个公式:
此公式要求镜像i中的文件在重构前后保持一致;
此公式保证了文件的顺序(实际是将公式9带入公式10)。
此公式描述加权成本最小化,其中 Ξ = {ξ1,1, ξ2,1, ..., ξM, N }
。
5 IMAGE RECONSTRUCTION ALGORITHM
上一章节定义的P是一个非线性整数规划问题。定义:
- T(n, j):第n层与现有第j层(j∈J,某一镜像所有特有层集合)的相似度,使用Jaccard指数表示;
按照相似度T(n, j)从高到低的顺序,将文件移入或移除第n层,使其与第j层相同。由于同repository具有更高相似度,因此只考虑同repository中的其他层,而不考虑整个registry中的层。具体算法如下:当迭代时间超过阈值或层相似性降低到阈值以下时,重构过程将终止。
Algorithm 1: SOIRA
--------------------------------------------------------------------
Input: file information of image i0: θ[i0,k], δ[i0,(k,k′)], λ[i0,(k,k′)]
Output: N , Ξ
--------------------------------------------------------------------
1 Initialize N and Ξ0 based on the current image layers and files; Set the iteration counter t as 0 ;
2 Compute the current weighted cost denoted as cost0 ;
3 Compute the layer difference Tn,j for each layer n ;
4 Choose layers (n∗,j∗) = arg max(T(n,j)),n∈M,j ∈J ;
5 Calculate the cost after reconstruction denoted as costRe ;
6 if cost0 < costRe then
7 Keep the image layer unchanged, set Dn,j = −∞ and goto line 4 ;
8 else
9 Make layer reconstruction, set cost0 = costRe and goto line 3 ;
10 end
11 t ← t + 1 ;
12 if (t > tthr eshold ) or ( max n ∈M, j ∈JTn, j ≤ Tthr eshold ) then
13 Return N, Ξ
14 end
6 EVALUATION
6.1 Setup
使用Docker Hub中的一部分镜像进行评估。对于每个镜像进行处理,获得文件集、大小、位置θ(i,k)和文件顺序δ[i, (k,k’)],通过指纹计算(Message-Digest Algorithm)来识别该层的每个文件。由于目前没有成熟的方法从有限的信息中准确判断文件的可交换性,假定所有文件都是不可交换的(λ[i, (k,k′)]=1)。将本文方法与以下方案进行了比较:
- Greedy Offline Image Reconstruction Algorithm (GOIRA)贪婪离线镜像重构算法:对于每个文件,它遍历所有需要它的镜像,并选择镜像中现有的层或创建一个新的层加入;
- Layered Images from Docker Hub (LIDH):来自Docker Hub的分层镜像,镜像的层次结构有开发人员独立决定(一般用来作为基准);
- One-File-Per-Layer (OFPL):每个文件被存储为一层,然后组成整个镜像。
6.2 Results
6.2.1 Storage and operation overhead
下图显示了累计存储与镜像数量的关系:
如图所示,以当前的分层方案(LIDH)为基准,GOIRA节省了1.3%的存储开销。GOIRA的效果是有限的,它是根据存储和操作成本来决定,且后续镜像的决策很大程度上受到前一决策影响,GOIRA最大的特点是:局部最优而不一定全局最优,即当前的决策是基于之前所作出决策最优的决策,但从整体来看不一定是最优的。
此时本文所提出的SOIRA就充当一个检查器,探索曾间的相似性,并调整已提交的层,使其与已经存在在registry中的层完全一样。当镜像数量为150是,SOIRA节省了近10%的存储开销。
同时可发现OFPL节省存储的效果最为明显,SOIRA与其有很大差距,但这几乎是不可能消除的,OFPL牺牲了操作开销来换取存储开销,如下图所示,之前所证明了操作开销与层深呈正比例关系:
由图可见,GOIRA所节省的微小存储是以增加层数为代价的(当镜像数量为150时,相较于LIDH增加了近7.3%),而SOIRA只比LIDH多几层。
下图显示了在不同α和β的比重下,镜像特有层的累计存储与镜像数量的关系:其中α表示操作成本所占比;β表示存储成本所占比,α:β分别从10:1变化到1:10。
由图16可知,随着α/β比值增大,SOIRA倾向于进行镜像重构来降低存储消耗(比值越大,所占存储越小);当镜像数量为150,比值为1:6时,SOIRA可节省6.6%左右的存储开销;当比值增大至1:2时,可以节省近8.3%的存储开销。
图17展示了α/β比值与总层数的关系。当镜像数量固定时,随着比值增大层数增多;当镜像数量为150,比值为1:4时,LIDH有559层,而SOIRA增多了5.3%左右;当比值为1:4时仅仅增多了几层。
6.2.2 Network traffic and client storage
只有本地没有存储的层才会被传输,因此镜像拉去产生的网络流量与客户端镜像存储的增量相同。设置了10个客户端随机拉取镜像,网络流量变化如下图所示:
由图可知,随着镜像数量的增大,SOIRA的网络流量节省率越来越高,当镜象数量达到50时,SOIRA相较于GOIRA多节省了8.3%,较LIDH多节省了8.8%的网络流量;
令每个客户端拉取20个镜像,然后0-20地变化客户端的数量,结果如下图所示:
当客户端数量达到20时可以节省近9.3%地网络流量。
综上,随着客户端数量和拉取镜像数量的增加,SOIRA的节省效果较LIDH越来越好,由此显示了此镜像重构策略的重大意义。
6.2.3 Storage and operation overhead trade-off
在建模中,使用α和β来权衡操作成本与存储成本。现固定镜像集,不同指标下GOIRA与SOIRA的加权成本如下图:
由图可见,操作成本与存储成本呈反比(镜像层越少,每层文件越多,存在更少的相同层,存储成本越高,操作成本越低)。
LIDH的存储空间为6.4381×1010字节,其中特有层的数量为559。当α:β=1:1时,SOIRA的存储空间占用为5.8772×1010,较LIDH节省了近9%的存储空间。
并且,层数相同时SOIRA曲线总在GOIRA曲线下方,说明存储成本更低。
6.2.4 Image reconstruction time
测试集为1250个镜像文件,记录了重构时间如下图:
图21为随着镜像数量的增加,平均每个镜像文件的重构时间。当数量为100时,t=0.012s;当增加至1250时,t=0.047s;这表明虽随着镜像文件数量增加,重构时间变长,但在可容忍范围内。
图22为重构时间的累计函数概率分布,当为1250时,80%的镜像重构时间小于0.05s。