/layer_sword

A tools used to split docker image save tar file into small parts containing costumed layers or merge these parts into origin image tar file.

Primary LanguageRust

项目功能说明书

[TOC]

介绍

背景介绍

在使用docker等工具时,用户一般从镜像仓库运行docker pull,从镜像仓库拉取镜像到本地,再创建容器使用。但是,在某些特殊场景下,如镜像过大,网络连接不佳,或者处于无网络环境时,我们不会使用镜像仓库,而是通过docker save+docker load(或者isula-build save+isula-build load)的方式导入镜像。这两条命令中,前者会将本机上已存在的镜像打包为归档文件tar,后者会以有效的归档文件tar作为输入,将镜像导入本机。

需要注意到,很多镜像都存在相同的基础组件,因此也会共享数个相同的层。这是因为在image制作时,它们都由相同的image加工而来。

在功能上,我们一般可以将image中的层进行简单的分类:提供操作系统的os(如ubuntuopeneuler),所依赖的运行组件lib(如PythonJDK),以及实现具体功能的app,一般情况下,这三类将会由底层到上层依次排布,且较上的类必须依赖下层的类。

因此,在这种场景下,我们可以使用一种办法来减少镜像体积并分类归档:我们可以将每个image都分为数个分割子集,oslibapp或更多,然后将其分别压缩并归档。这样的话,重复的分割子集将会只保存一次,能够大大节省储存空间。

产出描述

基于以上目标,我们构建了layer_sword,用于解决这个问题。

这是一款镜像分割归档工具,用户可以将有效docker等镜像归档文件tar作为输入,并分割成多个压缩子集tar.gz

除此之外,layer_sword也支持将分割后的子集合并为等效归档文件tar,用于导入有效镜像。

软件特色

  • 任意分割:用户可以将归档文件分割为2、3、4......层,任意合理的分割方式软件都可以处理
  • 配置文件:用户可以通过命令行参数或配置文件两种方式来指定分割方式
  • 自动推导:所有分割层数中,允许最多一个-1项,软件会自动计算实际分割层数
  • 自检查:分割后的子集,只需要放在同一个目录下,程序就可以自动完成检查及合并,无需额外的配置文件

安装教程

安装说明

  1. 从源码编译时,可以先clone项目到本地,移动到项目文件夹下,运行Cargo run --bin layer_sword --release构建项目,可执行文件是自动生成的release文件夹下的layer_sword.exe(Windows)或layer_sword(Linux)
  2. 也可以使用随项目提供的可执行文件layer_sword.exe(Windows)或layer_sword(Linux)直接运行

使用流程

  1. 准备好本机上的镜像,或者使用docker pull [mirror name]或类似命令拉取镜像
  2. 使用docker save -o [mirror.tar] [mirror name]或类似命令将指定镜像保存成tar归档文件
  3. 利用layer_sword split命令对tar归档文件进行分割,并对获得的压缩子集分别进行对应归档
  4. 利用layer_sword merge命令对tar.gz分割子集进行合并,获得等效原始tar归档文件
  5. 使用docker load -i [mirror.tar]或类似命令将tar归档文件进行导入

使用教程

命令介绍

split子命令

参数 简称 取值 描述 强制
--config -c <FILE> 从用户指定的配置文件获得分割信息 和[name && layers]二选一
--names -n <STR, STR...> 指定分割各子集名称 和[config]二选一
--layers -l <INT, INT...> 指定分割各子集含有层数量 和[config]二选一
--target -t <FILE> 指定镜像归档文件路径
--output -o <DIRECTORY> 指定的子集输出路径 否,默认值./out
--work -w <DIRECTORY> 指定的工作临时文件夹 否,默认值./tmp
--level -v 0-9, none, fast, best 指定分割子集压缩等级,越大压缩率越高 否,默认值6
--quiet -q 启用时,程序静默运行,不输出信息

merge子命令

参数 简称 取值 描述 强制
--target -t <DIRECTORY> 指定分割子集所在文件夹路径
--output -o <DIRECTORY> 指定的子集输出路径 否,默认值./out
--work -w <DIRECTORY> 指定的工作临时文件夹 否,默认值./tmp
--quiet -q 启用时,程序静默运行,不输出信息

配置文件

配置文件为json格式,需求nameslayers两个数组条目,与split子命令中的同名参数等效。

典型的配置文件内容如下:

{
    "names": [
        "os", 
        "lib", 
        "app"
    ], 
    "layers": [
        1, 
        -1, 
        1
    ]
}

