[ Technical exchange] 玲珑杀手Go - layer子命令:高效构建玲珑应用,大幅降低磁盘压力
Tofloor
poster avatar
System233
deepin
2025-03-11 22:19
Author

玲珑杀手Go - layer子命令:玲珑应用构建革命中的最后一块拼图

前言

在玲珑杀手Go最近更新的v1.4.x版本中,引入了全新的layer子命令,该命令独立于ll-builder之外实现了对玲珑layer文件的构建、打包、挂载/卸载和解析功能,无需生成~/.cache/linglong-builder构建缓存浪费存储空间,也不会产生大量无用的文件复制,大幅增强了玲珑应用的构建体验。

项目主页: https://github.com/System233/ll-killer-go.git

引言

在玲珑应用的打包过程中,传统的 ll-builder 工具因其重复性数据存储、低效的磁盘操作以及对构建环境的不够灵活支持,存在严重弊端。

相较之下,ll-killer 中的 layer 子命令工具以更高效、更轻量、灵活性更强的方式实现了构建与打包流程,尤其在 layer 文件管理上展现出明显优势。

本文将详细介绍 ll-builder 的不足,并着重阐述 ll-killer layer 子命令的核心优势、下载安装步骤以及常用子命令的实际使用案例。

相关内容:关于使用ll-kiler进行应用打包的详细信息,请查看上一篇文章:

玲珑杀手Go:全新玲珑应用本地构建系统,附Ubuntu源GIMP迁移示例-论坛-深度科技

目录


一、ll-builder 的缺点和弊端

ll-builder 在设计上存在严重缺陷,主要包括:

1. 存储浪费严重

每个使用 ll-builder 构建的应用都会在 ~/.cache/linglong-builderlinglong/output 下产生两份副本。即使是玲珑在后续更新中优化了重复的 develop 模块文件,最终仍会有两份重复的 binary 副本。早期玲珑甚至可能产生多达四份副本,虽然玲珑引入的 ostree 仓库可以对部分重复文件做硬链接处理,但依然难以避免同时在~/.cache/linglong-builderlinglong/output 下存在两份重复文件。

玲珑builder的文件存储结构

~/.cache/linglong-builder
├── layers
│   ├── {base}
│   └── {app}       # binary+delelop模块 linglong/output的副本
├── merged
│   └── {app+base}  # 通过ostree硬链接叠加实现的副本
├── repo            # ostree仓库
└── states.json

.                   # 玲珑工程目录
├── linglong
│   ├── entry.sh
│   └── output
│       ├── _build
│       ├── binary  # binary模块 _build的副本
│       └── delelop # delelop模块 _build的副本
├── linglong.yaml

2. 磁盘损耗大

在构建过程中,玲珑工程内的 build构建脚本 会将应用文件写入 $PREFIX,之后 ll-builder 会将其复制到 linglong/output/binarydevelop,再复制到 ~/.cache/linglong-builder。打包过程中,由于 mkfs.erofs 不能很好的支持预留文件头空间,还需再复制一遍临时文件以添加文件头,整个流程至少涉及 5 次文件复制,导致读写放大严重,增加了磁盘负担。

数据复制过程:

  1. build构建脚本->$PREFIX(linglong/output/_build)
  2. linglong/output/{binary,develop}
  3. ~/.cache/linglong-builder/{repo,binary,develop}
  4. tmp.erofs
  5. app.layer
  • 在旧版玲珑(约<=1.6.3)中,步骤2和3的读写量继续翻倍。

3. 默认执行 strip

默认启用 strip-symbols 对二进制文件进行符号剔除,这一行为可能对应用产生不可预知的影响,破坏用户对文件的预期。

4. 多任务并行构建问题

由于 ~/.cache/linglong-builder 中引入了 ostree,仅允许单个任务写入;此外,在新版本(v1.7.x)中,多任务构建可能导致自动重命名缓存目录,进而造成缓存损坏,ll-builder无法自行从错误中恢复。

只要同时跑两个ll-builder就有几率导致cache被重命名而重新构建cache。

5. 构建内容频繁清空

