Kubernetes DRA 动态资源分配升级/降级端到端测试套件(test/e2e_dra)运行指南与源码解析
【免费下载链接】kubernetesProduction-Grade Container Scheduling and Management项目地址: https://gitcode.com/GitHub_Trending/kuber/kubernetes
Kubernetes 官方主仓库在test/e2e_dra目录维护了一套面向 DRA(Dynamic Resource Allocation,动态资源分配)的跨版本升级/降级测试套件:它通过真实运行 kube-apiserver、kubelet 等二进制,先拉起上一个次版本(previous minor release)的集群,再原地升级到当前源码、随后回滚降级,以验证 DRA 各特性在版本演进中的前后兼容性。本指南基于该目录下的 README 并结合源码实现,完整梳理其定位、前置条件、三类运行方式与一键脚本、内部编排机制、子测试功能覆盖面与 Feature Gate 使用,帮助你在本地复现这类测试并理解其设计意图。
这套测试套件是什么:不是普通 e2e,也不是普通集成测试
从 README 的定义看,test/e2e_dra是一个“自带自动升级/降级测试”的 DRA 测试套件:
- 在概念上它类似于集成测试——会启动/停止集群组件(kube-apiserver、kubelet、etcd 等),并针对这些组件运行测试;
- 但它拥有独立目录,因为它的启动方式与其他集成测试、单元测试不同,行为上更像一套E2E(端到端)套件。
两者最核心的差异在于启动方式:普通集成测试在进程内构造 apiserver,而该套件是直接运行真实的 Kubernetes 可执行文件(actual binaries),并依赖 hack/local-up-cluster.sh 提供集群启停的逻辑与配置步骤。因此,该脚本对宿主机有额外的权限与准备要求(详见下文“环境准备”)。测试入口定义在 test/e2e_dra/upgradedowngrade_test.go,以标准 Go 测试函数TestUpgradeDowngrade形态存在,无需 ginkgo CLI 即可调用。
为什么必须独占目录、不能并入 test-integration
该目录被刻意独立出来的原因,从 README 与代码可以归纳为三点:
- 需要
make test而非make test-integration:make test-integration体系假定由测试框架自己管理 etcd;而这里的local-up-cluster.sh本身就想自行启动 etcd,二者会冲突。README 明确指出 “make testinstead ofmake test-integrationis intentional”。 - 需要宿主机级权限:脚本要创建/清理
/var/run/kubernetes、/var/lib/kubelet/plugins等系统目录并修改属主,普通集成测试不具备该前提。 - 需要跨版本下载与版本判定:套件要从远端拉取上一个次版本的 server 二进制包,再与本地的当前源码版本做对照(见后文“升级/降级编排机制”),这种能力无法在单进程集成测试里模拟。
辅助启动逻辑集中在test/utils/localupcluster包(localupcluster.go、cmd.go),测试代码通过localupcluster.New()创建集群句柄,调用Start/Modify/Stop控制集群生命周期。
环境准备与前置条件
README 给出的前置条件可拆解为四步,顺序执行即可:
1. 确保 hack/local-up-cluster.sh 可用
local-up-cluster.sh是整个测试的基础设施,必须能在你的机器上独立工作:
sudo必须可用(脚本需要 root 权限执行清理与建目录);- 按你的环境设置必要环境变量(如代理、网络等)。
2. 确保三个关键目录可写
测试驱动(DRA driver)以本地文件系统模式运行并挂载相关 socket 与设备路径,因此以下目录必须对运行测试的用户可写:
/var/lib/kubelet/plugins/var/lib/kubelet/plugins_registry/var/run/cdi
这三个路径分别对应 kubelet 插件(CSI/DRA 插件注册)与 CDI(Container Device Interface,容器设备注入)的运行目录。也可以直接使用仓库自带的 test/e2e_dra/run.sh,它会用sudo完成清理、建目录与chown(详见下文)。
3. 用 make 构建二进制
make构建产物默认输出到_output/local/bin/linux/amd64(取决于你的GOOS/GOARCH)。测试运行时 kube-apiserver、kubelet 等实际组件二进制就取自这里。
4. 导出测试所需环境变量
| 环境变量 | 必填 | 含义 |
|---|---|---|
KUBERNETES_SERVER_BIN_DIR | 是 | 指向make构建出的 server 二进制目录,如$(pwd)/_output/local/bin/linux/amd64(按你的 GOOS/GOARCH 调整)。测试入口若发现该变量为空,会直接以 Fatal 退出(见 upgradedowngrade_test.go) |
KUBERNETES_SERVER_CACHE_DIR | 否 | 下载的旧版本 release 二进制的缓存目录(如<bin_dir>/cache-dir)。设置后,多次测试调用间可复用已下载的 release 二进制,避免重复下载;未设置时每次使用临时目录 |
ARTIFACTS | 否 | 组件日志文件的持久化存放目录;未设置时日志写入测试的临时 tmp 目录。从 localupcluster.go 看,若设置且测试失败,集群现场(如 kind 目录)会被保留供排查 |
缓存目录的机制细节可见 upgradedowngrade_test.go:测试会按“上个版本的版本号”在缓存目录下建子目录,并在子目录中已存在组件二进制时直接跳过下载解压步骤。另需注意,README 示例中的缓存路径写的是.../linx/amd64/cache-dir(linx疑为linux的笔误),而官方脚本 run.sh 使用$(go env GOOS)/$(go env GOARCH)动态生成路径,可直接照搬。
三种运行方式
方式一:以 Go test 直接运行(推荐)
go test -v -count=1 -timeout=1h ./test/e2e_dra要点:不需要 ginkgo CLI。-count=1确保每次调用都真实执行(默认 Go 会缓存测试结果);-v使测试输出在运行时即可见。
方式二:在 dlv 调试器中运行
dlv test ./test/e2e_dra -- -test.v适合在断点调试 DRA 驱动或 apiserver 交互逻辑时使用;--之后的-test.v会被透传给测试二进制。
方式三:通过 make test 接入仓库标准测试框架
make test KUBE_TIMEOUT=-timeout=1h WHAT=test/e2e_dra FULL_LOG=true KUBE_TEST_ARGS="-count=1"- 刻意使用
make test而非make test-integration,原因正如上文所述:local-up-cluster.sh自己会启动 etcd; KUBE_TIMEOUT=-timeout=1h提供充裕的超时(整套流程含下载旧版本、三次集群生命周期切换);FULL_LOG=true对应-test.v,让输出实时可见;KUBE_TEST_ARGS="-count=1"关闭测试缓存。
一键脚本 run.sh
为简化“从零开始”的繁琐清理与权限操作,仓库提供了 test/e2e_dra/run.sh,它负责:清理旧状态 → 设置权限 → 再执行命令行指定的任意命令:
./test/e2e_dra/run.sh go test ./test/e2e_dra从脚本源码看,它实际做了这些事:
sudo rm -rf /var/run/kubernetes /var/run/cdi \ /var/lib/kubelet/plugins_registry /var/lib/kubelet/plugins \ /var/lib/kubelet/*_state /var/lib/kubelet/checkpoints /tmp/artifacts sudo mkdir /var/run/kubernetes /var/lib/kubelet/plugins_registry \ /var/lib/kubelet/plugins /var/run/cdi sudo chown "$(id -u)" /var/run/kubernetes /var/lib/kubelet/plugins_registry \ /var/lib/kubelet/plugins /var/run/cdi ARTIFACTS=/tmp/artifacts KUBERNETES_SERVER_BIN_DIR="$(pwd)/_output/local/bin/$(go env GOOS)/$(go env GOARCH)" KUBERNETES_SERVER_CACHE_DIR="${KUBERNETES_SERVER_BIN_DIR}/cache-dir" ... exec "$@"即它会自动:删除并重建 kubelet 的 plugins/plugins_registry、/var/run/kubernetes、/var/run/cdi,清掉 kubelet 状态与 checkpoint、临时 artifacts;把上述目录属主改为当前用户;把ARTIFACTS固定为/tmp/artifacts并自动导出KUBERNETES_SERVER_BIN_DIR与KUBERNETES_SERVER_CACHE_DIR;最后exec "$@"原样执行你传入的命令。因此最稳妥的复现姿势是:
./test/e2e_dra/run.sh make ./test/e2e_dra/run.sh go test -v -count=1 -timeout=1h ./test/e2e_dra升级/降级编排机制:主测试函数的源码级拆解
真正驱动整套流程的是 upgradedowngrade_test.go 中的testUpgradeDowngrade。将其按阶段拆开来看,测试流程大致如下:
判定当前源码版本 → 确定"上一个次版本" │ ▼ 下载并解压上个次版本的 server 二进制(cache 命中则跳过) │ ▼ 用上个次版本二进制启动集群(0-initial-<maj>.<min>) │ ▼ 阶段一:各子测试在"旧版本"上运行,返回"升级后继续"的回调 │ ▼ 用当前源码二进制热替换组件(1-<gitVersion>) │ ▲ kubelet 重启会清空所有 ResourceSlice → 等待驱动重新发布 ▼ 阶段二:各子测试在"升级后集群"上运行,返回"降级后继续"的回调 │ ▼ 回滚到上个次版本二进制(2-restored-<maj>.<min>) │ ▲ 再次等待 ResourceSlice 重建 ▼ 阶段三:各子测试在"降级后集群"上收尾/验证1. 判定“上一个次版本”与 alpha.0 特例
测试用sourceVersion执行 hack/print-workspace-status.sh 解析出当前源码的gitVersion,从而得到major与previousMinor = minor - 1(源码位置)。存在一个值得注意的版本号特例:
若 gitVersion 形如
x.y.z-alpha.0(即下一个开发周期刚开始、master 已把版本号提前),则会把previousMinor再减一。
代码注释给出的理由有两点:其一,在-rc.0左右的代码冻结期,master 已自报为下一版本的-alpha.0,若不特判就会悄悄改变原本已验证过的版本偏移(version skew)组合,历史上确实因此坏过一次;其二,新周期早期与上一版本的差异很小,往前多退一个版本更能测出有意义的差异。
2. 获取上一个次版本的 server 二进制
随后通过serverDownloadURL(源码位置)构造 Kubernetes 官方 release 下载地址:先按stable前缀请求<major>.<previousMinor>的版本号文本文件,若返回 404(该 minor 尚无 stable 发布)则回退尝试latest前缀,最终得到 server tarball 下载 URL。下载解压时,代码只从中挑选出localupcluster.KubeClusterComponents列出的集群组件(kube-apiserver、kubelet 等)写入binDir(源码位置)。
3. 三层回调:initial → upgraded → downgraded
这是本套件最有特色的编码模式。每个子测试不是一个孤立的函数,而是一串“链式回调”(类型定义见源码):
type initialTestFunc func(tCtx ktesting.TContext, builder *drautils.Builder) upgradedTestFunc type upgradedTestFunc func(tCtx ktesting.TContext) downgradedTestFunc type downgradedTestFunc func(tCtx ktesting.TContext)- 阶段一执行
initialTestFunc,在旧版本集群上做一轮验证,然后返回阶段二函数; - 阶段二在升级后的集群上执行返回的函数并再次返回阶段三函数;
- 阶段三在降级回滚后的集群上执行最终函数(通常做清理与终态校验)。
由于每个子测试都要在三种集群状态下接力,builder被配置为子测试结束时不删除对象(b.SkipCleanup = true),让 ResourceClaim、Pod 等对象存活到整个测试结束(见 upgradedowngrade_test.go)。驱动以IsLocal = true的“本地文件系统模式”运行,socket 在本机直接打开,避免因 apiserver 重启导致经由代理的间歇性错误与延迟。
4. 升级/降级的集群操作
- 用旧版本二进制首次拉起集群:
cluster.Start(..., "0-initial-<maj>.<min>", binDir, env, ""); - 升级:
cluster.Modify(..., "1-"+gitVersion, ModifyOptions{BinDir: dir}),dir即KUBERNETES_SERVER_BIN_DIR指向的当前源码产物目录(源码位置)。代码注释说明:暂未细分为“先升 apiserver → 控制面组件 → 再重启 kubelet”,而是整体替换后做前后对比(文件内留有TODO); - 降级:调用
cluster.Modify(..., "2-restored-<maj>.<min>", restoreOptions)用保存的选项回滚(源码位置); - 每次
Modify后,由于kubelet 重启后不记得之前有哪些驱动在运行,会清空全部 ResourceSlice,因此框架调用waitForSlices轮询(最长 5 分钟)等待各驱动的 ResourceSlice 重建完成(waitForSlices 源码)。子测试若声明driverResources == nil,则需自行等待 slice 重建。
5. 子测试的运行顺序与隔离性
子测试注册在一个 Go map 中(源码位置),而map 迭代顺序是不确定的,因此 README/注释都强调:子测试之间运行顺序不定,每个子测试必须自包含——它们共享同一个集群,但各自拥有独立的 driver 与设备,互不依赖。
子测试覆盖的 DRA 功能面
主测试在三个阶段统一驱动以下子测试,各自对应目录下的独立文件:
| 子测试名 | 覆盖功能 | 相关文件 |
|---|---|---|
core-dra | 核心 DRA 流程:创建外部 ResourceClaim → 创建引用它的 Pod → 验证 Pod 就绪/设备注入;升级后再建新 Claim+Pod 验证新版本行为;降级后显式清理 | coredra_test.go |
resource-claim-device-status | 资源声明设备状态(ResourceClaimDeviceStatus)在跨版本后的读写/展示 | resourceclaimstatus_test.go |
device-taints | 设备污点(DeviceTaints)规则 | devicetaints_test.go |
partitionable-devices | 可分区设备(PartitionableDevices)的跨版本行为 | partitionabledevices_test.go |
extended-resource-explicit/extended-resource-implicit | DRA 扩展资源(ExtendedResource)两种资源类型路径(显式/隐式) | extendedresources_test.go |
pool-name-field-selector-fallback | ResourceSlice 池名字段选择器的回退逻辑(该子测试额外把驱动配置为ReconcilePoolWithName并使用特权客户端) | poolnamefieldselector_test.go |
以 coredra_test.go 的coreDRA为例,可以看到阶段三清理时因 apiserver 已重启、连接是“陈旧连接”,删除请求偶尔会报EOF甚至出现pods "...": User "kubernetes-admin" cannot get resource ...之类的间歇性 403,因此清理必须放进Eventually重试循环而非一次性调用。这对你自行扩展子测试有直接借鉴意义。
此外,同一目录下还有不参与TestUpgradeDowngrademap 的独立测试:
- featuregatecycle_test.go:入口
TestFeatureGateCycle,用于验证 Feature Gate 开关翻转周期下的 DRA 行为; - compatibilitygroups_test.go 与 workloadresourceclaims_test.go:分别提供
compatibilityGroupsGateCycle、workloadResourceClaimsGateCycle,从命名与实现结构可以推断它们测试的是“门控(gate)被关闭后对应能力的行为”,与前者的 gate cycle 测试构成互补。
启动环境:RUNTIME_CONFIG 与 FEATURE_GATES
测试拉起旧版本集群时注入的环境变量(源码位置)本身即是 DRA 特性配置的权威样例:
localUpClusterEnv := map[string]string{ "RUNTIME_CONFIG": "resource.k8s.io/v1beta1,resource.k8s.io/v1beta2,resource.k8s.io/v1alpha3", "FEATURE_GATES": "DynamicResourceAllocation=true,DRADeviceTaintRules=true,DRADeviceTaints=true,DRAExtendedResource=true,DRAPartitionableDevices=true", }- RUNTIME_CONFIG同时开启
resource.k8s.io组的v1beta1、v1beta2、v1alpha3三个 API 版本。这与升级/降级测试的诉求强相关:上一版本集群可能只提供旧版本 API,而当前源码可能已推进到新版本 API,三者并存才能让新旧二进制与客户端在 skew 期间互操作(注意本地运行的 driver 本身是“本地文件系统模式”,因此注释说明无需ALLOW_PRIVILEGED=1)。 - FEATURE_GATES显式打开涉及升级路径的 DRA 特性门控。以当前仓库 pkg/features/kube_features.go 中记录的版本生命周期为参照,可看出该套件刻意覆盖“默认值/状态跨版本会发生变化”的特性:
| Feature Gate | 演进轨迹(出自本仓库 kube_features.go) |
|---|---|
DynamicResourceAllocation | DRA 的总开关(GA 于 1.36 并 LockToDefault) |
DRADeviceTaints | 1.33 Alpha(默认关)→ 1.36 Beta(默认开)→ 1.37 GA(默认开、未锁默认值) |
DRADeviceTaintRules | 1.35 Alpha(默认关)→ 1.36 Beta(依赖一个默认关闭的 Beta API,故仍默认关)→ 1.37 GA 默认开 |
DRAExtendedResource | 1.34 Alpha(默认关)→ 1.36 Beta(默认开)→ 1.37 GA 并 LockToDefault(计划 1.40 移除) |
DRAPartitionableDevices | 1.33 Alpha(默认关)→ 1.36 Beta(默认开) |
这些跨 minor 版本“默认值翻转”或“API 版本迁移”正是升级/降级测试要捕捉的回归点:例如当旧版本默认关闭某 gate、新版本默认开启时,升级前后对 ResourceSlice、ResourceClaim 语义的处理必须一致,kubelet 全量清空并重建 ResourceSlice 的机制也需要在两侧都成立。
常见问题与实战要点
综合 README、run.sh 与测试源码,运行与扩展本套件时有几个高价值经验:
- 不要对单个子测试的运行顺序做假设:子测试存于 map、以 map 遍历方式执行,顺序不定;新增子测试时请保证自包含并自备 driver 与设备。
- ResourceSlice 会被 kubelet 整体清空:每次升级/降级导致 kubelet 重启后,所有已发布 ResourceSlice 都被抹掉。若你的子测试自己管理 slice,必须在回调中自行等待重建,而不是假设它还在。
- 陈旧连接需要重试:apiserver 重启后客户端连接会残留,清理对象时请用
Eventually重试包裹,容忍EOF与偶发 403(coredra_test.go 是现成范例)。 - 依赖下载可能较慢:首次运行会下载上一 minor 的 server tarball,务必设置
-timeout=1h级别的超时;多次迭代调试验证时设置KUBERNETES_SERVER_CACHE_DIR可显著提速。 - 调试技巧:
dlv test ./test/e2e_dra -- -test.v可断点调试;ktesting.SetDefaultVerbosity(2)设置了默认日志级别,注释提示驱动操作排障时可用-v=5查看更多细节(见 upgradedowngrade_test.go)。 - 日志定位:不设
ARTIFACTS时日志落在测试临时目录;设置后落在指定目录,且 localupcluster.go 会在失败时保留集群现场供排查。 - 宿主目录权限是最大坑点:
/var/lib/kubelet/plugins、/var/lib/kubelet/plugins_registry、/var/run/cdi不可写、或sudo不可用,都会在local-up-cluster.sh阶段失败;直接使用仓库的 run.sh 可规避手工遗漏。
延伸阅读(仓库内路径)
- 套件说明:test/e2e_dra/README.md
- 一键清理/权限/运行脚本:test/e2e_dra/run.sh
- 升级降级主流程与子测试注册:test/e2e_dra/upgradedowngrade_test.go
- 集群生命周期封装(Start/Modify/Stop):test/utils/localupcluster/localupcluster.go、test/utils/localupcluster/cmd.go
- 底层集群启动脚本:hack/local-up-cluster.sh
- 版本号来源脚本:hack/print-workspace-status.sh
- DRA Feature Gate 版本演进定义:pkg/features/kube_features.go
- DRA 驱动的测试辅助工具:
test/e2e/dra/utils(被本套件大量引用)
【免费下载链接】kubernetesProduction-Grade Container Scheduling and Management项目地址: https://gitcode.com/GitHub_Trending/kuber/kubernetes
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考