情景示例

layer_sword split -n os,lib,app -l 1,3,1 -t base.tar

base.tar镜像归档文件自底向上分为oslibapp三个压缩子集,分别含有1层、3层、1层layer。临时工作目录为当前目录下的tmp文件夹(默认),输出文件在当前目录下的out文件夹(默认)。

layer_sword split -n os,lib -l 1,-1 -t base.tar -s -w work

base.tar镜像归档文件自底向上分为oslib两个压缩子集,前者含有1层layer,后者含有剩余所有层layer。除此之外,运行过程中不输出提示信息。临时工作目录为当前目录下的work文件夹(用户指定),输出文件在当前目录下的out文件夹(默认)。

layer_sword split -c config.json -t base.tar -o splits

base.tar镜像归档文件根据config.json配置文件中的信息分割为压缩子集。临时工作目录为当前目录下的tmp文件夹(默认),输出文件在当前目录下的splits文件夹(用户指定)。

layer_sword merge -t splits

splits文件夹下所有的分割子集合并为等效镜像归档文件。临时工作目录为当前目录下的tmp文件夹(默认),输出文件在当前目录下的splits文件夹(用户指定)。

技术细节

排序方案

layer_sword进行分割时,将用以下方案进行自排序:

每个分割子集中的split_config.json文件会记录当前序列号index,越低为越底层的子集,越高为越上层的子集,其中:

$index\in[0, len(splits)-1]$

验证方案

layer_sword进行分割时,将用以下方案进行自验证:

  1. 每个分割子集tar.gz文件的备注中,会记录内部tar文件的sha256

  2. 每个分割子集中的split_config.json文件会记录父级id和层叠id

    父级id计算方式如下

    $parrent_id=sha256(parrent_layer_file)$

    层叠id计算方式如下

    $stack_id(0)= sha256(""+"\n"+"")$

    $stack_id(i) = sha256(stack_id(i-1)+"\n"+sha256(parrent_layer_file))$

分割子集合并时,会验证以上所有id,以确认子集不存在错误

一致性方案

  1. tar压缩方案中,压缩文件内部文件元数据(如时间)将会影响压缩文件哈希,为了消除这种影响,执行压缩时将会忽略所有文件元数据。
  2. tar压缩方案中,压缩文件内部文件顺序将会影响压缩文件哈希。相应的,由于不同平台中默认文件读取顺序不一致,程序将在读取所有文件后,进行排序再压缩。

拓展方案

关于项目中分割验证方案和检查方案,我们提供了抽象接口用于未来可能的拓展,具体方案如下:

分割合并中的验证方案由dominator文件夹中的控制器文件BaseDominator提供,如果需要构建新的Xdominator,用户可以在其中创建新的控制器文件,在其中:

  1. 定义配置类Xconfig,对其impl Config trait并重写所有方法
  2. 定义控制类XDominator,对其impl Split trait并重写pack_tar_with_config方法用于配置文件的生成和打包tar,再impl Merge trait并重写check_with_config方法用于配置文件的验证和init_config方法用于配置类对象Xconfig的初始化

镜像文件的检查方案由inspector文件夹中的检查器BaseInspector提供,如果需要构建新的XInspector,用户可以在其中创建新的检查器文件,在其中:

  1. 定义检查类XInspector,对其impl Inspect trait并重写所有局部检查方法inspect_routeinspect_configinspect_layerinspect_manifest,用于实现各个检查过程

完成新的拓展类构建后,在client.rs中的pick_dominator_and_inspector函数里,将新构建的拓展类用Box指针作为返回值,并调整返回不同控制器和检查器的逻辑。

备注

功能完成度

功能 项目要求 完成程度
支持docker
支持isulad
支持isula-build
完整性校验
有效输入校验
原生rust
配置文件启动
自校验和自排序
自动推导
任意分割

软件架构

layer_sword软件中,根目录下src目录为源码集合,tests目录为测试项目集合,Cargo.toml和Cargo.lock(自动生成)为Cargo项目构建描述文件。

源码描述

文件名 描述
main.rs rust主程序入口,用于提供用户项相关功能
lib.rs rust库入口,用于供单元测试项调用相关功能
client.rs 命令行组件,用于解析命令和发起功能调用
split.rs 完成分割操作的相关函数
merge.rs 完成合并操作的相关函数
inspector.rs 完成镜像完整性检查的相关函数
util.rs 工具类函数
errors.rs 自定义错误类型集合

测试描述

