- 云原生
【免费下载链接】buildah
A tool that facilitates building OCI images.
本指南面向希望验证 Buildah 构建结果与 Docker 构建结果一致性的开发者。它完整讲解 tests/conformance/README.md 中定义的 Conformance 测试套件的原理、依赖安装、运行方法与扩展方式,并结合 tests/conformance/conformance_test.go 源码剖析其双层对比、报告生成与字段豁免机制。读完本文,你将掌握如何在本仓库中运行全套一致性测试、按用例名筛选测试、以逐行粒度对比构建层,并理解测试用例的组织结构与调试手段。
什么是 Buildah/Docker Conformance 测试套件
Conformance 测试套件用于验证「使用 Buildah 构建出的镜像,与使用 Docker 构建出的镜像等价」。其核心思路是:用被测试的 buildah 库构建一个镜像,再用 Docker 引擎的 build API 构建"同一个"镜像,最后对两者进行对比。这里的"同一个"指两者使用相同的 Dockerfile、相同的构建上下文与相同的构建参数,从而把差异缩小到纯粹由构建器实现差异所导致的部分。
该测试基于
go.podman.io/buildah、imagebuildah等库以 Go 测试(go test)形式运行,而不是作为 buildah CLI 的子命令存在。入口函数为 tests/conformance/conformance_test.go 中的TestMain与TestConformance。
测试不仅对比 Docker 与 Buildah,还可以在开启-compare-imagebuilder时额外引入 openshift/imagebuilder 作为第二参照系,形成三方交叉验证。每个测试用例通过testCase结构体描述(见 conformance_test.go 中的结构体定义),字段包括:
dockerfile/contextDir:构建所用的 Dockerfile 名称或上下文子目录;shouldFailAt:期望构建在第几行失败(从 1 计数,0 表示应成功);buildahRegex/dockerRegex/imagebuilderRegex:校验构建输出中应出现的正则片段;withoutDocker/withoutImagebuilder:针对依赖 Buildah 专有特性的用例,跳过某一参照方;dockerUseBuildKit/dockerBuilderVersion:要求 Docker 使用 BuildKit 或经典 V1 构建器;fsSkip/failOnExtraFSContent:声明允许的文件系统差异,或要求 Buildah 不得产生 Docker 没有的文件系统条目;transientMounts/buildArgs:向 Buildah 传入临时挂载或--build-arg类参数。
安装依赖
运行 Conformance 测试所需的额外依赖只有一个:docker。其余依赖(buildah 库本身、go-dockerclient、containers/storage 等)均已在仓库的 go.mod 中声明。
安装 Docker CE
Conformance 测试使用 Docker CE 构建用于对比的镜像,因此需要先安装 Docker CE。可依据发行版选用dnf、yum或apt-get安装,并确保docker服务已启动:
- Fedora、RHEL 与 CentOS 默认安装的可能是
docker或moby-engine软件包,而非 Docker CE; - Debian 或 Ubuntu 上则可能已有
docker.io软件包; - 无论采用哪种来源,请确保安装的版本不低于 19.03(对应 Docker API 版本需满足测试中对 builder 版本的协商要求,见下文源码分析)。
源码侧对 Docker 版本的要求在 conformance_test.go 中有更具体的体现:测试初始化时用mobyclient.New(mobyclient.FromEnv)连接 dockerd 并执行Ping以协商 API 版本,若用例要求 BuildKit(dockerUseBuildKit或dockerBuilderVersion非空),协商出的 API 版本低于 1.38 会直接t.Skipf跳过该用例;随后创建固定 API 版本为 1.51 的docker.NewVersionedClientFromEnv("1.51")客户端用于构建与清理镜像。
运行 Conformance 测试
1. 预拉取基础镜像
测试用例会引用多组基础镜像,建议在运行前手动拉取,避免测试中途因网络或镜像缺失失败:
docker pull mirror.gcr.io/golang docker pull mirror.gcr.io/alpine docker pull mirror.gcr.io/busybox docker pull quay.io/fedora/python-311:latest docker pull quay.io/libpod/centos:7 docker pull quay.io/libpod/ubuntu:latest docker pull quay.io/libpod/busybox@sha256:32968e717e29f79e5b889721908b3be6d1573992e1f3ba4c9a41718e2728458c docker pull quay.io/libpod/busybox@sha256:1faaf7a75319417261267b3dcef6a2b14c8c5122d7fcc7abeb07a32bc19728fd除手动拉取外,测试源码中还有自动兜底逻辑:buildUsingDocker会在解析 Dockerfile 阶段逐行识别# syntax=指令并调用pullImageIfMissing拉取对应前端镜像,同时遍历每个 stage 的FROM基础镜像并确保其已存在(见 conformance_test.go 的 buildUsingDocker 实现)。
2. 准备测试程序输入
mount-targets用例需要编译一个辅助测试程序,作为少数用例的输入:
make tests/conformance/testdata/mount-targets/true对应的 Go 源文件位于 tests/conformance/testdata/mount-targets/true.go,Dockerfile 位于 tests/conformance/testdata/mount-targets/Dockerfile。
3. 运行全部测试
使用go test运行整套测试(需要 root 权限;非 root 用户请先执行buildah unshare或podman unshare):
go test -v -timeout=30m -tags "$(./btrfs_installed_tag.sh)" ./tests/conformance参数说明:
-tags "$(./btrfs_installed_tag.sh)":根据本机是否安装 btrfs 支持,动态追加对应的 Go build tag,脚本位于 btrfs_installed_tag.sh;-timeout=30m:整体超时 30 分钟,防止构建或拉取卡死;-v:输出每个子用例的详细执行结果。
4. 运行单个测试用例
通过-run标志按用例名筛选。用例名由TestConformance与internalTestCases中的name拼接而成。例如只运行名为shell的用例:
go test -v -timeout=30m -tags "$(./btrfs_installed_tag.sh)" -run TestConformance/shell ./tests/conformanceshell用例(见 testdata/Dockerfile.shell)内容为:
FROM quay.io/libpod/centos:7 SHELL ["/bin/bash", "-xc"] RUN env它在internalTestCases中对应定义(conformance_test.go):
{ name: "shell test", dockerfile: "Dockerfile.shell", buildahRegex: "(?s)[0-9a-z]+(.*)--", dockerRegex: "(?s)RUN env.*?Running in [0-9a-z]+(.*?)---", },其中buildahRegex/dockerRegex用于校验各自构建日志是否包含预期输出片段。
5. 逐行对比构建层
如果希望不仅对比最终镜像,还按 Dockerfile 逐行(逐层)构建并对比中间结果,追加-compare-layers标志:
go test -v -timeout=60m -tags "$(./btrfs_installed_tag.sh)" ./tests/conformance -compare-layers该模式将超时延长到 60 分钟,因为每个用例会为 Dockerfile 的每一行(除首行FROM外)单独构建并对比一次。此标志对应源码中的compareLayers变量(conformance_test.go),在testConformanceInternal中,当compareLayers为真时,测试会扫描 Dockerfile 字节流,在每个换行处截取前缀并逐行t.Run构建对比(见 conformance_test.go)。
测试的内部工作机制
双层对比:镜像配置与文件系统
每个用例的执行流程(testConformanceInternal→testConformanceInternalBuild)大致如下:
- 为用例创建临时目录,分别作为构建上下文、buildah 存储根(
rootDir)与运行根(runrootDir),并用copier.GetContext/copier.PutContext把testdata下的上下文或 Dockerfile 复制到临时目录(conformance_test.go); - 初始化
storage.GetStore,为 buildah 分配独立的图驱动存储,并以STORAGE_DRIVER环境变量控制图驱动(conformance_test.go); - 分别调用
buildUsingDocker、buildUsingBuildah(可选buildUsingImagebuilder)构建同一镜像,构建输出统一写入报告目录(-buildah-dir、-docker-dir、-imagebuilder-dir或临时目录,见TestMain中的 flag 定义,conformance_test.go); - 通过
saveReport把每个构建产物的manifest.json、config.json(原始 Docker 格式)、oci-config.json(OCI 格式)、fs.json(文件系统树摘要)与build.log、version等落盘(conformance_test.go); - 用
readReport读回上述文件,分别对Docker 格式配置(originalSkip豁免集)、OCI 格式配置(ociSkip豁免集)与文件系统树(fsSkip豁免集)做compareJSON对比(conformance_test.go)。
对比会检查三类差异:只出现在 buildah 一侧的字段(missKeys)、只出现在 Docker 一侧的字段(leftKeys)、双方都有但值不同的字段(diffKeys),并通过configCompareResult/fsCompareResult生成可读的失败报告(conformance_test.go)。
报告文件的生成与调试价值
即使测试通过,报告目录中也会留下每个镜像的完整构建信息。saveReport保存的fs.json结构来自FSTree/FSEntry/FSHeader(conformance_test.go),它记录了每一层每个条目的类型、链接名、大小、权限、uid/gid、mtime、设备号、xattr 与内容摘要。applyLayerToFSTree则按RootFS.DiffIDs顺序把各层逐一应用到内存中的文件树,处理硬链接、.wh.白化条目与.wh..opq不透明目录(conformance_test.go),最终得到与真实文件系统等价的摘要树。当测试失败时,报告目录中的Dockerfile、build.log与各类 JSON 文件是定位差异的第一手资料。
豁免字段:为什么要容忍差异
测试对比时并非逐字段严格相等,而是通过豁免列表声明"可以接受"的差异,这些都定义在 conformance_test.go:
originalSkip:Docker 格式配置中的created、container、docker_version、container_config:hostname、config:hostname、config:image、container_config:cmd、container_config:image、history、rootfs:diff_ids、moby.buildkit.buildinfo.v1。这些字段天然反映构建器自身信息或时间戳,无法也不应一致;ociSkip:OCI 格式配置中的created、history、rootfs:diff_ids;fsSkip:文件系统层面的(dir):dev:mtime、(dir):proc:mtime、(dir):sys:mtime、(dir):etc:mtime——这些是RUN语句执行时挂载或合成目录的时间戳,buildah 与 docker build 的处理策略本就不同。
compareJSON对嵌套对象采用前缀递归(如config:Env),对数组采用"元素集合相等"语义(适用于 Labels、Env 等无序集合),对mode字段以八进制形式输出差异(diffDebug,conformance_test.go)。用例还可以通过自身的fsSkip字段声明额外的文件系统差异,例如某些目录的 mtime 不会被重置(见copy file to root等用例的fsSkip: []string{"(dir):a:mtime"})。
新旧行为的兼容矩阵
部分用例带有testUsingSetParent或testUsingVolumes标记,会在TestConformance中分裂为两个子测试,分别以"新行为"与"旧行为"运行:
new-set-parent/old-set-parent:通过dockerBuilderVersion = docker.BuilderBuildKit(新)或docker.BuilderV1(旧),并给 buildah 设置CompatSetParent为false/true,验证config.Parent字段的两种处理策略;new-volumes/old-volumes:同理用 BuildKit 与 V1 构建器,配合CompatVolumes的false/true,验证VOLUME指令是"仅记录配置"还是"保留卷内容"的差异;旧行为还追加fsSkipCompatVolumesTrue豁免集(conformance_test.go)。
这一机制保证测试套件在 Docker 构建器新旧交替的过渡期依然能给出确定性的对比结论。
SELinux 与临时挂载等平台细节
SELinux 相关的差异处理位于 tests/conformance/selinux_linux_test.go:当系统启用 SELinux 时,selinuxMountFlag()返回":Z",用于在挂载卷等场景自动附加重标记选项;未启用则返回空串。非 Linux 平台则由 selinux_unsupported_test.go 提供空实现。此外,testCase.transientMounts支持@@TEMPDIR@@占位符,在buildUsingBuildah与buildUsingImagebuilder中都会被替换为实际构建上下文路径(conformance_test.go)。
测试用例的组织与扩展
全部内置用例集中在internalTestCases(conformance_test.go 起),覆盖 ADD/COPY 的各类路径与权限场景、.dockerignore的各种匹配模式、多阶段构建、通配符与 glob、heredoc、WORKDIR、VOLUME、RUN挂载与临时挂载、符号链接与硬链接、预期失败用例(shouldFailAt)等。对应的 Dockerfile 与上下文素材均位于 tests/conformance/testdata/ 目录,例如:
copy-escape-glob:COPY 中 glob 转义行为,素材在 tests/conformance/testdata/copy-escape-glob/;dockerignore:.dockerignore规则的各种边界情况,素材在 tests/conformance/testdata/dockerignore/;multistage/copyback:多阶段构建中的跨阶段 COPY,素材在 tests/conformance/testdata/multistage/copyback/。
新增用例的通用做法是:在testdata下添加上下文目录或 Dockerfile,再在internalTestCases中登记一个testCase,视需要补充buildahRegex/dockerRegex、fsSkip、shouldFailAt等字段。测试框架会自动完成上下文复制、双(三)方构建、报告落盘与对比断言。
常见问题与排查建议
- 非 root 环境下权限不足:buildah 构建依赖容器存储与挂载能力,请使用
buildah unshare(或podman unshare)包裹go test命令再运行; - Docker daemon 未启动或版本过旧:测试初始化会失败或跳过 BuildKit 相关用例,确认
docker服务已启动且版本 ≥ 19.03,并保证 API 协商版本 ≥ 1.38; - 基础镜像拉取失败:先执行上文的基础镜像预拉取命令,尤其注意两个以 digest 引用的
busybox镜像; - 失败信息定位:测试失败时日志会打印上下文路径、Dockerfile 内容(含末尾无换行提示)、
.dockerignore内容以及 buildah / docker 的完整构建输出;结合报告目录中的fs.json与config.json可逐字段核对差异来源; - 仅看最终镜像还是逐层对比:默认对比最终镜像;需要定位"哪一条指令导致差异"时,使用
-compare-layers逐行构建对比。
通过本套件,开发者可以在每次改动 buildah 的镜像构建逻辑后,快速确认其输出与 Docker 保持等价,既服务于回归测试,也为镜像兼容性提供了可审计的量化依据。
- 云原生
【免费下载链接】buildah
A tool that facilitates building OCI images.
相关推荐
Kubernetes Conformance 镜像完全指南:基于 `test/conformance/image` 构建、发布与运行 E2E 一致性测试
Kubernetes Conformance 镜像完全指南:基于 test/conformance/image 构建、发布与运行 E2E 一致性测试 导读 :
云原生容器编排集群管理微服务Pyrefly Conformance 一致性测试套件:目录结构、预期错误标记与自动化验证实战
Pyrefly Conformance 一致性测试套件:目录结构、预期错误标记与自动化验证实战 本篇技术指南围绕 pyrefly 仓库中的 conformanc
开发工具静态分析IDE代码质量Nhost Auth 的 OpenID Conformance 认证套件:用官方一致性测试验证 OAuth2/OIDC 实现
Nhost Auth 的 OpenID Conformance 认证套件:用官方一致性测试验证 OAuth2/OIDC 实现 导读 Nhost 是一个开源的 F
后端认证鉴权数据库无服务开发工具云原生
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考