批量创建代码执行环境、扩容办公助手或启动 RL Rollout 任务前,可以先测试 Agent 集群的工作负载启动速度。本教程在同一个计算节点上依次完成预热、串行启动和并行启动测试,比较单个 Pod 的启动延迟、整批就绪时间和运行稳定性。
测试使用配套工具创建模拟 Agent 应用的 Pod,不需要编译 Cube 或负载发生器。完成后,您将得到两组可对比的结果,以及每轮测试的配置、日志和事件文件。
测试范围与指标
默认工作负载在普通 Linux 容器中执行
sleep infinity,以 Pod 进入 Ready 状态作为启动完成信号。它用于测量轻量工作负载的启动基线,不包含业务初始化、真实 LLM 请求或任务完成时间。测试 | Pod 数量 | 提交速率 | 最大在途创建请求数 | 用途 |
预热 | 1 | 1 QPS | 1 | 准备镜像与运行环境,不计入正式结果。 |
串行启动 | 16 | 1 QPS | 1 | 建立逐个提交工作负载时的基线。 |
并行启动 | 16 | 16 QPS | 16 | 观察批量启动时的整体耗时与尾延迟。 |
串行测试是逐个发出创建请求,并不等待前一个 Pod 完成业务或退出。并行测试约在 1 秒内提交 16 个 Pod,也不是所有请求在同一瞬间发出。
重点比较以下指标:
单 Pod 启动延迟:从开始创建 Pod,到发生器首次观察到该 Pod
Ready,记录 P50、P95、P99 和最大值。整批就绪时间:从批次开始到全部 Pod 就绪,包含按配置速率提交请求的时间。
成功率与稳定性:创建、调度、容器启动和就绪数量是否齐全,以及就绪后 10 秒观察期内是否重启或丢失就绪状态。
计时在集群内的负载发生器中完成。Scheduled、ContainersStarted 和 Ready 均是通过 Watch/List 观察到的状态时间,因此结果包含控制面、调度和状态传播的影响,不能当作 Cube 内部沙箱创建接口的纯执行耗时。
开始前准备
使用专用测试集群或隔离测试节点。工具会创建测试命名空间、权限和 RuntimeClass,并为发生器节点添加标签及
NoSchedule 污点;同一集群不要同时运行多套该测试。项目 | 要求 |
目标节点 | 状态为 Ready,已启用 Cube 运行时,具有 agc.cloud.tencent.com/cube-ready=true 标签;Kubernetes 可分配 CPU 不低于 16 核。 |
目标节点剩余容量 | 能同时容纳 16 个测试 Pod。默认每个 Pod 申请 512m CPU 和 512Mi 内存,还需留出系统开销、临时存储、Pod 槽位、Pod IP 和运行时资源。 |
发生器节点 | 状态为 Ready,与目标节点不同;至少剩余 16100m CPU 和约 5 GiB 内存。 |
操作机 | 使用能连接集群 API 的 Linux 主机,安装 Bash、Git、kubectl、jq、Mike Farah yq v4 及 GNU coreutils(包含 sha256sum)。 |
操作权限 | 使用测试集群管理员身份,允许创建测试资源、权限和 RuntimeClass,以及修改发生器节点标签与污点。 |
镜像访问 | 目标节点能拉取工作负载镜像,发生器节点能拉取工具中固定 digest 的发生器镜像。 |
第一步:下载并校验工具
工具位于 ags-cookbook 的 performance-test-tools 目录,包含运行脚本、清理脚本、工作负载清单和校验文件。本文固定使用
cube-cri-performance-test-tools-v1.0.17 对应的仓库提交,便于重复测试时保持工具一致。在操作机的 Bash 终端中执行。后续步骤继续使用同一终端,以保留参数和每轮测试 ID。
git clone https://github.com/TencentCloudAgentRuntime/ags-cookbook.git agc-performance-cookbookcd agc-performance-cookbookgit checkout --detach 46619b344e56b25b317ff7367ee72201f5af7f14export SCALE_LOAD_DIR="$PWD/benchmarks/agent-cluster/performance-test-tools"export RESULT_ROOT="$PWD/_output/agc-startup-performance"
检查工具和依赖:
kubectl version --clientjq --versionyq --versioncat "$SCALE_LOAD_DIR/VERSION"chmod +x "$SCALE_LOAD_DIR/run.sh" "$SCALE_LOAD_DIR/cleanup.sh"bash -n "$SCALE_LOAD_DIR/run.sh" "$SCALE_LOAD_DIR/cleanup.sh" "$SCALE_LOAD_DIR/sop-functions.sh"(cd "$SCALE_LOAD_DIR" && sha256sum -c SHA256SUMS)test -r "$SCALE_LOAD_DIR/manifests/cube-cri-load-simple-pod.yaml"test -r "$SCALE_LOAD_DIR/manifests/cube-cri-load-runtimeclass.yaml"
确认校验项全部为
OK 后继续。校验失败时重新取得相同提交的完整工具目录,不修改校验文件跳过检查。发生器以容器镜像交付,脚本已固定其 digest,无需本地构建。第二步:设置参数并检查节点
替换 kubeconfig 路径及两个节点名称。工作负载镜像需要包含
sleep 命令,并与节点架构兼容;两组测试使用相同镜像。进行多轮或跨日期比较时,建议固定镜像 digest。export KUBECONFIG='/absolute/path/to/kubeconfig'export TARGET_NODE='REPLACE_WITH_TARGET_NODE'export GENERATOR_NODE='REPLACE_WITH_GENERATOR_NODE'export LOAD_IMAGE='mirror.ccs.tencentyun.com/library/ubuntu:24.04'export TEST_PODS=16export SNAPSHOTTER_PROFILE=overlayfskubectl config current-contextkubectl cluster-infokubectl get nodes -o widekubectl get node "$TARGET_NODE" "$GENERATOR_NODE" -L agc.cloud.tencent.com/cube-readykubectl describe node "$TARGET_NODE"kubectl describe node "$GENERATOR_NODE"
在节点详情中检查
Allocatable、Allocated resources 和现有 Pod,确认剩余资源满足上表要求。节点标称规格不等于可分配资源,可分配资源也不等于尚未使用的资源。执行以下检查;任意一项不通过时,先处理节点选择或权限问题,不继续运行测试:
if [[ "$TARGET_NODE" == "$GENERATOR_NODE" ]]; thenecho '目标节点和发生器节点不能相同。' >&2elif [[ "$(kubectl auth can-i '*' '*')" != yes ]]; thenecho '当前身份没有测试集群管理员权限。' >&2elseecho '节点选择和管理员权限检查通过,请继续核对节点资源及运行环境。'fi
初始化测试命令:
source "$SCALE_LOAD_DIR/sop-functions.sh"
第三步:预热
使用与正式测试相同的工作负载模板和镜像,运行一个 Pod。就绪后观察 10 秒,成功后自动清理该轮资源,结果单独保存在本地。
run_warmup_test
看到
Warm-up succeeded. 后再进行正式测试。预热失败时停止,按输出中的目录保存和检查诊断材料。更换目标节点、镜像或工作负载类型后,需要重新预热。本教程比较预热后的启动表现,不将结果写成首次拉取镜像时的冷启动性能。正式轮次的镜像状态标记不能代替预热步骤。
第四步:串行启动测试
默认工作负载使用
cube-load RuntimeClass,在目标节点上执行 sleep infinity,每个 Pod 的 CPU、内存 requests 与 limits 均为 512m、512Mi。工具自动生成命名空间和唯一 Pod 名称,无需手工修改或提交清单。按 1 QPS、1 worker 提交 16 个 Pod:
run_serial_test "$TEST_PODS"
成功时会输出摘要并自动清理本轮集群资源,本地结果保留。检查
count、created、scheduled、containersStarted、ready 是否均为 16,随后记录延迟分位值、整批就绪时间、重启与 Warning 情况。需要再次查看本轮结果时执行:
show_result "$SERIAL_RUN_ID"
确认本轮成功并完成清理后,再进入并行测试。失败时先排查,不跳过失败轮次继续扩大压力。
第五步:并行启动测试
保持目标节点、工作负载镜像和类型不变,确认节点容量已经释放,再执行:
run_parallel_test "$TEST_PODS"
该命令令
count=qps=workers=16,在约 1 秒内提交 16 个 Pod。workers 表示最大在途创建请求数,实际值取决于请求完成速度。成功时同样会输出结果并清理该轮资源。重新查看结果:
show_result "$PARALLEL_RUN_ID"
第六步:比较结果
show_result 会先校验结果文件的完整性,再输出便于阅读的摘要。按下面的对应关系记录串行与并行两组结果:检查项 | 摘要中的字段 | 如何解读 |
创建和就绪数量 | count、created、scheduled、containersStarted、ready | 对照计划数量,不能只统计成功样本而忽略失败任务。 |
单 Pod 启动耗时 | latencyMs.createToReady | 记录 P50、P95、P99 和最大值,单位毫秒。 |
整批环境就绪 | batchMs.allReady | 包含按速率提交请求的时间,适合衡量整批资源何时可用。 |
实际请求速率 | actualRequestWriteQPS | 核对压力是否接近配置速率。 |
稳定性 | restartCount、readyLost | 结合 10 秒稳定观察期判断是否存在重启或就绪丢失。 |
告警与错误 | warnings、errors | 查看原因和受影响 Pod;最终成功也可能伴随过程中发生的 Warning。 |
工具判定 | success、accepted、slo | 同时查看是否配置延迟阈值,不能只凭 accepted=true 推导达到某个速度。 |
默认教程命令不启用正值延迟 SLO。需要按业务目标判定时,在正式测试前设置
READY_P99_SLO_MS 为所需毫秒阈值,并在结果中核对 slo。该阈值是本次测试目标,不是产品 SLA。原 SOP 的输出示例
以下仅用于说明如何比较结果,来自原 SOP 的示例输出,不是本次教程新增的实测结果,也不是固定达标值。
检查项 | 串行:16 Pod,1 QPS | 并行:16 Pod,16 QPS |
就绪数量 | 16 / 16 | 16 / 16 |
Create → Ready P50 | 约 325 毫秒 | 约 410 毫秒 |
Create → Ready P95 / P99 | 约 357 / 357 毫秒 | 约 1295 / 1295 毫秒 |
Create → Ready 最大值 | 约 357 毫秒 | 约 1295 毫秒 |
整批全部就绪 | 约 15.34 秒 | 约 2.23 秒 |
重启 / 就绪丢失次数 | 0 / 0 | 0 / 0 |
Warning | 0 | 1,原因为 FailedCreatePodSandBox |
串行组的整批时间包含约 15 秒的分批提交间隔;并行组更早提交完全部 Pod,因此整批就绪更快,但单 Pod 的尾延迟可能增加。这是同一运行时在两种提交方式下的比较,不能据此推导不同运行时的性能差异。16 个样本的 P99 也不能用于估算大规模长期尾延迟。
保存测试记录
每轮结果位于
$RESULT_ROOT/<run-id>/,重点保存:文件 | 用途 |
batch-summary.json | 完整结果摘要;与 show_result 展示结构不同,后者做了字段整理。 |
run-config.json | 本轮节点、镜像、并发、工作负载类型等参数。 |
pods.jsonl | 单 Pod 观测记录。 |
generator.log | 发生器日志。 |
warning-events.json | 工作负载 Warning 事件。 |
artifacts.sha256 | 结果文件校验信息。 |
同时记录集群及运行时版本、节点规格与架构、测试期间的已有负载和工具版本。需要增加样本量时,在每轮完成清理后重复测试;不要把不同镜像、工作负载类型或缓存条件的样本混在一起。
失败排查与资源清理
现象 | 优先检查 |
发生器 Pod 无法调度 | 发生器节点剩余 CPU、内存、污点及镜像拉取条件。 |
工作负载 Pending | 目标节点资源、Pod IP、Pod 数量上限、节点选择条件和工作区配置。 |
FailedCreatePodSandBox | 目标节点的 Cube 运行环境、运行时资源和相关事件。 |
镜像拉取失败 | 镜像地址、架构、仓库权限和节点网络。 |
延迟明显增大 | 镜像是否预热、节点其他负载、实际提交速率及调度和就绪阶段的耗时。 |
结果校验失败或文件不完整 | 保留已有目录,检查发生器退出状态及文件回传,不作为有效性能样本。 |
提示已有测试资源 | 先查清资源归属,结束并清理前一轮,不覆盖其他测试。 |
成功轮次会自动清理;失败、中断或资源残留时,使用对应的精确 run-id 清理。例如清理当前终端中失败的并行轮次:
cleanup_test "$PARALLEL_RUN_ID"
预热和串行轮次分别使用
$WARMUP_RUN_ID、$SERIAL_RUN_ID。终端重开后,从保存的配置或终端记录中取得实际 run-id,重新加载参数与函数后再清理,不猜测或复用 ID。所有轮次结束且不再有测试 Pod 后,清理共享资源和发生器节点标记:
cleanup_shared_test_resources
工具会检查资源归属和残留 Pod;检查不通过时停止清理,不应通过修改标签绕过。清理后核对发生器节点上的测试标签和污点已经移除。本地
$RESULT_ROOT 不会被删除,集群和节点池也不会被该命令释放;测试环境不再使用时,另按 删除集群 处理。进阶测试
使用更接近业务的工作负载
默认
simple 类型只有轻量常驻进程。需要观察初始化和健康检查的影响时,使用包含 Node.js 的镜像,并在三步中一致指定 production:export LOAD_IMAGE='mirror.ccs.tencentyun.com/library/node:22-bookworm-slim'run_warmup_test --workload-profile productionrun_serial_test "$TEST_PODS" --workload-profile productionrun_parallel_test "$TEST_PODS" --workload-profile production
以上命令应逐条执行,每一步成功并清理后再继续。该类型包含 init container、主服务、Sidecar、volume、HTTP/TCP 健康检查和
NET_ADMIN,执行前需确认测试环境允许这些权限和资源配置。完整清单位于工具目录的 manifests/cube-cri-load-pod.yaml。此时
Ready 更接近“服务可以接收请求”,但仍不包含真实模型调用或完整 Agent 任务。其结果应与 simple 分开记录;真正的业务启动指标还需由业务镜像和就绪检查定义。测试 EROX 镜像加速
export SNAPSHOTTER_PROFILE=eroxexport LOAD_IMAGE='REPLACE_WITH_EROX_IMAGE@sha256:REPLACE_WITH_DIGEST'run_warmup_testrun_serial_test "$TEST_PODS"run_parallel_test "$TEST_PODS"
每一步成功后再继续。切换参数本身不会安装 EROX 或转换镜像。EROX 与 overlayfs 的结果分开记录;镜像身份差异用于诊断,不单独作为该工具的验收条件。
扩大规模
先核算节点剩余资源,再逐级增加
TEST_PODS。默认每个 simple Pod 申请 512m CPU、512Mi 内存和一个 Pod IP,还需检查节点 Pod 数上限及 Cube/NBD 等运行时资源。工具中的 cube-load RuntimeClass 未额外声明资源 overhead,这不代表运行时本身没有资源开销。每档规模至少重复两轮,保持并行组
count=qps=workers=TEST_PODS。上一档出现失败时先排查;需要更多样本时优先清理后重复测试,不超出单节点剩余承载量。