3 个不稳定版本

0.3.1 2024年8月22日
0.3.0 2024年7月18日
0.2.12 2023年11月28日

#371开发工具

Download history 10/week @ 2024-05-23 7/week @ 2024-05-30 96/week @ 2024-07-18 22/week @ 2024-07-25 4/week @ 2024-08-01

122 每月下载次数

GPL-3.0-or-later

115KB
2K SLoC

rpmoci

rpmoci 从 RPM 软件包构建 OCI 容器镜像,使用 DNF。它本质上是在 dnf install --installroot=/some/rootfs PACKAGE [PACKAGE ...] 的基础上进行容器化的包装。

rpmoci 功能

  • 确定的 rpmoci 使用类似 bundler/cargo 等的包文件/锁定文件范式锁定 RPM 依赖关系,可以生成具有相同摘要的可重复镜像。
  • 非特权 rpmoci 可以在没有访问容器运行时和在不需要 root 权限的环境中构建镜像(这依赖于用户能够创建 用户命名空间
  • 小型 rpmoci 镜像仅从您请求的 RPM 和其依赖项构建,因此不包含不必要的依赖项。

rpmoci 的设计受到了 apkodistroless 工具的影响。

安装

rpmoci 依赖于 dnf 的运行时,因此需要具有 dnf 支持的 Linux 发行版。

rpmoci 可从 crates.io 下载,因此您需要一个 Rust 工具链。您还需要安装 sqlite、python3 和 openssl 开发包(例如,在 Fedora 和 RHEL 衍生产品上安装 sqlite-develpython3-developenssl-devel)。

然后通过 cargo 安装 rpmoci

cargo install rpmoci

构建

根据上述内容,您需要安装 dnf、Rust、python3-devel 和 openssl-devel。

cargo build

无根设置

当 rpmoci 以非 root 用户运行时,它将自动尝试设置一个用户命名空间来在其中运行。rpmoci 将用户的 uid/gid 映射到用户命名空间中的 root。

它还尝试将当前用户的subuid/subgid范围映射到用户命名空间,这对于rpmoci从包含非root用户所有文件的RPM中创建容器是必需的。

rpmoci要求至少分配999个子uid/subgid给您的用户。您可以在https://rootlesscontaine.rs/getting-started/common/subuid/中创建它们。

入门指南

您需要创建一个rpmoci.toml文件。例如:

[contents] # specifies the RPMs that comprise the image
repositories = [ "mariner-official-base" ]
packages = [
  "tini"
]

[image] # specifies image configuration such as entrypoint, ports, cmd, etc.
entrypoint = [ "tini", "--" ]

这配置了rpmoci从mariner-official-base存储库安装tini及其依赖项,并配置了镜像入口点使用tini

然后可以将其构建成镜像

sudo rpmoci build --image tini --tag my-first-rpmoci-image

镜像将创建在名为tini的OCI布局目录中。rpmoci不处理镜像分发 - 预期用户将使用orasskopeo之类的工具将镜像推送到注册表。

将创建一个锁文件rpmoci.lock,以便您可以稍后重新运行构建并获取相同的软件包。前提是它们仍然存在于指定的存储库中... rpmoci支持供应商RPM,因此您可以在不依赖于它们的情况下重复锁定构建

参考

软件包规范

存储库配置

存储库部分定义了RPM的来源。

在入门指南示例中,存储库是通过运行系统上的repo ID指定的。如果您想创建一个可移植的rpmoci.toml(例如,在Fedora/Ubuntu/Mariner上运行时构建相同的镜像),也可以在rpmoci.toml中完全指定存储库。

存储库可以通过其基本URL指定

[contents]
repositories = ["https://packages.microsoft.com/cbl-mariner/2.0/prod/base/x86_64"]

或通过包清单文件中的附加配置选项定义(默认为rpmoci.toml,在CLI上可以通过-f FILE指定)

[[contents.repositories]]
url = "https://packages.microsoft.com/cbl-mariner/2.0/prod/base/x86_64/"
options = { includepkgs = "foo,bar" }

默认情况下,gpgchecksslverify被启用 - 可以通过options字段禁用。

除了通过repo ID显式指定的存储库之外,忽略所有系统存储库。支持dnf插件,但rpmoci不支持指定插件配置。

软件包配置

软件包规范添加到contents.packages键下。支持本地和远程软件包

[contents]
repositories = ["https://packages.microsoft.com/cbl-mariner/2.0/prod/base/x86_64"]
packages = [
  "postgreqsl", # a package from the above repository
  "path/to/local.rpm", # a local RPM
]

文档文件

是否将文档文件包含在生成的容器中可以通过content.docs布尔字段指定。默认情况下不包含文档文件,以优化镜像大小。

GPG密钥配置

GPG密钥可以通过存储库选项或gpgkeys字段配置

[contents]
repositories = ["https://packages.microsoft.com/cbl-mariner/2.0/prod/base/x86_64"]
gpgkeys = [
  "https://raw.githubusercontent.com/microsoft/CBL-Mariner/2.0/SPECS/mariner-repos/MICROSOFT-RPM-GPG-KEY"
]
packages = [
  "postgresql"
]

在构建镜像时,将使用配置的GPG密钥验证软件包签名,除非是本地软件包或存储库中的软件包,其中gpgcheck已显式禁用。

认证RPM存储库

要使用需要HTTP基本身份验证的仓库,在toml文件中指定仓库的id,并定义环境变量RPMOCI_<id>_HTTP_USERNAMERPMOCI_<id>_HTTP_PASSWORD作为HTTP认证凭据,其中<id>是仓库id的大写。

例如,使用以下配置,您需要定义环境变量RPMOCI_FOO_HTTP_USERNAMERPMOCI_FOO_HTTP_PASSWORD

[[contents.repositories]]
url = "https://packages.microsoft.com/cbl-mariner/2.0/prod/base/x86_64/"
id = "foo"

镜像配置

可以在image键下指定额外的镜像配置

[contents]
repositories = ["https://packages.microsoft.com/cbl-mariner/2.0/prod/base/x86_64"]
gpgkeys = [
  "https://raw.githubusercontent.com/microsoft/CBL-Mariner/2.0/SPECS/mariner-repos/MICROSOFT-RPM-GPG-KEY"
]
packages = [
  "postgresql"
]
[image]
entrypoint = ["tini", "--"]
cmd = [ "foo" ]
exposed_ports = ["8080/tcp"]

[image.envs]
RUST_BACKTRACE = "1"
RUST_LOG = "hyper=info"

上述OCI镜像规范的config部分映射到rpmoci.toml中的镜像部分。例如,要指定镜像标签,可以使用image.labels部分,要指定镜像环境变量,使用image.envs

默认情况下,PATH环境变量设置为/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin,但可以通过image.envs字段进行覆盖。

/etc/os-release

可以通过content.os_release布尔字段指定/etc/os-release是否在解析过程中自动包含为依赖项,从而安装到生成的镜像中。这使SBOM和漏洞扫描工具能够更好地确定镜像中软件包的来源。默认情况下,此字段是启用的。

您还可以通过将发行版的<distro>-release包添加到包数组中包括/etc/os-release文件:此字段存在是为了确保默认包括/etc/os-release文件。

弱依赖

rpmoci不会安装弱依赖,优化小型容器镜像的大小。

镜像构建

运行rpmoci build --image foo --tag bar将构建一个OCI格式的容器镜像。

$ rpmoci build --image foo --tag bar
...
$ cat foo/index.json | jq
{
  "schemaVersion": 2,
  "manifests": [
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "digest": "sha256:1ad8cc1866d359e4e2ecb37fcc96759815540f06cb468811dcb9b8aac51da90d",
      "size": 350,
      "annotations": {
        "org.opencontainers.image.ref.name": "bar"
      }
    }
  ]
}