每次构建均会清空之前的构建内容,使得在同一构建环境中进行多次调整输出变得困难,同时伴随大量文件复制操作,让构建过程中本就不堪重负的磁盘雪上加霜。

6. 工具扩展性差

ll-builder 只支持内置的工具链,无法动态安装新工具,需要特制的 runtime,而且baseruntime相关的文档说明不足,导致构建难度大。

7. 构建环境与运行时环境不一致

构建时强制引入开发依赖,且开发依赖会在运行时消失,导致在构建环境中不能可靠地进行依赖检查,在构建环境中测试正常的应用可能在构建后无法运行。

8. 不可配置的压缩算法

玲珑在1.7.7版本中给layer引入了高版本erofs才支持的zstd压缩算法,旧版玲珑无法安装,也无法检测是什么问题并告知用户解决方案,且用户无法手动配置压缩算法,这彻底破坏了layer文件的兼容性。

二、ll-killer 构建环境及其优势

针对 ll-builder 的种种弊端,ll-killer 提出了全新的构建与打包解决方案,其核心思路在于将构建环境与运行时环境完全一致,并消除不必要的文件复制与缓存浪费。主要特点包括:

1. 构建环境一致性

  • 在项目的 linglong/filesystem 内维护应用文件,ll-builder 仅作为 base 下载器。
  • 使用 ll-killer build 命令可反复进入构建环境,且退出后不会重置构建内容。
  • 构建环境仅使用 base 中的 binary 模块,完全避免了开发依赖问题,保证了构建与运行时环境一致性。

2. 打包过程高效

  • ll-killer layer build 命令打包时仅需 2~3 次文件复制:首先运行工程内的buildScript脚本将应用文件复制到 $PREFIX,再利用 mkfs.erofs 命令创建 layer 文件并写入文件头。
  • 通过 mmap+memmove 技术,尽管受 mkfs.erofs 部分 bug 影响,仍然显著提升了在文件头部插入操作的性能。
  • 注:目前第3次复制为mkfs.erofs的bug所致,理想情况下为2次。

复制路径:

  1. build构建脚本->$PREFIX(linglong/output/build)
  2. app.layer
  3. app.layer (插入layer文件头)

3. 磁盘空间与缓存管理优化

  • 打包过程中只会生成一份文件副本,杜绝了 ~./cache/linglong-builder 下大量难以清理的缓存数据。
  • 支持多任务构建,无 ostree 参与,避免了多个项目间的缓存冲突。

4. 速度与灵活性提升

  • 相比 ll-builderll-killer 构建速度更快。
  • 可在宿主机上构建与打包,允许使用宿主机上的工具,提升了整体构建效率与灵活性。
  • 可以自由配置layer文件的各种参数,包括压缩算法。
  • 支持指定根文件系统,可以在任何根文件系统或base中构建。

