# splitter **Repository Path**: openeuler/splitter ## Basic Information - **Project Name**: splitter - **Description**: Splitting openEuler packages into multiple slices for building distroless images. - **Primary Language**: Unknown - **License**: MulanPSL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 6 - **Created**: 2025-03-27 - **Last Updated**: 2025-09-03 ## Categories & Tags **Categories**: Uncategorized **Tags**: sig-CloudNative ## README 中文 | [English](README.en.md) # splitter ## 介绍 splitter用于生成openEuler distroless镜像制作的原材料-slices,通过[EulerPublisher](https://gitee.com/openeuler/eulerpublisher)生成最终distroless镜像并发布。 一般情况下,制作一个应用容器镜像会直接使用`RUN yum install`在镜像中打包所需的应用及其依赖软件包。这种以RPM为最小粒度的镜像打包方式会导致镜像内包含冗余文件,暴露更多攻击点,并且额外增加镜像体积影响传播。 Distroless镜像是一种精简的容器镜像,旨在最小化容器的大小和攻击面。与一般的容器镜像不同,distroless镜像不包含操作系统的许多组件,只包含运行应用程序所需的最小依赖项。 openEuler distroless镜像构建时,首先使用splitter对RPM软件包进行切分处理,每个软件包会被切分成多个slices(每个slice包含一组具有特定功能的文件集合),软件包之间的依赖关系也更精细地表现为slice之间的依赖;然后以slice为最小构建单元生成最终的distroless镜像,可以有效减少冗余文件,进而降低安全风险。 ![img.png](docs/pictures/package.png) 如上图所示,软件包B依赖于软件包A等价于B_slice1和B_slice2依赖于A_slice1、A_slice2,在生成B的应用镜像时,可以不再打包A_slice3所包含的文件。 ## 软件架构 软件架构说明 ## 安装指南 splitter处于开发阶段,当前支持两种安装和运行方式: ### 方法一:在openEuler上部署(推荐使用[install.sh](./install.sh)一键安装) 1. 安装系统依赖 ``` dnf install python3-dnf git python3-pip cpio binutils ``` 2. 克隆源码仓库 ``` git clone https://gitee.com/openeuler/splitter.git cd splitter pip3 install . ``` 3. 验证 ``` splitter --help ``` ### 方法二:使用官方应用容器镜像运行 splitter 提供了官方应用容器镜像,你可以通过 [bin/splitter-docker.sh](./bin/splitter-docker.sh) 脚本直接运行 splitter 命令,无需安装本地依赖。 `bin/splitter-docker.sh` 脚本会自动拉取并使用官方发布的 [openeuler/splitter](https://hub.oepkgs.net/harbor/projects/10/repositories/splitter/artifacts-tab?publicAndNotLogged=yes) 镜像。 **指定 Splitter 版本**:`splitter-docker.sh` 默认使用 splitter 最新的稳定版本。你可以通过设置环境变量 `SPLITTER_VERSION` 来指定特定的版本。例如: ```bash SPLITTER_VERSION=1.0.3 ./bin/splitter-docker.sh cut -r 24.03-LTS -a x86_64 -o ./output python3_standard ``` ## 核心概念 splitter 工具的核心在于 Slice Definition File (SDF),本章节旨在帮助你理解 SDF 及其生成方式。 ### Slice Definition File (SDF) openEuler 的 slice 生成依赖于 **Slice Definition File(SDF)** 定义的软件包切分规则。SDF 文件以 YAML 格式定义了一个RPM包如何被精确地拆解成多个功能独立的、可按需组合的 “Slice”。 以python3.11的SDF(命名为:python3.11.yaml)为例: ``` package: python3 deps: - python3_copyright slices: core: deps: - media-types_data - python3_bins - python3_stdlib standard: deps: - python3_aix-support - python3_all-os - python3_concurrency - python3_core - python3_crypto - python3_custom-interpreters - python3_data-persistence - python3_data-types - python3_debug - python3_development-tools - python3_distribution - python3_extras - python3_file-formats - python3_filesys - python3_frameworks - python3_importing - python3_internet - python3_ipc - python3_language - python3_markup-tools - python3_multimedia - python3_net-data - python3_numeric - python3_osx-support - python3_pydoc - python3_text - python3_unix utils: deps: - python3_debug - python3_pydoc - python3_core contents: common: /usr/bin/pydoc3.11: /usr/bin/pydoc3: /usr/lib64/libpython3.so*: /usr/lib64/libpython3.11.so*: copyright: contents: common: /usr/share/licenses/python3/LICENSE: ``` 上述SDF,即python3.yaml文件中,`slices`指示python3的软件包被切分为:`python3_core`、`python3_standard`、`python3_utils`和`python3_copyright`四个slices,以及这些slice所包含的文件内容(详细信息请查看[slice-releases](https://gitee.com/openeuler/slice-releases))。 ### SDF 的现状 目前,所有官方的SDF文件都由社区专家**手工编写**,并按照以slice所属的openEuler版本为依据分别发布,所有SDFs以yaml文件存在于[slice-releases](https://gitee.com/openeuler/slice-releases)。[slice-releases](https://gitee.com/openeuler/slice-releases)仓库使用分支名来表示不同的openEuler版本。 随着openEuler生态的不断发展,软件包数量和版本持续增多,手工编写和维护SDF逐渐成为一个效率瓶颈。 ### SDF 的自动化生成 为了解决手工编写SDF的效率问题,splitter引入了gen命令。该命令的自动化流水线,旨在模拟专家的分析过程,为任意RPM包生成一个高质量的SDF草稿。 ![sdf-generation-workflow](./docs/pictures/sdf-generation-workflow-cn.png) 其自动化流程分为以下几个阶段: 1. **下载**: 获取目标RPM包。 2. **解压**: 将包内所有文件提取至临时目录。 3. **文件分类**: 将每个文件归类到对应的Slice中。 4. **依赖分析**: 找出各个Slice之间的依赖关系。 5. **写入**: 将分析结果写入格式化的SDF YAML文件。 其中,最核心的步骤是“文件分类”和“依赖分析”。 #### 文件分类 此步骤的目标是精确判断每个文件应归属哪个Slice。 * **基于约定分类**: 依据大量手工SDF的实践,建立了一套基于文件路径的规则引擎(如以`/etc/`开头的文件归入`_config` slice,以`/usr/bin/`或`/usr/sbin/`开头的文件归入`_bins` slice等)。 * **基于类型校验**: 为避免将脚本等非二进制文件错误归类,分类器会调用 `file` 命令进行校验。只有确认为 **ELF** 格式的可执行文件或共享库,才会被分别归入 `_bins` 和 `_libs` slice。 #### Slice 依赖分析 此步骤的目标是自动追溯二进制文件的依赖链,以确定Slice间的依赖关系。 该过程采用“**解析 -> 定位 -> 溯源**”的三步策略: 1. **解析需求**: 使用 `readelf -d` 静态分析每个ELF文件,安全地提取其运行时**需要 (NEEDED)** 的共享库列表(如 `libc.so.6`)。 2. **定位库路径**: 借助 `ldconfig -p` 的系统缓存,将库名快速映射到其在文件系统上的绝对路径。 3. **追溯归属**: 通过 `rpm -qf <路径>` 命令,根据库文件的路径反查出它所属的源RPM包(如 `glibc`)。 **最终形成的依赖链如下:** `brotli_libs` -> 需要 `libc.so.6` -> 定位到 `/usr/lib64/libc.so.6` -> 溯源到 `glibc` 包 -> **因此依赖 `glibc_libs`**。 ## 使用说明 `splitter` 提供了两个核心命令:`cut` 用于根据SDF切分软件包,`gen` 用于自动生成SDF文件。本章节介绍这两个命令的具体使用方法。 ### 生成 Slices (`cut` 命令) splitter使用cut命令行对软件包切分生成所需的slices(可设置SPLITTER_SLICE_REPO环境变量到自定义slice-releases源) 使用本地安装的splitter: ```bash # 示例 splitter cut -r 24.03-LTS -a x86_64 -o /path/to/output python3_standard python3_utils ``` 或者,使用官方容器镜像运行: ```bash # 示例 ./bin/splitter-docker.sh cut -r 24.03-LTS -a x86_64 -o /path/to/output python3_standard python3_utils ``` 上述命令中`-r/--release`指定所需slices所属的openEuler版本,`-a/--arch`指定OS架构,`-o/--output`指定生成slices的输出路径,`python3_standard python3_utils`是用户指定要生成的slices。 最终生成的所有slices打包保存在`/path/to/output`目录中。 ### 自动生成 SDF (gen 命令) splitter 新增了 `gen` 命令,可以根据RPM包的内容自动生成初始的SDF文件,方便开发者快速创建和维护SDF。 使用本地安装的splitter: ```bash # 示例:为 brotli 包生成 SDF 文件 splitter gen -r 24.03-LTS -o /path/to/output -p brotli ``` 或者,使用官方容器镜像运行: ```bash # 示例:为 brotli 包生成 SDF 文件 ./bin/splitter-docker.sh gen -r 24.03-LTS -o /path/to/output -p brotli ``` 此命令将为 `brotli` RPM包生成一个基本的 SDF 文件 `brotli.yaml`,开发者可以根据需要进一步编辑和完善。 ### 构建distroless容器镜像 [EulerPublisher](https://gitee.com/openeuler/eulerpublisher)集成splitter构建并发布最终distroless镜像 ## 参与贡献 欢迎广大开发者参与openEuler distroless镜像生态建设! 1. 请在[slice-releases](https://gitee.com/openeuler/slice-releases)提issue描述对distroless镜像的需求,并与社区讨论、开发相关SDF 2. 关于splitter的需求/bug请提交issue或PR