然后可以使用OCI工具(如skopeo或oras)复制此镜像。例如,将其复制到本地Docker守护进程

$ skopeo copy oci:foo:bar docker-daemon:foo:bar
Getting image source signatures
Copying blob 77b582c1f09c done
Copying config 577bea913f done
Writing manifest to image destination
Storing signatures

锁文件

rpmoci使用DNF生成构建的锁文件。这可以用作随后通过rpmoci build --locked重复构建。

可以通过运行rpmoci update来创建或更新锁文件。

$ rpmoci update
Adding filesystem 1.1-10.cm2
Adding grep 3.7-2.cm2
Adding openssl 1.1.1k-17.cm2
Adding libgcc 11.2.0-2.cm2
Adding postgresql 14.2-2.cm2
Adding libxml2 2.9.14-1.cm2
Adding ncurses-libs 6.3-1.cm2
Adding pcre 8.45-2.cm2
Adding pcre-libs 8.45-2.cm2
Adding glibc 2.35-2.cm2
Adding bash 5.1.8-1.cm2
Adding libsepol 3.2-2.cm2
Adding libcap 2.60-1.cm2
Adding krb5 1.19.3-1.cm2
Adding openldap 2.4.57-7.cm2
Adding coreutils 8.32-3.cm2
Adding postgresql-libs 14.2-2.cm2
Adding libselinux 3.2-1.cm2
Adding openssl-libs 1.1.1k-17.cm2
Adding readline 8.1-1.cm2
Adding tzdata 2022a-1.cm2
Adding xz-libs 5.2.5-1.cm2
Adding libstdc++ 11.2.0-2.cm2
Adding zlib 1.2.12-1.cm2
Adding e2fsprogs-libs 1.46.5-1.cm2
Adding gmp 6.2.1-2.cm2
Adding bzip2-libs 1.0.8-1.cm2

