news 2026/9/8 16:26:12

Kubernetes DRA 动态资源分配升级/降级端到端测试套件(test/e2e_dra)运行指南与源码解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Kubernetes DRA 动态资源分配升级/降级端到端测试套件(test/e2e_dra)运行指南与源码解析

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 与代码可以归纳为三点:

  1. 需要make test而非make test-integrationmake test-integration体系假定由测试框架自己管理 etcd;而这里的local-up-cluster.sh本身就想自行启动 etcd,二者会冲突。README 明确指出 “make testinstead ofmake test-integrationis intentional”。
  2. 需要宿主机级权限:脚本要创建/清理/var/run/kubernetes/var/lib/kubelet/plugins等系统目录并修改属主,普通集成测试不具备该前提。
  3. 需要跨版本下载与版本判定:套件要从远端拉取上一个次版本的 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-dirlinx疑为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_DIRKUBERNETES_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,从而得到majorpreviousMinor = 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})dirKUBERNETES_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-implicitDRA 扩展资源(ExtendedResource)两种资源类型路径(显式/隐式)extendedresources_test.go
pool-name-field-selector-fallbackResourceSlice 池名字段选择器的回退逻辑(该子测试额外把驱动配置为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:分别提供compatibilityGroupsGateCycleworkloadResourceClaimsGateCycle,从命名与实现结构可以推断它们测试的是“门控(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组的v1beta1v1beta2v1alpha3三个 API 版本。这与升级/降级测试的诉求强相关:上一版本集群可能只提供旧版本 API,而当前源码可能已推进到新版本 API,三者并存才能让新旧二进制与客户端在 skew 期间互操作(注意本地运行的 driver 本身是“本地文件系统模式”,因此注释说明无需ALLOW_PRIVILEGED=1)。
  • FEATURE_GATES显式打开涉及升级路径的 DRA 特性门控。以当前仓库 pkg/features/kube_features.go 中记录的版本生命周期为参照,可看出该套件刻意覆盖“默认值/状态跨版本会发生变化”的特性:
Feature Gate演进轨迹(出自本仓库 kube_features.go)
DynamicResourceAllocationDRA 的总开关(GA 于 1.36 并 LockToDefault)
DRADeviceTaints1.33 Alpha(默认关)→ 1.36 Beta(默认开)→ 1.37 GA(默认开、未锁默认值)
DRADeviceTaintRules1.35 Alpha(默认关)→ 1.36 Beta(依赖一个默认关闭的 Beta API,故仍默认关)→ 1.37 GA 默认开
DRAExtendedResource1.34 Alpha(默认关)→ 1.36 Beta(默认开)→ 1.37 GA 并 LockToDefault(计划 1.40 移除)
DRAPartitionableDevices1.33 Alpha(默认关)→ 1.36 Beta(默认开)

这些跨 minor 版本“默认值翻转”或“API 版本迁移”正是升级/降级测试要捕捉的回归点:例如当旧版本默认关闭某 gate、新版本默认开启时,升级前后对 ResourceSlice、ResourceClaim 语义的处理必须一致,kubelet 全量清空并重建 ResourceSlice 的机制也需要在两侧都成立。

常见问题与实战要点

综合 README、run.sh 与测试源码,运行与扩展本套件时有几个高价值经验:

  1. 不要对单个子测试的运行顺序做假设:子测试存于 map、以 map 遍历方式执行,顺序不定;新增子测试时请保证自包含并自备 driver 与设备。
  2. ResourceSlice 会被 kubelet 整体清空:每次升级/降级导致 kubelet 重启后,所有已发布 ResourceSlice 都被抹掉。若你的子测试自己管理 slice,必须在回调中自行等待重建,而不是假设它还在。
  3. 陈旧连接需要重试:apiserver 重启后客户端连接会残留,清理对象时请用Eventually重试包裹,容忍EOF与偶发 403(coredra_test.go 是现成范例)。
  4. 依赖下载可能较慢:首次运行会下载上一 minor 的 server tarball,务必设置-timeout=1h级别的超时;多次迭代调试验证时设置KUBERNETES_SERVER_CACHE_DIR可显著提速。
  5. 调试技巧dlv test ./test/e2e_dra -- -test.v可断点调试;ktesting.SetDefaultVerbosity(2)设置了默认日志级别,注释提示驱动操作排障时可用-v=5查看更多细节(见 upgradedowngrade_test.go)。
  6. 日志定位:不设ARTIFACTS时日志落在测试临时目录;设置后落在指定目录,且 localupcluster.go 会在失败时保留集群现场供排查。
  7. 宿主目录权限是最大坑点/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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/8 16:26:03

Flutter端侧声音克隆TTS实战:sherpa-onnx + ZipVoice离线方案

如果你正准备在 Flutter 里做一款带“声音克隆”能力的离线文字转语音应用&#xff0c;也就是用户录几秒钟自己的声音&#xff0c;然后 App 就能用这个音色把文字读出来&#xff0c;那这套组合应该是当前社区里少见的、能完整跑通的端侧方案&#xff1a;sherpa-onnx 负责离线 T…

作者头像 李华
网站建设 2026/9/8 16:25:49

STM32C5轮询读取LSM6D3TR-C陀螺仪数据:从寄存器配置到物理量换算

1. 项目概述与选型背景1.1 这颗芯片和传感器组合的来龙去脉STM32C5是意法半导体近期主推的入门级Cortex-M33内核MCU系列&#xff0c;主频能跑到250MHz级别&#xff0c;片内集成FPU和DSP指令集&#xff0c;放在几年前这配置妥妥是中高端定位&#xff0c;现在下放到入门系列&…

作者头像 李华
网站建设 2026/9/8 16:24:45

装饰器: 在不改变原函数的基础上, 动态给函数增加功能

面向对象 之 初识 是类之内置的一个装饰器, 的作用在于经由过程特定体式格局, 把一个按正常应当以方法情势调用的执行体, 转化为以属性情势来操控调用。 装饰器: 在不改变原函数的基础上, 动态给函数增加功能 很多装饰器的些许细节, 之前已然开展过讨论, 其本质是借助变量的…

作者头像 李华
网站建设 2026/9/8 16:23:44

前端转AI大模型开发:小白也能轻松入门的收藏必备学习路线!

本文为前端开发者提供了从前端逐步转向AI大模型应用开发的实用指南。作者结合自身近一年的经验&#xff0c;建议前端开发者不必死磕算法或研究训练&#xff0c;而是应优先进入“大模型应用开发”赛道。文章详细介绍了四个学习阶段&#xff1a;基础补齐&#xff08;Python编程&a…

作者头像 李华
网站建设 2026/9/8 16:23:03

Vibe Kanban:用看板管理AI编程助手的任务与上下文

过去一年&#xff0c;我绝大部分编码工作都是和AI编程助手结对完成的。代码生成、重构、补测试&#xff0c;模型干得很漂亮&#xff0c;但我的效率瓶颈却跑到了“会话”上&#xff1a;每次新开一个对话&#xff0c;都要把项目背景、模块入口、技术约束重新喂一遍&#xff1b;如…

作者头像 李华
网站建设 2026/9/8 16:20:59

常见的Python解释器,以及如何判断当前使用的是哪个解释器?

用于执行代码的程序是解释器, 它是语言核心组件之一, 负责读取代码, 负责解释代码, 负责运行代码, 解释器有几种不同实现, 每种实现都有其独特特点, 每种实现都有其独特用途。以下是一些主要的 解释器&#xff1a;1. 这是最为普遍的解释器, 该解释器是由软件基金会所开发的, 它…

作者头像 李华