文件名 测试名 描述
test_util.rs test_string_sha256 测试字符串哈希函数
[单元测试,测试工具函数输出] test_file_sha256 测试文件哈希函数
test_stack_id 测试层叠哈希函数
test_flow.rs test_init_path 测试工作目录路径初始化
[集成测试,测试工作流] test_inspect 测试镜像文件完整性检查
test_split_layer 测试分割功能
test_deduction 测试自动推导分割层数
test_split_four_layer 测试分割为4层
test_split_two_layer 测试分割为2层
test_merge 测试合并功能
test_compress_best 测试压缩到best级别
test_cmd.rs test_split_basic 测试基本压缩命令
[集成测试,测试命令行控制] test_split_negatives 测试带自动推导的压缩命令
test_split_config 测试用配置文件的压缩命令
test_merge_basic 测试基本合并命令
test_err.rs test_blank 测试空命令错误
[集成测试,测试错误处理] test_split_conflict 测试冲突命令错误
test_split_no_info 测试无分割信息错误
test_split_no_target 测试无分割目标错误
test_merge_no_target 测试无合并目标错误
test_split_bad_extension 测试分割目标错误后缀
test_split_bad_info 测试分割信息错误

覆盖率测试

82%(利用tarpaulin检测)

INFO cargo_tarpaulin::report: Coverage Results:
|| Uncovered Lines:
|| src/client.rs: 21-22, 25, 41-45, 47, 57-58, 62-63, 67-68, 76, 79, 81, 93-94, 98, 101, 107, 111, 115-118, 133-134, 141-142, 149-150, 165-167, 182-184, 188-190, 324-326, 343-344, 352, 369-372, 398-399
|| src/dominator/base.rs: 34-35, 37-38, 44, 51-52, 82-84, 87-89
|| src/errors.rs: 77-80, 82-85, 107, 122-124, 144, 159-160, 162-163, 187-188, 190-191
|| src/inspector/base.rs: 30, 34-36, 53-55, 68, 70, 72, 87-88, 91, 96-98, 106-108, 116-117, 123-125, 138-140, 156-157, 159-161, 177-180, 185-186, 193-194, 197-198, 204, 207-208, 219-220, 227-229, 234-236, 243-245, 252-254, 259-261, 268-270, 276, 278, 282-285
|| src/inspector.rs: 44
|| src/main.rs: 15-19
|| src/merge.rs: 23-25, 50, 56-58, 77, 94, 123-125, 136, 141, 162, 165
|| src/split.rs: 30-32, 37-39, 50, 56-59, 89, 111, 146-147, 183, 197, 201, 205, 208-209
|| src/util.rs: 50, 62, 171-172, 204-205, 212-215, 251-252, 254
|| src/validator.rs: 25, 52
|| tests/common.rs: 9
|| tests/test_err.rs: 22, 48, 73, 98, 122, 148, 175
|| Tested/Total Lines:
|| src/client.rs: 153/208
|| src/dominator/base.rs: 58/71
|| src/errors.rs: 10/31
|| src/inspector/base.rs: 115/186
|| src/inspector.rs: 11/12
|| src/main.rs: 0/5
|| src/merge.rs: 102/118
|| src/split.rs: 111/132
|| src/util.rs: 117/130
|| src/validator.rs: 12/14
|| tests/common.rs: 9/10
|| tests/test_cmd.rs: 64/64
|| tests/test_err.rs: 82/89
|| tests/test_flow.rs: 207/207
|| tests/test_util.rs: 7/7
||
82.40% coverage, 1058/1284 lines covered

压力测试

我们选取了来自英伟达的镜像nvcr.io/nvidia/tensorrt:21.02-py3,这个镜像是人工智能模型推理加速工具tensorRT的官方镜像,有5.7GB的大小和43的层数,非常适合测试layer_sword对于超大镜像的处理性能。测试运行时,采用了最常见的oslibapp分别为1,-1,1层的分割方案。

split测试

压缩等级 别名 耗时 处理速度
0 none 3m4.5s 31.64MB/s
1 fast 2m31.8s 38.45MB/s
6 default 6m32.2s 14.88MB/s
9 best 9m27.9s 10.27MB/s

merge测试

其中大小split分割后所有分割子集的文件大小之和

来源 大小 压缩率 耗时 处理速度
none 5.8G 101.8% 1m47.8s 55.09MB/s
fast 3.3G 57.9% 2m28.6s 22.74MB/s
default 3.0G 52.6% 2m23.9 21.35MB/s
best 3.0G 52.6% 2m24.8 21.22MB/s