可重复构建

假设RPMs可以可重复安装(如果涉及具有不可重复安装后脚本的RPMs,rpmoci构建将不会是可重复的),rpmoci可以生成位可重复的容器镜像构建。rpmoci试图从容器镜像中移除非确定性的来源,并尊重SOURCE_DATE_EPOCH环境变量。

当未设置SOURCE_DATE_EPOCH时,OCI镜像配置中的镜像创建时间设置为当前时间。在这种情况下,rpmoci仍然会从镜像中删除非确定性数据,并且可以通过设置SOURCE_DATE_EPOCH为镜像的创建时间(通过将镜像配置中的时间戳转换为自Unix纪元以来的秒数)来在以后重现构建。

此功能仅在Mariner Linux上进行了测试,但当rpmoci在将rpmdb作为sqlite数据库写入/var/lib/rpm/rpmdb.sqlite的任何Linux发行版上运行时,应能正常工作。

供应商

可以使用rpmoci vendor将RPMs供应商到文件夹中。可以在构建期间使用供应商文件夹以避免接触软件包仓库。

$ rpmoci vendor --out-dir vendor
$ ls vendor
ls vendor
031e779a7ce198662c5b266d7b0dfc9eece9c0c888a657b6a9bb7731df0096d0.rpm  8ea3d75dbb48fa12eacf732af89a600bd97709b55f88d98fe129c13ab254de95.rpm
...
$ rpmoci build --image foo --tag bar --vendor-dir vendor

由于rpmoci当前尝试从供应商目录安装所有RPMs,因此来自不同调用rpmoci vendor的供应商目录应保持隔离。

SBOM支持

rpmoci没有原生SBOM支持,但由于它仅使用标准OS包功能,因此可以使用trivy和syft等SBOM生成器生成生成图像的SBOM。

开发

rpmoci是用Rust编写的,目前通过嵌入的Python模块使用DNF解析RPMs。

它依赖于python3-developenssl-devel

检出项目后,您可以执行以下操作

cargo run

来运行它,或使用cargo-generate-rpm构建RPM

cargo generate-rpm

测试

测试是通过cargo test运行的。在tests/it.rs中的集成测试运行rpmoci build,因此必须以root权限运行,或者已设置用户命名空间支持。

测试使用test-temp-dir,因此可以将TEST_TEMP_RETAIN环境变量设置为1,以便在<CARGO_TARGET_DIR>/tests中进行调试时保留测试目录。

依赖关系

~57MB
~1M SLoC