借助 Google Kubernetes Engine (GKE),您可以扩缩 GKE 上的 Agent Substrate 集群,以支持高并发 AI 智能体工作负载,同时最大限度地降低闲置计算成本。通过将专用系统基础架构节点池与自定义 ComputeClass 和容量缓冲区相结合,您可以隔离关键系统组件、跨 Compute Engine 机器系列自动执行硬件回退,并保持备用工作器容量处于预热状态。
阅读本文档,了解如何调整 GKE 集群组件的大小、隔离系统基础设施和数据库节点池、为 WorkerPool 资源配置自动节点预配,以及规划集群维护和升级。本文档适用于在 GKE 基础设施上预配和管理 Agent Substrate 的平台管理员。
准备工作
在配置集群基础架构以进行扩缩之前,请确保您的环境满足以下要求:
- GKE 集群模式和版本:您必须使用 GKE Standard 集群,其中控制平面和节点池都运行 1.37 版或更高版本。Agent Substrate on GKE 不支持 GKE Autopilot 集群。
- Agent Substrate on GKE 安装:您必须已安装 Agent Substrate on GKE,或计划在启用系统基础设施节点隔离的情况下安装 Agent Substrate on GKE。如需了解安装步骤,请参阅在 GKE 上安装 Agent Substrate。
合理调整 Agent Substrate 集群的大小
根据同时观看人数峰值和峰值恢复率(而非平均值)确定集群大小。工作器节点必须能够容纳同时运行的最大 Actor 数量,并且系统基础设施组件和存储路径必须跟上 Actor 恢复和暂停的最快速度。
估算工作负载峰值
如果您没有生产测量数据,则可以根据注册的 Actor 总数 (total_actors)、同时运行的 Actor 的平均比例 (average_active_fraction)、流量突发倍数 (burst_factor) 以及 Actor 每次保持活跃状态的平均时长 (average_seconds_active_per_resume) 来估算并发峰值和恢复速率:
peak_running_actors = total_actors × average_active_fraction × burst_factor
peak_resume_rate = peak_running_actors / average_seconds_active_per_resume例如,如果您的 Actor 总数为 100,000,平均活跃率为 10%(10,000 个 Actor),突发系数为 2,则您的集群必须支持 20,000 个峰值运行 Actor。如果每个 Actor 在挂起之前平均运行 100 秒,那么维持该峰值需要每秒恢复 200 次,每秒挂起 200 次。
GKE 上的 Agent Substrate 不会自动暂停空闲 Actor;您的应用或编排器必须触发暂停。
确定数据平面的大小
本部分介绍了调整数据平面大小的最佳实践,包括工作器 Pod、GKE 节点、快照带宽和 atelet 节点代理。
计算工作器 Pod 和 GKE 节点
WorkerPool 中的每个工作器 Pod 都托管多个 Actor。如果工作器托管的 Actor 数量少于 ateworker 容器上的 --max-actors 标志设置的限制(默认值为 1000),并且有足够的剩余 CPU 和内存容量来满足 ActorTemplate 限制,则该工作器可以接受新的 Actor。如需详细了解 Worker 限制如何与 Actor 限制交互,请参阅开源在 Agent Substrate API 中调整资源大小指南。
计算具有 20% 可用调度容量(0.8 因子)的工作器 Pod 的所需数量:
slots_per_worker = min(max_actors, worker_memory / actor_memory, worker_cpu / actor_cpu)
total_workers = peak_running_actors / (slots_per_worker × 0.8)在此公式中,max_actors 变量默认为 1000(由 --max-actors 标志配置)。从 worker_memory 值中减去工作器为其自身进程预留的内存。对于 microVM WorkerPool 资源,请将 Worker 报告的每个 Actor 的虚拟机监视器 (VMM) 内存开销添加到 actor_memory 值中,因为放置会将该开销计入 Worker 容量。
每个节点有一个工作器 Pod,工作器数量等于 GKE 工作器节点数量。请勿将节点的可分配 CPU 和内存 100% 分配给工作器 Pod;请在每个节点上为 atelet DaemonSet(位于 ate-system 命名空间中)和 GKE 系统守护程序留出容量。GKE Standard 支持每个集群最多 65,000 个节点,每个节点最多 512 个 Pod。默认上限为每个节点 110 个 Pod(可通过 --max-pods-per-node 标志进行配置),大型集群还有其他要求。
调整快照带宽和节点磁盘的大小
每次 Actor 暂停时,都会将快照写入 /var/lib/ate 目录路径下的本地节点磁盘。在 SYNCHRONOUS 模式下,atelet 代理会在挂起操作返回之前将快照上传到 Cloud Storage。在 ASYNCHRONOUS 模式下,atelet 代理会在拍摄快照后延迟大约 --snapshot-upload-delay 标志值(默认值为 5m)的时间,然后安排上传到 Cloud Storage。Agent Substrate 会在节点上缓存最新快照,缓存时长由 --snapshot-detach-delay 标志配置,默认值为 30m。在该时间段内恢复的 Actor 会调度到同一节点,并从本地磁盘恢复,而不是从 Cloud Storage 下载快照。
将未压缩的快照大小 (snapshot_size) 估算为 ActorTemplate 内存限制加上写入可写根文件系统或持久目录的所有数据。稀疏文件处理和 zstd 压缩可减小在 Cloud Storage 之间传输的压缩快照大小 (compressed_snapshot_size),Agent Substrate 在 Cloud Storage 中为每个 Actor 最多存储两个快照(上一个快照和正在进行的快照):
- Cloud Storage 带宽峰值(每个方向):
peak_resume_rate × compressed_snapshot_size - 每个节点的节点磁盘写入吞吐量峰值:
2 × peak_resume_rate × snapshot_size / total_nodes(恢复下载和暂停检查点都会写入节点磁盘)。
GKE 中的默认 100 GiB hyperdisk-balanced 启动磁盘提供 3,600 IOPS 和 290 MiB/s 的吞吐量(默认性能随磁盘大小而变化),这可能会限制在客户机 I/O 负载较重时的快照操作。创建工作器节点池时,请在 /var/lib/ate/snapshots 目录路径中装载专用磁盘,或通过将 --boot-disk-provisioned-iops 和 --boot-disk-provisioned-throughput 标志与 gcloud container node-pools create 命令结合使用来提高启动磁盘性能。
检查 Security Token Service 配额
每次向 Cloud Storage 传输快照或从 Cloud Storage 传输快照都需要一个限定于 Actor 的令牌,ate-api-server 组件会从 Security Token Service (STS) API 请求该令牌。ate-api-server 组件每 20 到 40 分钟(通常是在该时间段内的首次恢复时)为每个 Actor 发出最多一个 STS 请求,并重复使用该令牌,直到令牌过期前 20 分钟。
STS Exchange token global requests 配额默认为每个项目每分钟 6,000 个请求,并与 Workload Identity Federation for GKE 共享:
- 所需的 STS 配额(每分钟请求数):估计为上次快照转移时间超过 20 分钟前的 Actor 的恢复峰值速率的 60 倍,加上 Workload Identity Federation for GKE 的容量。如果您不知道该费率,请使用
60 × peak_resume_rate公式作为后备方案。例如,每秒 200 次恢复需要每分钟最多 12,000 个请求。 - 如何提高配额:在 Google Cloud 控制台的配额和系统限制页面上申请提高配额。即使项目中未启用 Security Token Service API,快照转移作业也能正常运行,但您必须启用该 API,配额才会显示在配额和系统限制页面上。如需了解详情,请参阅 Workload Identity Federation for GKE 文档中的 Security Token Service 中 Exchange Token API 的配额。
调整 atelet 节点代理
atelet DaemonSet 会在每个工作器节点上运行一个 Pod,以恢复 Actor 并为其创建检查点。在每个 atelet Pod 中,objectstore-plugin 容器会压缩快照并将其传输到 Cloud Storage 或从 Cloud Storage 传输快照。
下表介绍了 atelet 和 objectstore-plugin 容器的关键大小设置:
| 设置 | 默认 | 尺码指南 |
|---|---|---|
atelet 和 objectstore-plugin 容器 CPU 和内存
|
请求:50 mCPU,128 MiB 内存;无限制 |
通过省略两个容器的内存限制,配置没有内存限制的可突发 QoS,因为达到内存限制会停止节点代理并暂停节点上的所有恢复。提高 CPU 和内存请求,以匹配在峰值恢复速率下完整节点上测得的峰值用量,这样工作器 Pod 就不会消耗 atelet 代理所需的资源。
|
--upload-memory-budget 标志(objectstore-plugin 容器)和 --snapshot-upload-workers 标志(atelet 容器)
|
512 MiB 预算;2 个上传工作器 |
为防止并发快照上传在暂停率较高的多 Actor 节点上排队,请执行以下操作:
|
--drain-timeout 标志和 terminationGracePeriodSeconds 字段
|
5 分钟(--drain-timeout 标志);330 秒(terminationGracePeriodSeconds 字段)
|
控制 atelet 代理在节点排空或升级期间其 Pod 停止时完成正在进行的恢复和快照上传的时间。如果您为大型快照增加了 --drain-timeout 标志的值,则还要增加 atelet DaemonSet 上的 terminationGracePeriodSeconds 字段的值,以便 kubelet 代理在上传完成之前不会停止 atelet 代理。
|
调整系统基础设施组件和数据库的大小
对于 ate-system 命名空间中可横向扩缩的无状态系统组件 Deployment,请使用 35% 的可用容量(0.65 因子)加上一个备用副本 (+ 1) 来估算所需的副本数,以容忍节点中断:
replicas = ceil(peak_load / (capacity_per_replica × 0.65)) + 1在共享依赖项(例如 PostgreSQL CPU 或连接限制)达到容量上限后,横向伸缩将无法再提高吞吐量。同时调整系统基础架构组件和数据库的大小。如需详细了解组件标志,请参阅开源运算符安装指南。
下表介绍了每个系统基础架构组件的资源和伸缩规则:
| 组件 | QoS 和资源规则 | 扩缩和连接调整 |
|---|---|---|
入站流量路由器 (atenet-router) |
配置有保证的 QoS:设置匹配的 CPU 和内存请求及限制,以便 Go 运行时将其线程数与容器 CPU 预算保持一致。 | 根据每秒峰值入站流量请求数、mTLS 开销和请求载荷大小,横向扩缩副本。 |
API 服务器 (ate-api-server) |
配置有保证的 QoS:设置匹配的 CPU 和内存请求及限制,以便 Go 运行时将其线程数与容器 CPU 预算保持一致。 |
根据每秒恢复和暂停周期峰值(每个恢复和暂停周期大约需要 4 毫秒的 CPU 时间)以及入站流量请求查找负载来扩缩副本。明确设置 ateapi.postgres.poolMaxConns 设置。每个副本最多可打开 poolMaxConns + 6 个数据库连接,并且所有副本的总和必须小于 PostgreSQL max_connections 设置。
|
| 数据库 (PostgreSQL) |
配置具有保证内存的可突发 QoS:将内存请求设置为等于限制,以将 shared_buffers 缓存锁定在内存中,并帮助防止内存不足 (OOM) 逐出。在专用 ate-postgres 节点上运行时,请省略 CPU 限制,以免内核 CPU 限制减慢检查点和自动清理突发。
|
纵向扩缩此单实例 StatefulSet。预配足够的 CPU,以在峰值负载下保持低于 80% 的利用率;预配足够的 RAM,以缓存 Actor 表(将 shared_buffers 参数设置为大约 25% 的内存);以及在安装时使用 ateapi.postgres.storageClass 设置来设置 SSD 支持的 StorageClass(例如,C4 或 N4 上的 dynamic-rwo StorageClass,或 C3 或 N2 上的 premium-rwo StorageClass)。增加 postgres-config ConfigMap(postgresql.conf 文件)中的 max_connections 参数,以适应所有 ate-api-server 副本。
|
出站代理 (atenet-egress) |
资源规则:设置 512 mCPU 的 CPU 请求(限制为 8 vCPU)和 512 MiB 的内存请求(限制为 1 GiB)。如果出站 Actor 流量的延迟时间增加,请增加请求和限制。 | 根据 Actor 出站请求流量横向扩缩副本。 |
工作器控制器 (ate-controller) |
资源规则:保留一个副本(无领导者选举)。根据大型 WorkerPool 横向扩容事件期间的峰值用量设置 CPU 和内存请求。 |
如果新的工作器 Pod 在大型舰队中注册的时间过长,请增加 --worker-sync-concurrency 标志的值(默认值为 2)。
|
证书控制器 (podcertificate-controller)
|
资源规则:在 podcertificate-controller-system 命名空间中运行,用于为新的工作器 Pod 签署 mTLS 证书,然后这些 Pod 才能接受 Actor。
|
对于包含数千个工作器的队列,请增加 podcert.workersPerSigner 设置(默认值为 0,即每个签名者对应 1 个工作器,这是清单默认值)、--kube-api-qps 标志(默认值为 5)和 --kube-api-burst 标志(默认值为 10)的值。
|
身份提供方 (ate-idp-server) |
资源规则:在 ate-system 命名空间中提供 OpenID Connect (OIDC) 发现文档和 JSON Web 密钥集 (JWKS),用于验证 Actor JSON Web 令牌 (JWT)。根据 JWKS 查询量峰值设置 CPU 和内存请求。
|
如果依赖方以高请求速率查询 JWKS 端点而不缓存密钥,则横向扩缩副本。 |
凭证提供程序 (k8s-credential-provider)
|
资源规则:启用 Kubernetes Secret 凭证注入后,在 ate-system 命名空间中运行。根据出站凭据注入量设置 CPU 和内存请求。
|
根据需要在 atenet-egress 代理处注入 Kubernetes Secret 凭据的出站 Actor 请求速率,横向扩缩副本。
|
隔离系统基础架构和工作器节点池
在生产集群中,工作器节点上的高流失率 Actor 调度和快照流量可能会与系统基础架构组件争用 CPU、内存和磁盘 I/O。将 GKE 集群划分为三个不同的节点池层级:
- 数据库节点池(
ate-postgres标签):PostgreSQL StatefulSet 的专用节点池,用于存储所有 Actor 和 Worker 元数据。 - 系统基础架构节点池(
ate-system-infra标签):一个专用节点池,用于其余系统基础架构服务(atenet-router、ate-api-server、atenet-egress、ate-controller、ate-idp-server和k8s-credential-provider组件)。 - 工作器节点池 (WorkerPool):由 GKE 自定义 ComputeClass 和 GKE 容量缓冲区管理的动态扩缩节点池,用于托管活跃的 Actor 沙盒。
在 GKE 上安装 Agent Substrate 时,您可以使用 cordonSystemInfraComponents 设置(或 --cordon-system-infra-components 标志)将数据库和系统基础设施组件隔离到专用节点上。在安装之前,您必须创建具有所需标签和 NoSchedule 污点的两个节点池:
# 1. Create the dedicated PostgreSQL node pool
gcloud container node-pools create ate-postgres-pool \
--cluster=CLUSTER_NAME \
--location=LOCATION \
--node-locations=POSTGRES_ZONE \
--machine-type=POSTGRES_MACHINE_TYPE \
--num-nodes=1 \
--node-labels=ate.dev/workloadType=ate-postgres \
--node-taints=ate.dev/workloadType=ate-postgres:NoSchedule
# 2. Create the dedicated system infrastructure node pool
gcloud container node-pools create ate-system-infra-pool \
--cluster=CLUSTER_NAME \
--location=LOCATION \
--machine-type=SYSTEM_INFRA_MACHINE_TYPE \
--num-nodes=2 \
--node-labels=ate.dev/workloadType=ate-system-infra \
--node-taints=ate.dev/workloadType=ate-system-infra:NoSchedule
替换以下内容:
CLUSTER_NAME:GKE Standard 集群的名称。LOCATION:集群控制平面的 Compute Engine 可用区或区域。POSTGRES_ZONE:单副本 PostgreSQL StatefulSet 的单个 Compute Engine 可用区。对于区域级集群,请指定集群区域内的一个可用区,以便节点池在将要创建 StatefulSet 的永久性磁盘的可用区中预配单个节点。对于可用区级集群,请指定集群的可用区。POSTGRES_MACHINE_TYPE:为 PostgreSQL 实例调整大小的 Compute Engine 机器类型。SYSTEM_INFRA_MACHINE_TYPE:为系统基础架构组件调整大小的 Compute Engine 机器类型(例如c3-standard-8机器类型)。
在区域级集群中,--num-nodes 标志适用于每个可用区(例如,在三个可用区中设置 --num-nodes=2 标志总共会创建六个节点)。如需将 ate-system-infra-pool 节点池放置在特定可用区中,请添加 --node-locations 标志。
如果您在安装期间启用 cordonSystemInfraComponents 设置,并且任一节点池缺失,则相应的系统 Pod 将保持 Pending 状态。
使用自定义 ComputeClass 自动定位硬件
借助 GKE 自定义 ComputeClass,您可以为 WorkerPool 节点定义 Compute Engine 机器系列的优先级列表,并在可用区资源不可用时自动回退到备用机器系列。
按运行时选择 Compute Engine 机器系列
为每个沙盒运行时配置单独的 ComputeClass:
- MicroVM 运行时 (Cloud Hypervisor):在 ComputeClass 优先级中设置
enableNestedVirtualization: true字段,并定位到支持嵌套虚拟化的 x86 Compute Engine 机器系列(例如 N4、C3、C4 或 N2),或使用原生运行 KVM 且不需要enableNestedVirtualization字段的 x86 裸金属机器类型。不支持嵌套虚拟化限制中列出的基于 Arm 的虚拟机和机器系列。 - gVisor 运行时:以未启用嵌套虚拟化的通用或计算优化 x86 Compute Engine 机器系列(例如 N4、C3、C4 或 N2)为目标。GKE 上的 Agent Substrate 不支持在混合 CPU 微架构(例如 E2)和基于 Arm 的虚拟机上运行的机器系列。ActorTemplate 选择的所有 WorkerPool 资源都必须共用同一 CPU 架构。
如需详细比较,请参阅选择沙盒运行时。
Agent Substrate 所需的 ComputeClass 规则
在 GKE 工作器节点上为 Agent Substrate 创建 ComputeClass 时,请遵循以下规则:
- 使用基于节点的 ComputeClass:请勿使用基于 Pod 的结算(
podFamily字段),因为该结算模式不支持嵌套虚拟化。 - 请勿设置
nodePoolConfig.sandbox.type: microvm字段:该设置会为基于 Pod 的工作负载启用 GKE Sandbox 的 Kata 运行时,并应用sandbox.gke.io/runtime=microvm污点,从而阻止 Agent Substrate 在 GKE 工作器上进行调度。GKE 上的 Agent Substrate 管理自己的 microVM 运行时,并且仅需要在标准 Container-Optimized OS(cos_containerd映像类型)节点上使用 Compute Engine 嵌套虚拟化。 为高沙盒密度配置 Linux 内核
sysctl设置:如果工作器节点运行大约 800 个以上的并发沙盒,则必须提高文件系统监控(fs.inotify.*参数)和地址解析协议 (ARP) 邻居表(net.ipv4.neigh.default.gc_thresh*参数)的 Linux 内核限制。安装程序仅将这些设置应用于其创建的集群。配置自定义 ComputeClass(使用priorityDefaults.nodeSystemConfig.linuxNodeConfig.sysctls字段)或预先创建工作器节点池(使用--system-config-from-file标志)时,请自行应用以下sysctl值:fs.inotify.max_user_instances:65536fs.inotify.max_user_watches:1048576net.ipv4.neigh.default.gc_thresh1:4096net.ipv4.neigh.default.gc_thresh2:16384net.ipv4.neigh.default.gc_thresh3:32768
如需详细了解如何配置节点
sysctl参数,请参阅 Sysctl 配置选项。
为 microVM WorkerPool 资源选择扩容模式
MicroVM 工作器 Pod 会请求 ate.dev/kvm: 1 扩展资源,该资源仅在节点启动且 atelet 代理启动后由 atelet 设备插件通告。由于 GKE 节点自动预配功能在未预配的节点模板上看不到 ate.dev/kvm 扩展资源,因此待处理的 microVM 工作器 Pod 无法自行触发 GKE 从零个副本自动创建新的节点池。不过,如果节点池已至少有一个正在运行的节点,GKE 就可以扩容该节点池。
下表比较了在 GKE 上伸缩微虚拟机 WorkerPool 资源时支持的模式:
| 模式 | 工作原理 | 可缩减至 0 个节点? | 资源不可用时是否采用多优先级后备机制? | 最佳使用时机 |
|---|---|---|---|---|
具有 minimumCapacity 字段的自动创建的池
|
使用显式 machineType 优先级设置 nodePoolAutoCreation.enabled: true 和 minimumCapacity.targetNodeCount: 1 字段。
|
否(保持 1 个温节点运行) |
仅在初始预配时针对类级 minimumCapacity 字段;突发中回退需要为 spec.priorities[] 字段中的每个回退条目设置 minimumCapacity.targetNodeCount: 1 字段(每个优先级保留 1 个温节点)
|
您希望自动创建节点池,并始终保持至少 1 个工作器节点处于运行状态。 |
| 具有容量缓冲区的自动创建池 |
将 nodePoolAutoCreation.enabled: true 字段与活跃或备用 CapacityBuffer 结合使用,后者的 PodTemplate 省略了 ate.dev/kvm 扩展资源。
|
暂停至待机状态(例如,在具有默认 100 GiB 启动磁盘的 c3-standard-4 节点上,费用约为运行计算费用的 8%)
|
是(缓冲区占位符会触发节点自动预配回退到其他优先级) | 您希望实现快速突发伸缩(1 到 4 秒内激活,或 20 到 35 秒内恢复备用节点,50 到 85 秒内新工作器准备就绪),同时保持较低的空闲成本。 |
| 预先创建的具有节点模板标签的 0 节点池 |
预先创建带有 clusterautoscaler-nodetemplate-resources.ate.dev/kvm=4096 标签的自动扩缩 0 节点池,并在 ComputeClass 的 spec.priorities[].nodepools 字段中引用这些节点池。
|
是(真正 0 节点,无闲置计算费用) | 是,跨预先创建的节点池 | 您希望实现真正的缩减至零,而无需维护备用容量缓冲区。 |
示例:具有 minimumCapacity 字段的 ComputeClass
使用 minimumCapacity 字段时,spec.priorities 字段中的每个条目都必须指定显式 machineType 字段,而不能仅指定 machineFamily 字段:
apiVersion: cloud.google.com/v1
kind: ComputeClass
metadata:
name: substrate-microvm
spec:
priorityDefaults:
enableNestedVirtualization: true
nodeSystemConfig:
linuxNodeConfig:
sysctls:
fs.inotify.max_user_instances: 65536
fs.inotify.max_user_watches: 1048576
net.ipv4.neigh.default.gc_thresh1: 4096
net.ipv4.neigh.default.gc_thresh2: 16384
net.ipv4.neigh.default.gc_thresh3: 32768
priorities:
- machineType: n4-standard-4
- machineType: c3-standard-4
- machineType: n2-standard-4
nodePoolAutoCreation:
enabled: true
minimumCapacity:
targetNodeCount: 1
nodePoolConfig:
imageType: cos_containerd
whenUnsatisfiable: DoNotScaleUp
如需将 WorkerPool 定位到此 ComputeClass,请将 nodeSelector:
{cloud.google.com/compute-class: substrate-microvm} 字段添加到 WorkerPool Pod 模板。
当 ComputeClass 在不同的 x86 机器系列(例如从 N4 到 C3 或 N2)之间回退时,以内存保真度(SNAPSHOT_FIDELITY_MEMORY 模式)恢复持久性快照需要一个工作器,其 CPU 功能是快照 CPU 功能的超集。如果没有兼容的工作器有可用容量,Agent Substrate 会将恢复降级为根文件系统保真度(SNAPSHOT_FIDELITY_ROOTFS 模式),这会在重启 Actor 的进程时保留根文件系统和持久卷数据。
示例:使用预先创建的 0 节点池从零扩缩
将高密度 Linux 内核
sysctl设置保存为system-config.yaml文件:linuxConfig: sysctl: fs.inotify.max_user_instances: '65536' fs.inotify.max_user_watches: '1048576' net.ipv4.neigh.default.gc_thresh1: '4096' net.ipv4.neigh.default.gc_thresh2: '16384' net.ipv4.neigh.default.gc_thresh3: '32768'使用
--system-config-from-file=system-config.yaml标志和clusterautoscaler-nodetemplate-resources.ate.dev/kvm=4096标签预先创建 0 个节点的主嵌套虚拟化节点池和后备嵌套虚拟化节点池,以便 GKE 集群自动扩缩器知道空池提供ate.dev/kvm扩展资源:# Primary N4 pool (0 nodes) gcloud container node-pools create microvm-n4-pool \ --cluster=CLUSTER_NAME \ --location=LOCATION \ --machine-type=n4-standard-4 \ --enable-nested-virtualization \ --system-config-from-file=system-config.yaml \ --num-nodes=0 \ --enable-autoscaling --total-min-nodes=0 --total-max-nodes=10 \ --node-labels=cloud.google.com/compute-class=substrate-microvm-zero,clusterautoscaler-nodetemplate-resources.ate.dev/kvm=4096 \ --node-taints=cloud.google.com/compute-class=substrate-microvm-zero:NoSchedule # Fallback C3 pool (0 nodes) gcloud container node-pools create microvm-c3-pool \ --cluster=CLUSTER_NAME \ --location=LOCATION \ --machine-type=c3-standard-4 \ --enable-nested-virtualization \ --system-config-from-file=system-config.yaml \ --num-nodes=0 \ --enable-autoscaling --total-min-nodes=0 --total-max-nodes=10 \ --node-labels=cloud.google.com/compute-class=substrate-microvm-zero,clusterautoscaler-nodetemplate-resources.ate.dev/kvm=4096 \ --node-taints=cloud.google.com/compute-class=substrate-microvm-zero:NoSchedule替换以下内容:
CLUSTER_NAME:GKE Standard 集群的名称。LOCATION:集群的 Compute Engine 可用区或区域。使用--total-min-nodes和--total-max-nodes标志可设置整个节点池(而不是每个可用区)的自动扩缩边界。
在 ComputeClass 中按优先级顺序引用预先创建的池:
apiVersion: cloud.google.com/v1 kind: ComputeClass metadata: name: substrate-microvm-zero spec: nodePoolAutoCreation: enabled: false priorities: - nodepools: - microvm-n4-pool - nodepools: - microvm-c3-pool whenUnsatisfiable: DoNotScaleUppriorityDefaults字段不适用于使用nodepools字段的优先级,因为节点设置来自预先创建的节点池。与自动创建的后备优先级一样,在后备机器系列上恢复 Actor 时,如果其 CPU 功能不是快照 CPU 功能的超集,则会将恢复降级为SNAPSHOT_FIDELITY_ROOTFS模式,并重启 Actor 的进程。
如需详细了解 ComputeClass 配置,请参阅使用自定义 ComputeClass 控制自动扩缩的节点属性。
为 WorkerPool 突发伸缩配置容量缓冲区
当 Actor 流量激增超过正在运行的 WorkerPool Pod 的容量时,新的 Worker Pod 必须等待 Compute Engine 节点预配、atelet 代理初始化和沙盒资源预热,然后才能为 Actor 提供服务。GKE 容量缓冲区将节点预配和 atelet 资源预热移出关键路径:
- 活跃容量缓冲区(
buffer.x-k8s.io/active-capacity策略):保持预配的节点处于运行和就绪状态,以便新的工作器 Pod 在 1 到 4 秒内完成调度,费用为 Compute Engine 虚拟机的完整运行费用。 - 备用容量缓冲区(
buffer.gke.io/standby-capacity策略):预配节点,并让atelet代理在备用初始化窗口期间在磁盘上初始化和预热沙盒资源(runsc二进制文件或 Cloud Hypervisor 和 Kata 映像),然后 GKE 会暂停 Compute Engine 虚拟机。此时间范围默认为 5 分钟 (5m),由 CapacityBuffer 上的buffer.gke.io/standby-capacity-init-time注解配置。如果资源预热时间超过 5 分钟,请增加此注解的值。发生突发流量时,GKE 会在大约 20 到 35 秒内恢复暂停的节点,并且端到端 WorkerPool 就绪(调度工作器 Pod、颁发 Pod 证书以及向ate-api-server组件注册容量)会在大约 50 到 85 秒内完成。在暂停期间,您只需为已暂停的虚拟机内存和启动磁盘存储空间付费。例如,在具有默认 100 GiBhyperdisk-balanced启动磁盘的c3-standard-4节点上,暂停存储费用约为运行计算费用的 8%;确切百分比因机器形状、启动磁盘大小以及预配的 IOPS 或吞吐量而异。
如需详细了解缓冲区策略,请参阅缩短启动延迟时间的策略。
容量缓冲区的限制和规则
为 GKE 上的 Agent Substrate 配置容量缓冲区时,请遵循以下规则:
- Compute Engine 机器配置对备用缓冲区的限制:挂接了本地 SSD 的虚拟机或内存超过 208 GB 的机器配置(例如
c3-highmem-88机器类型或裸金属配置)不支持 Compute Engine 实例暂停。对于大型工作器节点形状或具有本地 SSD 的节点,请使用活跃容量缓冲区,而不是备用缓冲区。 - 避免将
scalableRef字段指向 0 副本 WorkerPool 或 microVM 部署:- 当池扩缩到 0 个副本时,GKE CapacityBuffer 控制器无法解析自定义资源 (
kind: WorkerPool)。 - 对于 microVM WorkerPool 资源,也不要将
scalableRef字段指向底层 Deployment,因为 Deployment Pod 模板包含ate.dev/kvm: 1扩展资源(这会阻止节点自动预配功能横向扩容空池)。而是通过引用 PodTemplate(在podTemplateRef字段中)来配置缓冲区,该 PodTemplate 会选择 ComputeClass,而无需请求ate.dev/kvm扩展资源。 - 对于 gVisor WorkerPool 资源(不请求
ate.dev/kvm扩展资源),您可以将scalableRef字段直接指向 WorkerPool 底层 Deployment,也可以使用 PodTemplate。
- 当池扩缩到 0 个副本时,GKE CapacityBuffer 控制器无法解析自定义资源 (
示例:使用 PodTemplate 的备用 CapacityBuffer
以下清单定义了适用于 microVM 或 gVisor WorkerPool 资源的 PodTemplate 和备用 CapacityBuffer:
apiVersion: v1
kind: PodTemplate
metadata:
name: substrate-worker-buffer-template
namespace: WORKERPOOL_NAMESPACE
template:
spec:
nodeSelector:
cloud.google.com/compute-class: COMPUTE_CLASS_NAME
containers:
- name: buffer-placeholder
image: registry.k8s.io/pause:3.9
resources:
requests:
cpu: "WORKER_CPU_REQUEST"
memory: "WORKER_MEMORY_REQUEST"
---
apiVersion: autoscaling.x-k8s.io/v1beta1
kind: CapacityBuffer
metadata:
name: substrate-standby-buffer
namespace: WORKERPOOL_NAMESPACE
spec:
podTemplateRef:
name: substrate-worker-buffer-template
replicas: BUFFER_REPLICAS
provisioningStrategy: buffer.gke.io/standby-capacity
替换以下内容:
WORKERPOOL_NAMESPACE:WorkerPool 的 Kubernetes 命名空间(例如my-workerpool)。COMPUTE_CLASS_NAME:WorkerPool 针对的自定义 ComputeClass 的名称(例如substrate-microvm)。WORKER_CPU_REQUEST和WORKER_MEMORY_REQUEST:WorkerPool 中单个工作器 Pod 的 CPU 和内存请求(例如,4 vCPU 节点上的2CPU 和4Gi内存),为atelet代理和 GKE 系统守护程序留出节点容量。将这些值与一个工作器 Pod 的请求相匹配,有助于确保每个缓冲区副本预配一个备用工作器节点,而不是将多个缓冲区占位符打包到单个节点上。BUFFER_REPLICAS:要保持预热状态的备用工作器节点数量(例如4个节点)。
使用备用容量缓冲区时,请向 atelet DaemonSet 添加 buffer.gke.io/standby-capacity-node-suspended 污点(带有 operator:
"Exists" 字段)的容忍度,以便替换 atelet Pod 可以在暂停的备用节点上保持调度状态(例如,在 DaemonSet 滚动更新期间)。如需查看 DaemonSet 配置示例,请参阅在备用缓冲区上预加载映像。
如果多个工作负载共用同一个 ComputeClass,请向 WorkerPool Pod 模板和缓冲区 PodTemplate 添加匹配的工作负载分离容忍设置和 nodeSelector 标签,以便 GKE 预配其他工作负载不会声明的专用污点节点。此外,还要向 atelet DaemonSet 添加匹配的容忍度,以便 atelet 代理可以在这些专用节点上调度。如需查看分步说明,请参阅配置容量缓冲区。
管理升级和自动扩缩器互动
本部分介绍如何在不中断活跃 Actor 的情况下管理 atelet DaemonSet 更新、集群自动扩缩器缩容事件和节点升级。
使用备用缓冲区更新 atelet DaemonSet
暂停的备用缓冲区节点在暂停期间会带有 buffer.gke.io/standby-capacity-node-suspended 污点和不可访问污点,因此暂停节点上的 kubelet 代理无法确认 Pod 删除。确保 atelet DaemonSet 容忍 buffer.gke.io/standby-capacity-node-suspended 污点(使用 operator:
"Exists" 字段)。即使有这种容忍度,在 atelet DaemonSet(具有 maxUnavailable: 1 字段)的标准滚动更新期间,暂停节点上的 Pod 也会进入 Terminating 状态,并可能阻止更新在运行中的节点上继续进行。
在具有暂停的备用节点的集群上更新 atelet DaemonSet 之前,请执行以下操作之一:
- 为确保无法访问的备用节点不会耗尽更新预算,请将
ateletDaemonSet 上的spec.updateStrategy.rollingUpdate.maxUnavailable字段设置为大于暂停的备用节点数上限的值。 在更新
ateletDaemonSet 之前,请暂时将备用 CapacityBuffer 副本扩缩到0,并等待集群自动扩缩器移除所有暂停的备用缓冲区节点(通常需要 10 到 15 分钟)。如需检查是否有任何节点仍报告Suspended条件状态为True,请运行以下命令:kubectl get nodes -o custom-columns='NAME:.metadata.name,SUSPENDED:.status.conditions[?(@.type=="Suspended")].status'
在缩容和升级期间保护活跃 Actor
由于活跃 Actor 在由 atelet 代理管理的沙盒中执行,而不是作为单独的 Kubernetes Pod 执行,因此即使该节点上的工作器 Pod 托管活跃 Actor,GKE 集群自动扩缩器也可能会认为节点的 CPU 和内存请求较低。当节点被排空时,kubelet 代理会向工作器 Pod 发送 SIGTERM 信号,工作器会将 SIGTERM 信号转发到其托管的每个 Actor 的每个容器的主进程。然后,每个活跃 Actor 最多有 30 分钟的时间在 Worker 停止之前被暂停。为每个有状态 Actor 提供一个 SIGTERM 处理程序,该处理程序不会在信号上退出,并使用 DURABILITY_MODE_SYNCHRONOUS 模式调用 SuspendActor 方法。如果 Actor 的主进程因 SIGTERM 信号而退出,则会立即丢失未保存的状态。如果工作器 Pod 在仍托管活跃 Actor 的情况下停止,则 Actor 会进入 ACTOR_STATE_CRASHED 状态。
为了最大限度地减少缩容事件和 GKE 集群升级期间的中断,请应用以下做法:
- 避免将 Spot 虚拟机用于有状态 WorkerPool 节点,因为 Spot 虚拟机抢占在停止节点之前仅提供 15 秒到 2 分钟的终止通知。
- 在编排器中协调排空,以便在工作器 Pod 停止之前对活跃 Actor 进行检查点设置和暂停,或防止集群自动扩缩器逐出活跃工作器 Pod(例如,使用
cluster-autoscaler.kubernetes.io/safe-to-evict: "false"Pod 注解)。 如果您在 WorkerPool Pod 模板中静态设置safe-to-evict: "false"注解,则当 Worker Pod 调度到某个节点上时,集群自动扩缩器不会缩容该节点,即使该 Worker 托管的活跃 Actor 数量为零也是如此。如需释放空闲节点,您必须缩容 WorkerPool 副本或动态管理注解。如需详细了解 Worker 逐出生命周期以及如何使用RevertActor方法恢复崩溃的 Actor,请参阅 Agent Substrate API 指南中的逐出。 - 配置 GKE 超额配置升级并查看在资源受限的环境中升级,以便在封锁和排空现有工作器节点之前预配新的工作器节点。
后续步骤
- 在为 GKE 上的 AI 智能体工作负载规划架构中,了解如何选择合适的编排模型、沙盒运行时、隔离控制和存储选项。
- 详细了解 GKE 自定义 ComputeClass 和 GKE 容量缓冲区。
- 探索 Agent Substrate GitHub 代码库中的更多资源。