5. 可靠性加强

  • 没有自定义复制行为,不会出现ll-builder的复制错误 (#1039)
  • 不会擅自修改构建结果中的二进制文件内容(no-strip)

三、ll-killer layer 子命令使用案例

ll-killer 提供了多个子命令来管理 layer 文件,下面分别介绍常用命令及其使用场景。

命令概览

$ ll-killer layer --help
独立于玲珑的layer管理器,提供强大的layer文件处理支持,需要安装erofs-utils。

Usage:
  ll-killer layer [command]

Available Commands:
  pack        将文件夹打包为layer。
  build       无需ll-builder, 直接将当前项目构建为layer。
  mount       挂载layer文件。
  umount      卸载layer挂载点。
  dump        输出layer信息。

Flags:
  -h, --help   help for layer

Use "ll-killer layer [command] --help" for more information about a command.
  • 每一个子命令都有详尽的帮助文档,推荐查看。

1. 获取与配置最新 ll-killer

在使用ll-killer工具之前,需要在主机上获取此工具,如果你已经安装最新版本的ll-killer (版本>=1.4.0) 则可以跳过本步骤。

  1. 安装必要依赖
    确保系统中安装了以下依赖:

    # 用于依赖检查和搜索功能
    sudo apt install apt-file
    
  2. 下载最新版本的 ll-killer
    当前最新版本为 v1.4.5,下载命令如下:

    wget https://github.com/System233/ll-killer-go/releases/latest/download/ll-killer-amd64 -O ll-killer
    chmod +x ll-killer
    

    以上脚本下载的是 amd64架构,ll-killer-go还支持 arm64loong64架构,可以修改链接中的 amd64至其他版本,或进入项目Releases页手动下载。

    如果上面的链接下不动,可以网上搜索github release 下载加速,然后把链接粘进去加速下载,再把文件放入相应位置。

    ⚠警告:从第三方地址加速下载时,注意文件的安全性。Releases页面提供了各个文件的sha1校验码,在文件下载完成后务必使用 sha1sum校验文件的完整性。

  3. 全局安装(可选)
    为了方便全局使用,可以将 ll-killer 安装到 ~/.local/bin

    mkdir -p ~/.local/bin
    mv ll-killer ~/.local/bin
    ll-killer -v
    

    如果提示找不到命令或 ~/.local/bin 未添加至 PATH,请执行:

    echo 'export PATH=$HOME/.local/bin:$PATH' >>~/.bashrc
    source ~/.bashrc
    
  • 后续章节假设ll-killer已全局安装,且ll-killer版本>=1.4.x

2. 高效构建玲珑项目并生成layer

  • 功能说明
    ll-killer layer build子命令在宿主机上启动一个模拟ll-builder的构建环境,直接在宿主机上将当前项目构建为 layer,避免不必要的ostree提交和磁盘复制。

    • 本命令用于取代ll-killer项目中的 ll-builder build+ll-builder export --layer命令组合。
    • 如果是项目不基于ll-killer,但构建脚本不使用任何base特定功能,也可以使用本命令加速打包。
    • 如果你希望在构建脚本中使用宿主机中的工具,可以使用本命令进行构建。
    • 🚀 你可以在命令参数中指定根文件系统,如指定使用玲珑的base所在的路径作为rootfs,如不指定则使用宿主机的根目录。

    此构建模式提供以下内容,以模拟ll-builder环境:

    环境变量

    LINGLONG_APPID="{APPID}"
    PREFIX="/opt/apps/{APPID}/files"
    TRIPLET="x86_64-linux-gnu|aarch64-linux-gnu|loongarch64-linux-gnu|..." 
    KILLER_PACKER=1 ## killer构建环境标识
    

    目录

    • /project: 项目目录
    • /run/host/rootfs: 与宿主机相同
    • /: 与指定的rootfs相同。

    后处理

    • 为快捷方式和服务单元添加ll-cli run前缀
    • KILLER_PACKER 标识当前处于killer环境,killer环境下setup.sh会自动跳过符号链接修复,
      可以在启动前设置KILLER_PACKER=0禁用该行为。
  • 示例

3. 打包文件夹为 layer

  • 功能说明
    将具有 layer 结构的文件夹(参考 linglong/output/binary 目录)打包为 layer。该命令利用 mkfs.erofs 直接对目录进行打包,省去了传统 ll-builder 中将文件提交到 ostree 的步骤,减少了不必要的文件复制和缓存生成,保护磁盘寿命。

    • 上一节中的 ll-killer layer build 子命令会自动调用本功能,本功能也可以单独使用。
  • 示例

4. 挂载和卸载 layer

  • 挂载 layer
    使用该命令可以将 layer 文件挂载到指定目录,方便查看和调试。

    ll-killer layer mount  <挂载目标目录> -- [erofsfuse 选项]
    

    示例

    $ ll-killer layer mount curl_8.11.1.0_x86_64_binary.layer curl
    erofsfuse 1.8.2
     erofs: curl_8.11.1.0_x86_64_binary.layer mounted on curl with offset 868
    
    $ ls curl -l
    总计 20
    -rw-r--r-- 1 system system 291  3月11日 17:36 curl.install
    drwxr-xr-x 3 system system  44  3月11日 17:36 entries
    drwxr-xr-x 4 system system 236  3月11日 17:36 files
    -rwxr-xr-x 1 system system 801  3月11日 17:36 info.json
    -rwxr-xr-x 1 system system 763  3月11日 17:36 linglong.yaml
    
  • 卸载 layer
    当不再需要访问挂载内容时,可使用以下命令卸载:

    ll-killer layer umount <挂载目录> -- [fusermount 选项]
    

    示例

    $ ll-killer layer umount curl
    $ ls curl -l
    总计 0
    

5. 输出 layer 信息

  • 功能说明
    该命令用于输出 layer 文件的详细信息,帮助用户诊断和验证 layer 内容。

  • 使用方法

    ll-killer layer dump  -- [dump.erofs 选项]
    

    常用参数:

    • -x:显示文件头信息;
    • -l:显示 Layer 信息;
    • -e:显示 Erofs 信息;
    • -a:显示全部信息(默认启用)。

    示例:

    $ ll-killer layer dump curl_8.11.1.0_x86_64_binary.layer 
      Layer文件头:
      文件名: curl_8.11.1.0_x86_64_binary.layer
      文件大小: 25444 字节
      魔数: <<
    

四、总结

ll-killer 通过重构构建环境,打破 ll-builder 在文件复制、缓存管理、多任务构建及环境一致性方面的局限,实现了更高效、节省资源的构建与打包流程。

无论是打包layer的高效处理,还是直接在宿主机构建的便捷操作,都使得 ll-killer 成为当前构建领域的强有力工具。用户可以通过简单的下载安装步骤迅速上手,并利用丰富的子命令灵活应对各种场景,从而大大提高开发与发布效率。

对玲珑ll-builder的建议

ll-killer已经通过实践中证明在构建阶段引入ostree是100%的负优化,强烈建议删除构建的阶段的~/.cache/linglong-builder,仅保留baseruntime下载功能,进一步降低对磁盘的压力和损耗。

Reply Favorite View the author
All Replies
System233
deepin
2025-03-11 22:45
#1

保留沙发
o( ̄︶ ̄)o

Reply View the author
MeGusta
deepin
2025-03-12 00:07
#2

A.捉虫:

构建环境一致性
  • ...
  • 使用 ll-killer build 命令可反复进入构建环境,且退出后不会重置构建内容。
  • ...

结合之前的帖子,这篇文章此处的 ll-killer build 也许指的是 ll-killer run

B.体积问题:

使用 ll-killer layer build 来生成layer,速度确实快了很多,但是相应地,体积也增大了。

对于我的 org.qgis.qgis.stable.linyaps_3.42.0.0_x86_64_binary.layer 来说:

ll-killer layer build 体积:1.1GB

ll-killer export --layer 体积:848MB

C.联网问题:

目前我的QGIS程序运行流畅,但是无法联网。

体现为:无法检查更新,无法更新插件(插件存放在用户目录),无法获取在线地图。

不明白是玲珑的限制,还是我用apt少装了什么东西?

截图_选择区域_20250311234541.png

Reply View the author
System233
deepin
2025-03-12 00:58
#3
MeGusta

A.捉虫:

构建环境一致性
  • ...
  • 使用 ll-killer build 命令可反复进入构建环境,且退出后不会重置构建内容。
  • ...

结合之前的帖子,这篇文章此处的 ll-killer build 也许指的是 ll-killer run

B.体积问题:

使用 ll-killer layer build 来生成layer,速度确实快了很多,但是相应地,体积也增大了。

对于我的 org.qgis.qgis.stable.linyaps_3.42.0.0_x86_64_binary.layer 来说:

ll-killer layer build 体积:1.1GB

ll-killer export --layer 体积:848MB

C.联网问题:

目前我的QGIS程序运行流畅,但是无法联网。

体现为:无法检查更新,无法更新插件(插件存放在用户目录),无法获取在线地图。

不明白是玲珑的限制,还是我用apt少装了什么东西?

截图_选择区域_20250311234541.png

ll-killer build进入构建环境,身份为root,环境由ll-killer直接配置。
ll-killer run是ll-builder run的别名,身份为普通用户,内部环境由entrypoint.sh配置。

体积方面,layer build保留完整输出,不会过滤或剔除任何文件,仅给快捷方式和服务单元的Exec添加ll-cli run 前缀。
此外压缩算法默认为lz4,新版玲珑用了zstd,压缩率可能高点。
可以使用-z zstd参数也指定使用zstd压缩算法对比一下。

网络问题与/etc/resolv.conf相关,我看到你的包里覆盖了这个文件,指定了DNS为10.0.0.1。
可以直接删除包里的这个文件。
后续更新处理下这个问题

发了v1.4.4版,在构建过程中删除了etc下的这几个文件,把这些文件交给玲珑处理
这个版本更新了脚本文件,可以使用ll-killer build-aux --force生成并覆盖已有脚本

Reply View the author
神末shenmo
deepin
Spark-App
2025-03-12 03:44
#4
System233

ll-killer build进入构建环境,身份为root,环境由ll-killer直接配置。
ll-killer run是ll-builder run的别名,身份为普通用户,内部环境由entrypoint.sh配置。

体积方面,layer build保留完整输出,不会过滤或剔除任何文件,仅给快捷方式和服务单元的Exec添加ll-cli run 前缀。
此外压缩算法默认为lz4,新版玲珑用了zstd,压缩率可能高点。
可以使用-z zstd参数也指定使用zstd压缩算法对比一下。

网络问题与/etc/resolv.conf相关,我看到你的包里覆盖了这个文件,指定了DNS为10.0.0.1。
可以直接删除包里的这个文件。
后续更新处理下这个问题

发了v1.4.4版,在构建过程中删除了etc下的这几个文件,把这些文件交给玲珑处理
这个版本更新了脚本文件,可以使用ll-killer build-aux --force生成并覆盖已有脚本

可能和strip有关,清理符号可以降低很多的空间占用

调试符号对普通用户几乎没有任何作用,在debian的默认打包策略中也是专门进行了符号剔除,应该就差这点了

除此之外还有开发依赖的保留也可能造成空间占用大幅提高,不过我不太清楚玲珑的组织形式,并不清楚保留开发包是否非常必要,不评价了

Reply View the author
神末shenmo
deepin
Spark-App
2025-03-12 04:51
#5

简单了解了一下ll-killer,不知道我说的对不对

ll-killer的方案是直接在deepin23的base上加源装别的发行版的包,然后保留现场,反正玲珑内部只读,以后再也不会容器内更新了,能跑!

然后把一整堆塞进玲珑layer里,又因为overlayfs的特性只用保留diff

下次重新跑构建重新diff,空间占用仍然不大

如果是这样的话——太棒了!

维护者省心省力,用户端没有明显区别,快速扩充生态

Reply View the author
神末shenmo
deepin
Spark-App
2025-03-12 05:03
#6
It has been deleted!
神末shenmo
deepin
Spark-App
2025-03-12 05:21
#7
It has been deleted!
MeGusta
deepin
2025-03-12 09:46
#8
System233

ll-killer build进入构建环境,身份为root,环境由ll-killer直接配置。
ll-killer run是ll-builder run的别名,身份为普通用户,内部环境由entrypoint.sh配置。

体积方面,layer build保留完整输出,不会过滤或剔除任何文件,仅给快捷方式和服务单元的Exec添加ll-cli run 前缀。
此外压缩算法默认为lz4,新版玲珑用了zstd,压缩率可能高点。
可以使用-z zstd参数也指定使用zstd压缩算法对比一下。

网络问题与/etc/resolv.conf相关,我看到你的包里覆盖了这个文件,指定了DNS为10.0.0.1。
可以直接删除包里的这个文件。
后续更新处理下这个问题

发了v1.4.4版,在构建过程中删除了etc下的这几个文件,把这些文件交给玲珑处理
这个版本更新了脚本文件,可以使用ll-killer build-aux --force生成并覆盖已有脚本

用你的1.4.4重新构建了一下,QGIS可以联网啦,辛苦大佬熬夜开发!

kissing_heart

Reply View the author
System233
deepin
2025-03-12 15:41
#9
神末shenmo

可能和strip有关,清理符号可以降低很多的空间占用

调试符号对普通用户几乎没有任何作用,在debian的默认打包策略中也是专门进行了符号剔除,应该就差这点了

除此之外还有开发依赖的保留也可能造成空间占用大幅提高,不过我不太清楚玲珑的组织形式,并不清楚保留开发包是否非常必要,不评价了

killer里面一般是直接安装deb,deb在打包时已经由维护者处理过了,尽量不二次修改它。

另外我觉得打包器层面不能大范围全都strip了,什么能strip什么不能应该交给开发者精细控制,或者需要明确显式启用。

Reply View the author
System233
deepin
2025-03-12 15:54
#10
神末shenmo

简单了解了一下ll-killer,不知道我说的对不对

ll-killer的方案是直接在deepin23的base上加源装别的发行版的包,然后保留现场,反正玲珑内部只读,以后再也不会容器内更新了,能跑!

然后把一整堆塞进玲珑layer里,又因为overlayfs的特性只用保留diff

下次重新跑构建重新diff,空间占用仍然不大

如果是这样的话——太棒了!

维护者省心省力,用户端没有明显区别,快速扩充生态

确实是这样的,不过ll-killer还做了兜底方案实现在没有fuse的情况下合并rootfs,在1.2之前都默认用的合并方案,1.2之后就支持默认用fuse了

开发阶段是必须要overlayfs做diff,内核overlay模块虽然能直接用但是不支持lower中的挂载点,因此还是用的fuse。

另外ll-killer exec本质是构造一个容器,因此可以用这个命令来拿玲珑的base来构造一个环境,由于是直接拿的宿主机上的base文件夹,里面没有挂载点,所以可以直接使用内核overlay模块进行挂载和构建。
唯一的问题就是新版本玲珑的容器ID全都是不可读的hex,不好找base,还没看这个ID是怎么生成的

Reply View the author
System233
deepin
2025-03-12 15:56
#11
MeGusta

用你的1.4.4重新构建了一下,QGIS可以联网啦,辛苦大佬熬夜开发!

kissing_heart

这个网络的问题最初还是我用ll-killer exec直接在宿主机上启动玲珑base发现的,里面也有/etc/resolv.conf影响了域名解析,后面我看玲珑OCI配置里也挂载了这几个文件。

like

Reply View the author
神末shenmo
deepin
Spark-App
2025-03-12 20:48
#12
System233

确实是这样的,不过ll-killer还做了兜底方案实现在没有fuse的情况下合并rootfs,在1.2之前都默认用的合并方案,1.2之后就支持默认用fuse了

开发阶段是必须要overlayfs做diff,内核overlay模块虽然能直接用但是不支持lower中的挂载点,因此还是用的fuse。

另外ll-killer exec本质是构造一个容器,因此可以用这个命令来拿玲珑的base来构造一个环境,由于是直接拿的宿主机上的base文件夹,里面没有挂载点,所以可以直接使用内核overlay模块进行挂载和构建。
唯一的问题就是新版本玲珑的容器ID全都是不可读的hex,不好找base,还没看这个ID是怎么生成的

我很喜欢这个构建容器的点子
 
我大概可以用ll-killer 替换掉 bwrap ?
 
 
Reply View the author
神末shenmo
deepin
Spark-App
2025-03-12 20:51
#13
System233

确实是这样的,不过ll-killer还做了兜底方案实现在没有fuse的情况下合并rootfs,在1.2之前都默认用的合并方案,1.2之后就支持默认用fuse了

开发阶段是必须要overlayfs做diff,内核overlay模块虽然能直接用但是不支持lower中的挂载点,因此还是用的fuse。

另外ll-killer exec本质是构造一个容器,因此可以用这个命令来拿玲珑的base来构造一个环境,由于是直接拿的宿主机上的base文件夹,里面没有挂载点,所以可以直接使用内核overlay模块进行挂载和构建。
唯一的问题就是新版本玲珑的容器ID全都是不可读的hex,不好找base,还没看这个ID是怎么生成的

base不好找那就自己下一个?反正构建时候用


@mozixun 我就说玲珑里面套容器是可行的吧joy

Reply View the author
System233
deepin
2025-03-12 21:00
#14
神末shenmo
我很喜欢这个构建容器的点子
 
我大概可以用ll-killer 替换掉 bwrap ?
 
 

可以,ll-killer exec--socket选项还让容器支持了重入和进程管理,以支持应用的多次启动。

这个功能需要确保机器已启用非特权命名空间kernel.unprivileged_userns_clone

Reply View the author
New Thread

Popular Events

More
国际排名
WHLUG