Kubernetes 测试镜像全流程指南:从修改、构建、测试到发布 e2e 测试镜像
Kubernetes 的端到端(E2E)测试依赖一套独立于主仓库之外维护的测试镜像,用来验证各类集群特性与功能。本文以 test/images/README.md 为主线,系统讲解这些镜像的设计动机、版本管理机制,以及完整的"修改镜像 → 构建镜像 → 本地测试 → 晋升发布"工作流,并结合仓库内 Makefile、image-util.sh、cloudbuild.yaml 与镜像目录的源码实现展开说明。读完本文,你将掌握为 Kubernetes E2E 测试镜像添加功能、正确升版、构建多架构镜像、改写镜像仓库并最终将其合入官方测试工作流所需的全部技能。
镜像的定位与多架构发布模型
test/images/ 下所有镜像都服务于 Kubernetes 测试,用于确保集群各项特性与功能的正确性(见 test/images/README.md)。这些镜像并非单架构产物,而是以 manifest list(manifest 列表) 的形式构建并发布,从而支持 multiarch 与跨平台使用。
从仓库目录看,test/images/ 下每个子目录即一个镜像,例如 agnhost、busybox、kitten、nautilus、resource-consumer、sample-apiserver、nginx、perl 等。每个镜像目录通常包含:
VERSION:镜像版本号,构建时作为 tag 使用(如 agnhost/VERSION 当前为2.66.1);BASEIMAGE:列出各os/arch对应的基础镜像(见下文);Dockerfile与可选的Dockerfile_windows:区分 Linux/Windows 构建;Makefile(可选):负责在构建镜像前先编译 Go 二进制;ALIAS(可选):用于为镜像指定别名(如nginx-new的案例)。
BASEIMAGE 文件:多架构的"路线图"
image-util.sh 以 BASEIMAGE 文件为构建依据:每一行是一个 <os>/<arch>[=<base image>] 或 os/arch/os_version=base image 条目。以 agnhost 的 BASEIMAGE 为例:
linux/amd64=alpine:3.21
linux/arm64=arm64v8/alpine:3.21
linux/ppc64le=ppc64le/alpine:3.21
linux/s390x=s390x/alpine:3.21
windows/amd64/1809=REGISTRY/busybox:1.38.0-1-windows-amd64-1809
windows/amd64/ltsc2022=REGISTRY/busybox:1.38.0-1-windows-amd64-ltsc2022
windows/amd64/ltsc2025=REGISTRY/busybox:1.38.0-1-windows-amd64-ltsc2025
- 若镜像目录 没有
BASEIMAGE文件,构建脚本会回退到默认平台集合linux/amd64、linux/arm64、linux/ppc64le、linux/s390x(定义于 image-util.sh); - 含 Windows 条目的格式是
os/arch/os_version三段式,因为 Windows 需按 OS 版本(1809、ltsc2022、ltsc2025)分别构建; - 值中的
REGISTRY占位符会在构建时被替换为真实的REGISTRY环境变量值(见getBaseImage中的sed替换逻辑)。
在构建每个平台时,脚本先把镜像目录拷贝到临时目录,若存在 Makefile 则先执行 make bin OS=... ARCH=... TARGET=... 调用 image-util.sh 的 bin() 函数,在容器内以 CGO_ENABLED=0 交叉编译 Go 二进制;随后按 os/arch 决定使用 Dockerfile 还是 Dockerfile_windows,最终通过 docker buildx build --platform <os>/<arch> 产出带 -<os>-<arch>[-<os_version>] 后缀的镜像 tag。push 阶段再通过 docker manifest create --amend 与 docker manifest annotate 把这些分平台镜像合并成 manifest list 并推送(其中 Windows 条目会额外标注 --os-version,以便 Windows 节点拉取与自身匹配的镜像,见 image-util.sh)。
环境准备:Linux 构建节点与 buildx
构建 Docker 测试镜像需要一个 Linux 节点,并安装 make、docker 以及 docker buildx——多架构镜像与 Windows 镜像都依赖 buildx 完成。为保证多架构与 Windows 镜像能被正确构建,还需要一次性的初始化(CI 中该初始化定义在 cloudbuild.yaml 内):
docker run --privileged --rm tonistiigi/binfmt --install all
docker buildx create --name img-builder --use
docker buildx inspect --bootstrap
- 第一条命令安装 QEMU binfmt 处理器,使 Linux 节点能执行非本机架构的构建步骤;
- 后两条创建并引导名为
img-builder的 buildx builder 实例。
另外,构建节点必须能够向目标镜像仓库推送镜像,因此请确保已通过目标 registry 的认证。
更新测试镜像的整体流程
Kubernetes E2E 测试数以千计,但并非全部镜像都被每次 PR 触发使用(尤其是不属于 Conformance 测试的部分)。为了防止镜像回归导致测试任务失败,任何对镜像本身或其内置二进制的改动,都必须同步提升镜像版本号;若发生无法立即修复的回归,则会将 E2E 测试中使用的镜像版本回退到最后一个已知稳定版本。
E2E 测试套件中的大多数测试使用 agnhost 镜像。它包含若干子命令,用于校验不同的 Kubernetes 行为(详见 agnhost/README.md)。官方建议:当需要测试新功能时,先考虑把它实现为 agnhost 的一个子命令,而不是另起一个全新的测试镜像。
更新镜像的完整流程分为四步,下文逐一展开:
完成这四步后,你的镜像改动才会真正进入 e2e 测试。此外,对于全新镜像与 Windows 测试镜像还有额外注意事项。
agnhost:跨平台行为一致的瑞士军刀
由于 Linux 与 Windows 在行为与信息获取方式上存在显著差异(例如 DNS 后缀列表在 Linux 上位于 /etc/resolv.conf,Windows 上则没有该文件),agnhost(agnostic + host 的合成词)被设计成一个可扩展、行为与输出不随底层操作系统变化的 CLI。它的镜像同时覆盖 Linux 与 Windows 容器(Windows 版基于 busybox,而 busybox 又基于 nanoserver),从源码结构看,其子命令大多位于 agnhost/ 下的独立包中(如 dns/、net/、webhook/、mounttest/、fakeregistryserver/ 等)。
镜像内同时打包了大量测试常用工具(iperf、curl、包含 dig 的 dns-tools、CoreDNS),并预置了测试所需的监听端口(80/8080/8081/9376/5000,见 Dockerfile)以及 TLS 证书(localhost.crt/localhost.key)。值得一提的是,Dockerfile 还创建了 uid=1000 的用户与 gid=50000 的用户组,供 SupplementalGroups 相关 E2E 用例使用——这正是"测试镜像为具体用例服务"的典型证据。
创建并晋升全新镜像
首先请与 SIG Testing 沟通,确认能否先用 agnhost 满足需求(包括为其新增功能)。整合镜像的好处包括:
- 减少打补丁的负担(依赖、基础镜像、Go 版本等只需在一处维护);
- 降低运行时拉取镜像的依赖,从而加快测试速度;
- 简化离线(airgap)测试环境——需要镜像/同步的镜像更少。
Kubernetes 社区正努力减少镜像总数。如果你确认需要增加一个完全独立的镜像,仍需与 SIG Testing 确认,并让它能被 Image Builder 自动构建——为此你需要在 kubernetes/test-infra 仓库中为它定义 postsubmit Prow job(官方通过运行 k8s-staging-e2e-test-images.sh 脚本来生成)。
Windows 测试镜像注意事项
理想情况下,同一个 Dockerfile 应能同时构建 Windows 与 Linux 镜像,但并非总能如此。若某个镜像需要独立的 Windows Dockerfile,应命名为 Dockerfile_windows;构建时 image-util.sh 会优先在 Windows 构建中检查该文件。
构建过程虽然都用 docker buildx,但 Windows 镜像存在若干已知限制(这些限制也解释了仓库内 Dockerfile_windows 为何采用特定写法):
- Dockerfile 可以包含多阶段,但 Windows 阶段不能有任何
RUN命令(参见 agnhost 的Dockerfile_windows:所有依赖RUN的准备工作——下载并解压 CoreDNS、iperf、coreutils——都在linux/$BUILDARCH的 prep 阶段完成,再用COPY --from=拷入 Windows 阶段); - Windows 阶段不能有
WORKDIR(docker/buildx issue #378); - 向 Windows 镜像拷贝 symlink 时,buildx 会改写 symlink 目标并加
Files\前缀(docker/buildx issue #373)。规避方式是使用相对路径的 symlink 目标并在对应位置存在目标副本(参见 busybox 的Dockerfile_windows); docker buildx会把镜像的 PATH 环境变量覆盖成 Linux 风格,这在 Windows 上无法正常工作(moby/buildkit issue #1560),因此 agnhost 的Dockerfile_windows用ENV PATH=...显式写回 Windows 风格 PATH;- 所有 Windows 镜像的基础镜像是 nanoserver,它比 Windows Servercore 小约 10 倍。大多数二进制可直接运行,但少数因缺少依赖而无法工作(注意:即使加入的二进制无法运行,镜像依然能构建成功)。例如
coredns.exe依赖netapi32.dll,nanoserver 中没有该 DLL,需从 servercore 镜像中拷贝(见 Dockerfile_windows,其来源镜像即windows-servercore-cache)。经验法则:优先选用 64 位应用(依赖更少),并用宿主机上的procmon.exe(使用进程隔离而非 Hyper-V 隔离)排查缺失依赖。
由于 buildx 对 Windows 阶段 RUN 的限制,构建 Windows 镜像需要一个 Windows helper 镜像(否则需要 Windows 构建节点)。该 helper 镜像位于 e2eteam/powershell-helper:6.2.7,其构建细节见 windows/README.md,对应的 Dockerfile 位于 test/images/windows/powershell-helper/。
最后,Windows 上若要启动进程隔离容器,容器 OS 版本需与宿主机 OS 版本高度匹配。因此测试镜像为多个 Windows OS 版本分别构建:1809(Windows Server 2019)、ltsc2022(Windows Server 2022)、ltsc2025(Windows Server 2025)。若要支持新的 Windows OS 版本,需要先在 windows-servercore-cache 与 busybox 镜像中为该版本新增条目(这两个镜像被其他 E2E 测试镜像用作缓存/基础镜像),再推广到其余镜像。
修改镜像与版本管理
修改镜像功能以满足测试用例需求后,必须更新版本号。版本号只需修改镜像目录下的 test/images/${IMAGE_NAME}/VERSION 文件,构建时该值会作为镜像 tag 使用(见 image-util.sh)。
注意镜像间的父子依赖:部分测试镜像(如 agnhost)会被用作其他镜像(如 kitten、nautilus)的基础镜像。看 kitten/BASEIMAGE 可发现其条目指向 REGISTRY/agnhost:2.66.1-...,即引用了 agnhost 的分平台镜像 tag。因此:
- 若父镜像(如 agnhost)的
VERSION被提升,必须同步提升子镜像(kitten、nautilus 等)BASEIMAGE 文件中的版本引用,否则子镜像无法反映父镜像的改动。
同时要意识到:Kubernetes CI 在镜像晋升发布之前不会使用你的改动。因此建议先把镜像构建并推送到自己的仓库,用真实测试验证通过,再进入发布流程。
构建镜像
测试镜像统一通过 make 构建,入口是 test/images/Makefile。由于部分镜像(如 agnhost)是其他镜像的基础,若涉及这类镜像建议优先构建它们。
构建单个镜像:
make all WHAT=agnhost
构建并推送镜像:
make all-push WHAT=agnhost
从 Makefile 可以看到两者的实现差异:all 只调用 image-util.sh build ... "docker"(输出到本地 docker daemon);all-push 则先以 "registry" 作为 output type 构建、再执行 push(输出直接进 registry 并用 manifest list 合并)。因此本地 make all 只构建 Linux 平台(Windows 容器镜像必须推送到 registry,见 image-util.sh 中 docker 输出类型会跳过 Windows 的逻辑)。
默认情况下,镜像会被打 tag 并推送到 registry.k8s.io/e2e-test-images 仓库(REGISTRY 变量的默认值,定义在 Makefile)。如需使用自定义仓库:
REGISTRY=foo_registry make all-push WHAT=agnhost
注(面向 gcr.io 测试镜像发布者):部分测试(如 "should serve a basic image on each replica with a private image")要求
agnhost镜像同时发布到一个经过认证的私有仓库,因此需要分两次推送:REGISTRY=registry.k8s.io/e2e-test-images make all-push WHAT=agnhost REGISTRY=gcr.io/k8s-authenticated-test make all-push WHAT=agnhost
此外,WHAT=all-conformance 用于构建/推送 E2E Conformance 测试中最常用的镜像子集。从 image-util.sh 可见,该目标只会处理 8 个镜像:busybox、agnhost、glibc-dns-testing、kitten、nautilus、nonewprivs、resource-consumer、sample-apiserver——因为完整构建 test/images/ 下所有镜像耗时极长,且部分镜像极少使用。CI 侧,cloudbuild.yaml 通过 $_WHAT、$_REGISTRY、$_PULL_BASE_SHA 等 substitution 调用 make all-build-and-push,将镜像发布到 staging 仓库 gcr.io/k8s-staging-e2e-test-images。
构建细节层面,每个平台镜像都会携带构建元数据 label:image_version=<TAG>、commit_id=<GIT_COMMIT_ID>、git_url=...(见 image-util.sh),便于追溯镜像来源;同时构建使用 --no-cache --pull 保证可复现性。
测试镜像改动
镜像构建并推送到了可访问的仓库之后,就可以用指向该仓库的 E2E 测试来验证改动。方法是在运行相关测试前设置环境变量:
export KUBE_TEST_REPO_LIST=/path/to/repo_list.yaml
repo_list.yaml 是 E2E 测试读取的仓库重定向配置。其字段由 manifest.go 中的 RegistryList 结构体定义(YAML 字段与结构体 tag 一一对应),包括 promoterE2eRegistry、buildImageRegistry、invalidRegistry、gcEtcdRegistry、gcRegistry、sigStorageRegistry、privateRegistry、dockerLibraryRegistry、cloudProviderGcpRegistry 等。示例文件只需给出你要覆盖的仓库:
promoterE2eRegistry: your-awesome-registry
gcRegistry: your-awesome-registry
sampleRegistry: your-awesome-registry
readRepoList 会读取该 YAML 并覆盖默认 registry 配置(也支持以 http(s):// 开头的 URL 形式),随后通过 initImageConfigs 生成每个镜像的 {registry, name, version} 映射;当 KUBE_TEST_REPO_LIST 未设置时则回退到 manifest.go 中的内置默认 registry。由于 imageConfigs 在包初始化阶段(var ... = readRepoList(...))就已从环境变量读取,该环境变量必须在测试进程启动前就设置好。
要留意的是,一个测试可能使用多个镜像,所以最好把相关镜像一并构建并推送。例如从 manifest.go 可见,仅 agnhost 一族就被拆成了 Agnhost(默认 2.66.1)、AgnhostPrev(2.55,供滚动升级类测试使用)与 AgnhostPrivate(私有认证仓库 agnhost:2.6)三个配置项。
让 E2E 测试使用新版本镜像
即便构建推送完成,E2E 测试真正引用的镜像 tag 仍硬编码在 test/utils/image/manifest.go 中(如 configs[Agnhost] = Config{list.PromoterE2eRegistry, "agnhost", "2.66.1"})。因此还需要修改该文件中的镜像版本,并重新编译测试二进制:
./build/run.sh make WHAT=test/e2e/e2e.test
之后即可运行期望的测试来验证改动是否生效。
晋升发布(Promoting Images)
在本地完成改动与测试后,即可通过如下多步骤流程把改动分享出去(这也是改动真正进入官方 E2E 测试的最后环节):
-
在同一 PR 中提升镜像版本:每个测试镜像目录下都有
VERSION文件,例如 agnhost 的版本文件在 test/images/agnhost/VERSION。这一步与"修改镜像"阶段同步完成。 -
等待自动 postsubmit job 构建:PR 被批准并合并后,会自动触发 postsubmit job 构建发生改动的镜像。例如改动在
test/images/agnhost,会触发post-kubernetes-push-e2e-agnhost-test-imagesjob,把镜像推送到 staging 仓库gcr.io/k8s-staging-e2e-test-images。你可以用 staging 仓库的镜像做更多测试。所有镜像的 postsubmit job 及其日志可在 testgrid 的 sig-testing-images 面板查看,便于排查问题。注意:这些 staging 镜像与 E2E job 实际使用的镜像不同,仍需晋升到最终仓库。 -
晋升到最终仓库:在
kubernetes/k8s.io仓库的registry.k8s.io/images/k8s-staging-e2e-test-images/images.yaml中增加一行,把镜像晋升到registry.k8s.io/e2e-test-images。晋升需要镜像 manifest list 的 digest,可用manifest-tool获取:manifest-tool inspect --raw gcr.io/k8s-staging-e2e-test-images/${IMAGE_NAME}:${VERSION} | jq '.[0].Digest' -
更新 E2E 测试引用:最后开一个 PR,更新 test/utils/image/manifest.go 中镜像 tag,让 E2E 测试改用新晋升的镜像。
完成以上步骤后,你的改动就会被 E2E 测试正式采用。
已知问题与规避手段
docker manifest create 因权限问题失败:错误表现为在 /etc/docker/certs.d/gcr.io 上 permission denied。解决方式是放开 /etc/docker 的执行权限:
sudo chmod o+x /etc/docker
同 SHA 无法重复推送:busybox、nginx、nginx-new、perl 等镜像从 dockerhub 镜像到 gcr.io/k8s-staging-e2e-test-images 后,只保留了一个 noop Dockerfile。由于 test-infra 的已知问题,同一 SHA 不能重复推送;对这些镜像做一次小改动以产生新的 SHA,才能重新推送并晋升。
小结
测试镜像是 Kubernetes E2E 质量保障体系中容易被忽视却至关重要的一环。其核心机制可归纳为四点:以 VERSION + BASEIMAGE 驱动的多架构构建模型、以 agnhost 为优先复用对象的镜像整合策略、以 manifest.go 为唯一事实来源的镜像引用管理,以及 staging → 最终仓库的两段式晋升流程。理解并遵守这套流程,既能避免镜像回归拖垮海量 E2E 任务,也能让新特性以标准化方式进入官方测试体系。若需深入研究,建议继续阅读 agnhost 子命令文档、镜像构建脚本 与 E2E 镜像引用配置。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00