Kubernetes Windows E2E 测试指南:从 [sig-windows] 用例集到密度测试的源码级解析
本文基于 Kubernetes 仓库中的 test/e2e/windows/README.md,讲解如何在一套包含 Windows 节点的集群上运行 SIG-Window 端到端(E2E)测试:包括准备 KUBECONFIG 与测试镜像仓库清单 KUBE_TEST_REPO_LIST 等前置条件、使用 --ginkgo.focus 精确筛选 [sig-windows] 用例的完整命令行,以及这些 Windows 专属 E2E 测试在源码层面如何通过 Feature 标记、OS 分发跳过机制和密度测试(Density Test)实现“只在 Windows 节点上运行”的隔离逻辑。读完后,你可以独立搭好 Windows 节点 E2E 测试环境,并能定位到每个测试用例、跳过逻辑与性能阈值在仓库中的具体实现位置。
一、前置条件:KUBECONFIG 与测试镜像仓库清单
原文档给出的前置步骤是:
KUBECONFIG=path/to/kubeconfig
curl https://raw.githubusercontent.com/kubernetes-sigs/windows-testing/master/images/image-repo-list -o repo_list
export KUBE_TEST_REPO_LIST=$(pwd)/repo_list
这里做了三件事:
-
KUBECONFIG指向目标集群的 kubeconfig,该集群必须至少包含一个可调度 Windows Pod 的 Windows 节点(带kubernetes.io/os=windows标签)。后续所有测试框架的跳过判断都依赖这一点。 -
拉取 Windows 测试镜像清单:
curl下来的image-repo-list是一份 YAML,用于将 E2E 测试所用镜像重定向到 Windows 可运行的镜像仓库(例如mcr.microsoft.com上的 Windows 版本pause/agnhost镜像)。 -
导出
KUBE_TEST_REPO_LIST:这是测试框架识别镜像重写配置的正式入口。可以在 test/e2e/framework/test_context.go#L361 看到对应 flag 的定义:flags.StringVar(&TestContext.KubeTestRepoList, "kube-test-repo-list", "", "A yaml file used for overriding registries for test images. " + "Alternatively, the KUBE_TEST_REPO_LIST env variable can be set.")也就是说
--kube-test-repo-listflag 与KUBE_TEST_REPO_LIST环境变量等价。更详细的镜像仓库重写机制说明见 test/images/README.md:repo_list.yaml中每行形如promoterE2eRegistry: your-registry,E2E 测试在拉取镜像时会按该映射替换注册表前缀。
二、运行 Windows E2E 测试的命令
原文档给出的运行命令是:
./e2e.test --provider=local \
--ginkgo.no-color \
--ginkgo.focus="\[sig-windows\]|\[Feature:Windows\]" \
--node-os-distro="windows"
各参数含义:
| 参数 | 作用 |
|---|---|
--provider=local |
使用本地已有的 kubeconfig(即 KUBECONFIG)而非云上 provider 创建集群 |
--ginkgo.no-color |
禁用彩色输出,便于 CI 日志收集 |
--ginkgo.focus |
Ginkgo 的正则焦点,只匹配描述中含 [sig-windows] 或 [Feature:Windows] 的用例 |
--node-os-distro=windows |
告诉测试框架节点 OS 分发为 Windows,是 SkipUnlessNodeOSDistroIs 判断的数据来源 |
这个 focus 正则能精确命中,是因为 Windows 测试套件的每个用例都打上了这两个标签,二者分别来自:
-
[sig-windows]:由测试包的SIGDescribe注入,见 test/e2e/windows/framework.go#L31:// sigDescribe annotates the test with the SIG label. var sigDescribe = framework.SIGDescribe("windows") -
[Feature:Windows]:由 Ginkgo Feature 标签注入,Feature 定义在 test/e2e/feature/feature.go:// Owner: sig-windows // Indicates that tests labeled with this feature depend on Windows and thus may only be run on Windows nodes. Windows = framework.WithFeature(framework.ValidFeatures.Add("Windows"))典型的用例注册方式(见密度测试 test/e2e/windows/density.go#L44):
var _ = sigDescribe(feature.Windows, "Density", framework.WithSerial(), framework.WithSlow(), skipUnlessWindows(func() { ... }))
三、skipUnlessWindows:确保用例只在 Windows 节点上执行
framework.go 中的核心封装是 skipUnlessWindows:
// skipUnlessWindows wraps some other Ginkgo callback such that
// a BeforeEach runs before tests defined by that callback which
// skips those tests unless the node OS is Windows.
func skipUnlessWindows(cb func()) func() {
return func() {
ginkgo.BeforeEach(func() {
// all tests in this package are Windows specific
e2eskipper.SkipUnlessNodeOSDistroIs("windows")
})
cb()
}
}
它包了一层 BeforeEach,在每个用例开始前调用 SkipUnlessNodeOSDistroIs("windows")。该函数实现在 test/e2e/framework/skipper/skipper.go#L151-L156:
func SkipUnlessNodeOSDistroIs(supportedNodeOsDistros ...string) {
if !framework.NodeOSDistroIs(supportedNodeOsDistros...) {
skipInternalf(1, "Only supported for node OS distro %v (not %s)",
supportedNodeOsDistros, framework.TestContext.NodeOSDistro)
}
}
framework.TestContext.NodeOSDistro 正是运行命令中 --node-os-distro=windows 写入的值。因此形成一条完整的保护链:命令行声明 OS → skipper 比对 → 非 Windows 环境自动跳过,再叠加 focus 正则,保证这套用例既能在通用 E2E 流程中安全混跑(自动 skip),又能被单独精确拉出来跑。
四、Windows E2E 用例全景
test/e2e/windows/ 目录下的文件即全部用例模块,覆盖了 Windows 节点的主要能力面:
| 文件 | 覆盖主题 |
|---|---|
| cpu_limits.go、memory_limits.go | CPU/内存限额与调度行为(Hyper-V 下内存限额有平台限制) |
| hyperv.go | Hyper-V 隔离容器(对应 Feature WindowsHyperVContainers) |
| host_process.go | HostProcess 容器(Feature WindowsHostProcessContainers) |
| gmsa_full.go、gmsa_kubelet.go | Windows GMSA 组托管服务账户认证 |
| hybrid_network.go | 混合网络(Linux 节点 + Windows 节点 Service 互通) |
| eviction.go、node_shutdown.go、reboot_node.go | 驱逐、节点计划关闭、重启等生命周期场景 |
| security_context.go、dns.go、service.go、volumes.go、subpath_cleanup.go、device_plugin.go、kubelet_stats.go、density.go | 安全上下文、DNS、Service、卷挂载、SubPath 清理、Device Plugin、kubelet 统计与密度 |
配套工具函数集中在 utils.go,其中包含几处值得注意的工程细节:
skipUnlessContainerdOneSevenOrGreater:要求 Windows 节点运行 containerd 1.7+,否则跳过(test/e2e/windows/utils.go#L90-L106);windowsTestMemorySafetyBuffer(256 MiB):在内存容量边界测试中预留固定头寸,吸收 kubelet/cgroup 内存记账漂移,避免在容量边界产生 flaky 的OutOfmemory调度错误(test/e2e/windows/utils.go#L108-L119);detectPodOverheadMemory:通过一次 DryRun Pod 创建探测集群准入链是否注入了 Pod Overhead 内存,并缓存结果用于后续容量计算(test/e2e/windows/utils.go#L136-L183)。
五、Density 密度测试:从 e2e_node 借来的 Windows 版
原文档第二节说明:e2e_node/density_test diff —— “该测试借自 e2e_node/density_test,除第一个测试外的其余测试以及部分日志均被省略”。对应实现即 density.go,其核心参数与流程:
5.1 测试参数与性能阈值
dTests := []densityTest{
{
podsNr: 10,
interval: 0 * time.Millisecond,
// percentile limit of single pod startup latency
podStartupLimits: e2emetrics.LatencyMetric{
Perc50: 30 * time.Second,
Perc90: 54 * time.Second,
Perc99: 59 * time.Second,
},
// upbound of startup latency of a batch of pods
podBatchStartupLimit: 10 * time.Minute,
},
}
即:一次性并发创建 10 个 Pod,单个 Pod 启动延迟的 P50/P90/P99 分别不得超过 30s/54s/59s,整批启动不得超过 10 分钟。源码注释明确指出这些值比较宽松(TODO(coufon): the values are generous, set more precise limits with benchmark data),后续将用基准数据收紧。
5.2 执行流程
- 创建测试 Pod:
newDensityTestPods生成 10 个 pause 镜像 Pod,并带NodeSelector: kubernetes.io/os=windows,保证全部调度到 Windows 节点(density.go#L246-L248); - 建立 Watch:
newInformerWatchPod用 client-go 的cache.NewInformer+ ListWatch 按标签type=density_test_pod监听 Pod 状态,当 Pod 变为Running时记录时间戳(density.go#L174-L221); - 批量创建:
createBatchPodWithRateControl为每个 Pod 起一个 goroutine 并发创建,interval用于吞吐控制(当前为 0)(density.go#L164-L172); - 等待与断言:在 10 分钟内等待 Watch 观察到全部 10 个 Pod Running,计算每个 Pod 的端到端延迟与整批延迟(
lastRunning - firstCreate),随后deletePodsSync并发删除并等待所有 Pod 消失。
该用例注册在 framework.WithSerial() + framework.WithSlow() 下(串行且不计入 P0 快速套件),与 README 中“只保留第一个测试”的裁剪说明一致。
六、小结:如何把这套流程用起来
把原文档浓缩为可复现的操作序列:
# 1. 指向含 Windows 节点的集群
export KUBECONFIG=path/to/kubeconfig
# 2. 准备 Windows 镜像清单并交给测试框架
curl https://raw.githubusercontent.com/kubernetes-sigs/windows-testing/master/images/image-repo-list -o repo_list
export KUBE_TEST_REPO_LIST=$(pwd)/repo_list
# 3. 只运行 [sig-windows] 相关 E2E 用例
./e2e.test --provider=local --ginkgo.no-color \
--ginkgo.focus="\[sig-windows\]|\[Feature:Windows\]" \
--node-os-distro="windows"
适用前提:集群中至少存在一个可运行的 Windows 节点(带 kubernetes.io/os=windows 标签);e2e.test 为编译好的 E2E 二进制(可按 test/images/README.md 的说明用 ./build/run.sh make WHAT=test/e2e/e2e.test 编译);KUBE_TEST_REPO_LIST 指向的清单必须包含 Windows 平台可用的测试镜像,否则 Pod 拉取镜像会失败。所有 Windows 专属行为——SIG 标签、Feature 标记、OS 跳过、密度阈值——都可以直接回溯到 test/e2e/windows/ 包内的源码进行验证。
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 StartedRust0631
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
video-shotcraftAI宣传片skill,使用 Remotion 制作电影级产品视频:提供106 张镜头配方卡和可复用的视频魔板。适用于 Claude Code 与 Codex以及所有其他智能体Markdown00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python09
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