
gVisor 测试镜像体系images 目录的镜像组织、Make 构建、哈希缓存与多架构支持【免费下载链接】gvisorApplication Kernel for Containers项目地址: https://gitcode.com/GitHub_Trending/gv/gvisor本文围绕 gVisor 仓库中 images/README.md 讲解的测试容器镜像体系展开它说明了 gVisor 如何把测试所需的各类容器镜像统一放在images/目录下通过 Make 目标完成列出、加载、构建与推送并用目录内容哈希实现跨 CI 运行的镜像缓存。读完后你可以掌握在 gVisor 中添加一个新测试镜像的完整流程、gvisor.dev/images规范化标签的设计目的以及通过ARCH变量进行交叉架构构建的原理。images 目录的定位所有测试镜像的单一事实来源images/README.md 开宗明义该目录包含所有被测试使用的镜像This directory contains all images used by tests。这些镜像有三个关键属性集中管理每类镜像一个子目录例如images/basic/busybox/、images/gpu/ollama/、images/benchmarks/ffmpeg/目录内包含一个 Dockerfile 及镜像所需的任何其他文件CI 自动推送所有镜像必须推送到托管在 Google Container Registry 上的测试项目这一步由持续集成自动完成。这样做的目的是加速——镜像不需要在每次测试运行时从零构建Make 工具链驱动镜像工具通过make暴露具体实现在 tools/images.mk由顶层 Makefile 第 89 行include tools/images.mk引入。从仓库结构看images/下已经沉淀了大量真实镜像覆盖了 gVisor 测试面的主要场景images/basic/alpine、busybox、python、redis、nginx、mysql、ruby、rust、ubuntu、sudo 等基础与中间件镜像供 syscall 测试、集成测试使用images/benchmarks/ffmpeg、nginx、redis、sysbench、iperf、hackbench 等性能基准镜像images/compatibility/kafka、rabbitmq、postgresql、wordpress 等兼容性验证镜像images/gpu/与images/tpu/CUDA 测试、PyTorch、vLLM、ollama 等 GPU/ML 工作负载镜像images/iptables/、images/nftables/、images/packetdrill/网络栈相关测试镜像images/default/构建工具链引导镜像下文为什么用 Make会解释。为什么选择 MakeREADME 中给出了一个简短但关键的答案Make 可以用来引导bootstrapdefault镜像该镜像包含bazel及工具链的其他所有部分。这是一个典型的鸡生蛋问题gVisor 的主要构建系统是 Bazel参见 Makefile 中include tools/bazel.mk的逻辑但 Bazel 本身被封装在一个规范化的 Docker 容器内运行而这个容器镜像images/default/本身又要被构建和加载。Make 作为 Linux 系统上几乎必然存在的低级构建工具承担了这一自举层在 Bazel 可用之前先用make构建好运行 Bazel 的镜像。列出镜像从仓库顶层目录运行$ make list-all-images在 tools/images.mk 中该目标的实现是对每个镜像名逐个echolist-all-images: ## List all images. for image in $(ALL_IMAGES); do echo $${image}; done镜像名的来源是目录扫描而非手工维护ALL_IMAGES通过find images/ -name Dockerfile -o -name Dockerfile.$(ARCH)找到所有含 Dockerfile 的目录把相对路径中的images/前缀去掉、斜杠替换为下划线得到镜像名。也就是说images/basic/busybox/→ 镜像名basic_busybox→ 目标load-basic_busyboximages/下新增一个带 Dockerfile 的目录后无需修改 Makefile 就能自动生成对应的 load/push 目标。此外还有一组细化的列表目标list-all-test-images剔除NON_TEST_IMAGES当前排除gpu/ollama/bench、gpu/vllm、gpu/triton、tpu/vllm四个非测试镜像、list-cpu-images与list-gpu-images按gpu_*、tpu_*前缀区分并经过分片过滤。加载镜像load 目标与规范化标签README 给出的核心用法To build a specific image, usemake load-imagefrom the top-level directory. This will ensure that an imagegvisor.dev/images/image:latestis available.例如make load-basic_busybox之后本地就会存在gvisor.dev/images/basic/busybox:latest。这个标签体系有两层含义规范化路径解耦测试与镜像基础设施。README 明确指出Images should always be referred to via thegvisor.dev/imagescanonical path. This tag exists only locally, but serves to decouple tests from the underlying image infrastructure.镜像必须始终通过gvisor.dev/images规范化路径引用。该标签只存在于本地其作用是把测试与底层镜像基础设施解耦。测试代码侧的对应实现在 pkg/test/testutil/testutil.go// ImageByName mangles the image name used locally. This depends on the image // build infrastructure in images/ and tools/vm. func ImageByName(name string) string { return fmt.Sprintf(gvisor.dev/images/%s, name) }也就是说无论远端仓库是 Google Artifact Registry 还是将来换成别的存储测试看到的名字永远是gvisor.dev/images/...变更基础设施不需要改测试代码。CI 的两种依赖粒度。README 说明持续集成系统可以对单个镜像取细粒度依赖逐个load目标或者用一次load-all-test-images拉取全部测试镜像。顶层 Makefile 中的集成测试目标正是这种细粒度依赖的实例例如integration-test-images: load-image-test load-basic load-systemd-integ load-systemd-services load-ubi10-init以及 GPU 场景的gpu-smoke-images: load-gpu_cuda-tests load-gpu_cuda-tests-12-8、网络场景的iptables-tests: load-iptables $(RUNTIME_BIN)等。注意load-basic这类组目标tools/images.mk 会为每个镜像前缀如basic、gpu、runtimes自动展开load-group目标其依赖是同组下所有load-group_*子目标。添加新镜像的完整流程README 的 Adding new images 一节给出了四步流程结合 tools/images.mk 的实现可以补充更多细节建目录在images/下新建目录或加入现有子目录放入 Dockerfile 及镜像所需的其他文件。由于ALL_IMAGES是目录扫描生成的目标会自动出现保证可复现所有镜像会用目录内容哈希打标签并做记忆化memoize。因此每个镜像都应尽可能做到完全可复现——尽量使用固定 tag 和固定版本。仓库中的真实示例 images/basic/busybox/Dockerfile 只有一行FROM busybox:1.31.1这正是固定版本要求的直观体现尽量与架构无关README 要求镜像在可行时保持 architecture-independent构建脚本会负责把正确架构的版本加载到机器上并打上统一的规范化标签下节展开在 Makefile 中加依赖如果某个测试集需要该镜像就在顶层 Makefile 中相应的测试目标里加上load-image依赖该目标会在远端仓库已有该 tag 时直接拉取而不重新构建。构建与推送镜像README 说明所有镜像都可以用build-image手动构建、用push-image推送也可以用push-all-images一次推完推送需要项目在镜像仓库上的相应权限。CI 侧同样支持细粒度的单个push目标依赖或一次push-all-images确保所有镜像最新。对照当前 tools/images.mk 的实现可手动操作的完整目标集为目标作用rebuild-image强制在本地重新构建README 旧称build-image当前工具链中的目标名是rebuild-注释解释了改名原因避免与 Bazel 的build术语冲突pull-image强制从远端重新拉取load-image首选入口先尝试拉取失败则本地构建should never failpush-image校验远端 tag 不存在后构建并推送tag-image对本地镜像打哈希 tag latest双标签local-image-image打印当前本地镜像的image:tag全名其中load的拉取失败即构建逻辑tools/images.mk是这个体系健壮性的关键load-%: register-cross ## Pull or build an image locally. if [ -f $(call path,$*)/$(call dockerfile,$*) ]; then \ if [ $(SKIP_IMAGE_LOAD) true ]; then \ ... else \ ($(call pull,$*)) || ($(call rebuild,$*)); \ fi; \另有SKIP_IMAGE_LOADtrue开关若本地已存在镜像则直接使用不存在则直接报错退出适合完全离线或本地镜像已就绪的场景。哈希标签与两级 tag 机制缓存如何工作这是 README 中tagged and memoized using a hash of the directory contents一句背后的完整机制也是理解 CI 为何不需要每次从零构建的核心。tools/images.mk 中的定义# The tag construct is used to memoize the image generated (see README.md). tag $(shell cd images find $(subst _,/,$(1)) -type f | sort -f -d | xargs -n 1 sha256sum | sha256sum - | cut -c 1-16) remote_image $(REMOTE_IMAGE_PREFIX)/$(subst _,/,$(1))_$(ARCH) local_image $(LOCAL_IMAGE_PREFIX)/$(subst _,/,$(1))要点tag 是对目录内所有文件内容的 sha256 的 sha256取前 16 个字符。只要 Dockerfile 及目录内任何文件不变tag 不变远端镜像可精确命中一旦内容有改动tag 改变CI 会构建并推送新版本。这实现了远端激进缓存 内容寻址的语义缓存永远以本地文件为准ensuring that images will always be sourced using the local files每个镜像实际打两个 tag远端 tagremote_image:hash内容寻址以及本地规范化 taggvisor.dev/images/path:latest。tag宏tools/images.mk先docker tag到本地哈希 tag再复制为latest保证测试代码看到的永远是latest已存在则跳过Makefile 加载时通过docker images枚举本地已有镜像EXISTING_IMAGES若remote_image_tag已存在就生成一条已加载规则使load直接短路避免重复 pull。远端仓库前缀默认为REMOTE_IMAGE_PREFIX ? us-central1-docker.pkg.dev/gvisor-presubmit/gvisor-presubmit-imagesGoogle Artifact Registry且远端镜像名带架构后缀..._ARCH与本地gvisor.dev/images/...前缀解耦——两者都可以被 CI 覆盖。多架构与交叉构建README 的 Multi-Arch images 一节给出默认行为与交叉构建方式By default, the image is built for host architecture. Cross-building can be achieved by specifyingARCHvariable to make. For example:$ make ARCHaarch64 rebuild-defaulttools/images.mk 的实现链路是ARCH默认取uname -m宿主架构命令行覆盖后DOCKER_PLATFORM_ARGS自动变为--platform$(ARCH)docker build/pull 都会带上该参数Dockerfile 支持按架构选择dockerfile宏优先查找Dockerfile.$(ARCH)例如Dockerfile.aarch64不存在才回退到通用Dockerfile。仓库中images/arm-qemu/Dockerfile.x86_64、images/jekyll/Dockerfile.x86_64等就是这一机制的实例images/下也存在大量Dockerfile.x86_64命名的文件qemu binfmt 注册load/pull/rebuild/test目标都依赖register-crosstools/images.mk。当ARCH与宿主架构不一致且宿主未注册qemu-*binfmt 时自动运行docker run --rm --privileged multiarch/qemu-user-static --reset --persistent yes使非原生架构的容器可以在本地执行。tools/images.mk 头部注释还指出当 Bazel 以 server 模式运行时这套跨架构链路甚至可以产出交叉编译的二进制顶层 Makefile 中有一个基于该机制的完整用例arm-qemu-smoke-test以--configaarch64构建 runscload-arm-qemu加载 ARM QEMU 镜像再把runsc挂载进gvisor.dev/images/arm-qemu容器验证其可执行。CI 分片PARTITION 与 TOTAL_PARTITIONStools/images.mk 还实现了镜像级的分片能力PARTITION1-indexed与TOTAL_PARTITIONS两个变量在 CI 中由.buildkite/hooks/pre-command填充SHARDED_CPU_IMAGES/SHARDED_GPU_IMAGES/SHARDED_ALL_IMAGES用awk NR % TOTAL PARTITION % TOTAL做轮询切片。对应的list-cpu-images、test-cpu-images、test-gpu-images、test-all-test-images目标让 CI 可以把成百上千个测试镜像摊到多个分区并行执行。顶层 Makefile 同样有PARTITION/TOTAL_PARTITIONS变量透传给 Bazel 测试--test_envPARTITION... --test_envTOTAL_PARTITIONS...两者配合实现从镜像准备到测试执行的整链路分片。另一条镜像路径Bazel 的 docker_image 规则并非所有镜像都走 Make/Dockerfile 通道。images/defs.bzl 定义了一个 Bazel 规则docker_image它接收一个镜像文件系统 tarball.tgz/.tar.gz和若干额外 Dockerfile 指令生成一个可执行脚本运行docker import把 tarball 导入为镜像。这类文件系统快照直接转镜像的路径适合不需要复杂构建过程的场景与 Make 工具链互为补充。小结与实操要点围绕 images/README.md 描述的体系日常操作可归纳为# 1. 查看有哪些镜像 make list-all-images # 2. 加载单个镜像拉取失败自动本地构建 make load-basic_busybox # 3. 加载全部测试镜像 make load-all-test-images # 4. 交叉架构构建qemu 自动注册 make ARCHaarch64 rebuild-default # 5. 本地强制重建 / 推送 make rebuild-image make push-image # 需要远端仓库权限需要记住的设计约束镜像名用_分隔目录层级因为:会被 Make 解释所以 tag 与仓库名的分隔也改用_见 tools/images.mk 的注释测试代码只能通过gvisor.dev/images/...规范化标签引用镜像pkg/test/testutil/testutil.go新增镜像必须固定基础镜像版本以保证哈希缓存生效新增镜像只需建目录写 Dockerfile构建、缓存、推送目标全部自动生成。【免费下载链接】gvisorApplication Kernel for Containers项目地址: https://gitcode.com/GitHub_Trending/gv/gvisor创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考