

























Tencent 什么是竞价实例(波动型):https://cloud.tencent.com/document/product/213/17816?from=console_top_search
Aliyun 什么是抢占式实例:https://help.aliyun.com/zh/ecs/user-guide/what-is-a-spot-instance?spm=5176.ecsnewbuy.customBuy_chargeTypeNewTooltip.1.3c413675JxeQbT
华为云 什么是竞价实例:https://support.huaweicloud.com/usermanual-ecs/ecs_03_0188.html#section0
AWS 什么是竞价实例:https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-spot-instances.html
在 AI 大模型训练、分布式深度学习、科学计算等算力密集型场景下,GPU 计算资源的成本占比已成为企业 AI 基础设施建设的核心痛点 —— 传统包年包月的 GPU 实例采购模式灵活性差,无法适配 AI 任务波峰波谷的算力需求;按量计费模式虽灵活但单价高昂,长期运行会带来巨额成本支出。而云厂商推出的竞价 / 抢占式 GPU 实例,以 按量计费 1-2 折的价格 提供同等性能的算力,成为降本的核心选择,却因资源中断回收、跨云厂商规格不统一、调度管控复杂等问题,难以直接在生产级 AI 算力调度中落地。
Kubernetes Operator 基于 CRD(自定义资源定义)实现集群能力扩展,可将多云竞价实例运维逻辑、AI 算力调度规则转化为自动化工作流,是解决上述问题的最佳技术方案。本实战项目聚焦GPU 竞价实例资源池统一调度管理 Operator的从 0 到 1 开发,正是基于这一行业痛点与技术趋势,通过 CRD 定义算力调度的核心资源模型,联动腾讯云、阿里云、华为云、AWS 等主流云厂商的竞价实例 OpenAPI,打造集动态扩缩容、成本最优调度、故障自愈、资源池化管控于一体的生产级能力,实现 AI 算力调度的成本最小化、资源利用率最大化、业务稳定性高保障。
GPU 竞价实例统一调度管理 Operator 架构分四层,各层单向依赖、职责隔离:
| 架构层级 | 核心模块 | 核心职责 |
|---|---|---|
| 资源抽象层(CRD) | GPUSpotPool、GPUSpotStrategy、GPUWorkloadProfile | 作为整个 Operator 的核心数据模型底座,通过 k8s 自定义资源定义,统一封装 GPU 竞价实例资源池、算力调度策略、工作负载配置等核心对象,标准化定义资源规格、调度规则、成本约束等核心参数,彻底屏蔽底层云厂商的资源差异,为上层调度控制提供统一的资源抽象入口。 |
| 控制调度层(Controller) | Pool Controller、Interrupt Controller、Scheduler Controller、CloudSync Controller |
作为 Operator 的核心控制中枢,实现「状态感知→规则判断→动作执行」的全闭环自动化逻辑; 各控制器分责协同,分别负责资源池全生命周期管理、实例中断事件实时感知与应急处理、成本最优的算力调度决策、多云资源状态与价格数据的实时同步,是整个系统的核心调度与控制引擎。 |
| 多云适配执行层(Provider) | TencentCloud、AliCloud、HuaweiCloud、AWS | 作为底层云厂商的标准化适配执行层,封装各主流云厂商竞价实例的官方 OpenAPI/SDK,实现实例创建 / 销毁、状态查询、实时价格获取、中断事件监听等核心执行能力的标准化封装,彻底屏蔽不同云厂商在 API 接口、定价规则、中断机制、状态定义上的差异,为上层控制调度层提供统一的多云资源执行接口。 |
| 执行 & 观测保障层 | kube-scheduler 协同、Metrics、Events、Alert |
实现与 k8s 原生调度体系的深度无缝协同,同时提供全链路的可观测性与生产级故障保障能力; 通过 Metrics 采集资源运行、调度决策、成本消耗全维度数据,通过 Events 记录资源全生命周期关键事件,通过 Alert 实现实例中断风险、资源异常、调度故障的实时告警,是整个系统生产级稳定运行的核心保障底座。 |
本 GPU 竞价实例调度 Operator 整体采用四层分层架构,层级之间严格遵循单向依赖、职责隔离原则,上层只依赖下层接口,各层级权责清晰、互不越界。
这种设计不仅让代码解耦、易于扩展维护,也完美适配多云场景、动态调度、策略迭代等生产级诉求,下面逐层拆解职责边界、运行逻辑与设计优势。
整套架构自上而下分为
层级单向流转:资源抽象层 → 控制调度层 → 多云适配执行层 → 执行 & 观测保障层。每层只聚焦自身核心能力,不侵入其他层级逻辑。
资源抽象层(CRD)层基于 Kubernetes CRD 实现,包含三大自定义资源:GPUSpotPool、GPUSpotStrategy、GPUWorkloadProfile,核心定位是统一的配置与期望状态载体。
它只做一件事:面向用户暴露标准化配置入口,描述业务想要达到的最终资源状态,不包含任何调度、创建、销毁等执行代码。
使用示例:用户在集群中提交一条 GPUSpotPool 自定义资源,声明期望状态:需要 10 台 V100 32G 竞价 GPU 实例,单台最高出价 5 元 /
小时。
配置提交完成后,资源抽象层的工作就彻底结束,后续所有资源落地、调度、异常处理全部交由下层组件完成。
控制调度层(Controller)是整个 Operator
的决策中枢,包含资源池控制器、中断控制器、调度控制器、云状态同步控制器等多个
Controller,依托 Kubernetes Reconcile 协调循环工作。
它只做一件事:只做状态对比、逻辑判断、指令生成,绝不直接对接任何云厂商
SDK/API。
运行逻辑:
简单来说,这一层 只关心“要做什么”,不关心“具体怎么调用云接口实现”。
多云适配执行层(Provider)层 是对接各大公有云的适配底座,实现了腾讯云、阿里云、华为云、AWS 等多厂商 Provider 适配器,核心定位是纯执行单元。
它制作一件事:只接收上层下发的执行指令,调用对应云厂商官方 SDK/API
完成操作,不理解、不参与任何调度策略与业务逻辑。
运行逻辑:
这一层 只关心“怎么去做”,不关心 "上层为什么要创建实例、为什么选择当前机型、为什么要替换中断节点"。
执行 & 观测保障层 作为整套系统的生产级底座,该层独立于业务调度与资源执行逻辑,聚焦 可观测性、集群协同、风险告警 三大能力,为全链路稳定运行保驾护航。
核心能力划分:
简单的来说,这一层 只关心 "状态监控、事件留痕、风险预警、集群协同",不关心 "调度决策与资源执行"。
apiVersion: spotgpu.zuoyang.cn/v1alpha1
kind: GPUSpotPool
metadata:
name: training-pool-a100
spec:
minInstances: 2 # 最少保活实例数
maxInstances: 20 # 最大实例数
gpuTypes: # GPU 型号白名单
- A100-40G
- A100-80G
maxPricePerHour: 8.0 # 单实例小时价格上限(元)
providers: # 允许使用的云厂商
- name: tencent
regions: [ap-guangzhou, ap-shanghai]
- name: aliyun
regions: [cn-shenzhen, cn-beijing]
interruptStrategy:
gracefulShutdownSeconds: 120 # 中断后给 Pod 的优雅退出时间
checkpointRequired: true # 要求 Pod 配合 checkpoint
rebuildOnInterrupt: true # 中断后自动重建
scaleStrategy:
scaleUpStep: 2 # 单次扩容上限,防止抖动
scaleDownStep: 1 # 单次缩容上限
cooldownSeconds: 300 # 扩缩容冷却时间
Status 子资源是实际运行状态的核心,必须设计清楚:
status:
currentInstances: 8
desiredInstances: 10
instances:
- id: ins-abc123
provider: tencent
region: ap-guangzhou
gpuType: A100-40G
status: Running # Pending | Running | Interrupting | Terminated
pricePerHour: 4.2
createdAt: "2025-05-20T10:30:00Z"
nodeName: gpu-spot-abc123 # 对应 k8s Node 名
- id: ins-def456
provider: aliyun
region: cn-shenzhen
gpuType: A100-80G
status: Interrupting # 正在被中断
interruptNoticeAt: "2025-05-20T14:55:00Z" # 收到中断通知的时间
pricePerHour: 5.1
interruptedInstances:
- id: ins-def456
interruptReason: "price_exceeded"
totalCostThisMonth: 2150.6 # 当月累计成本(元)
lastSyncTime: "2025-05-20T15:00:00Z"
conditions:
- type: Ready
status: "True"
- type: ScaleUpInProgress
status: "True"
message: "creating 2 instances on tencent ap-guangzhou"
几个设计注意点:
apiVersion: spotgpu.zuoyang.cn/v1alpha1
kind: GPUSpotStrategy
metadata:
name: cost-first-strategy
spec:
mode: CostFirst # CostFirst | StableFirst | Balance
costFirst:
preferLowPrice: true
priceTolerancePercent: 20 # 价格比最低价高 20% 以内都接受,避免只选最便宜的导致库存不足
fallbackOnInsufficient: true # 低价机型库存不足时,自动 fallback 到次低价
stableFirst:
preferLongUptime: true
avoidHighInterruptRate: true
minStableMinutes: 60 # 优先选择承诺稳定时长的机型(如阿里云首小时不中断)
balance:
costWeight: 0.6
stabilityWeight: 0.4
策略不是排他选择,而是可以动态切换。实际情况是:训练任务用 CostFirst,推理服务用 StableFirst,同一个资源池在不同时段可能切换策略(比如白天推理为主用 StableFirst,夜间跑训练用 CostFirst)。
apiVersion: spotgpu.zuoyang.cn/v1alpha1
kind: GPUWorkloadProfile
metadata:
name: pytorch-ddp-training
spec:
gpuRequirement:
minCount: 4 # 最少 GPU 卡数
minMemoryGB: 32 # 最少显存
preferredTopology: NVLink # 优先 NVLink 互联
priority: high # 配合 k8s PriorityClass
interruptTolerance:
maxInterruptPercent: 30 # 资源池内最多 30% 实例可同时中断
checkpointEnabled: true # 任务支持断点续算
checkpointIntervalSeconds: 600 # 建议的 checkpoint 间隔
poolRef: training-pool-a100 # 绑定到哪个资源池
用单 CRD 多协同 Controller 模式,4 个 Controller 各管一件事:
Reconcile 流程不是简单的 "对比差异→执行→更新",生产环境中大量异常分支:
Reconcile(pool) {
1. 获取 pool 的 spec(期望状态)
2. 通过 CloudSync 同步多云实例真实状态
→ 如果同步失败(API 超时/限流),不继续执行,直接返回 error
→ 原因:基于过时状态做决策比不做决策更危险
3. 对比 desiredInstances vs currentInstances
→ 不足:触发扩容
→ 超出:触发缩容
→ 相等:检查是否有异常实例需要替换
4. 扩容逻辑:
a. 调度策略选机型+厂商+可用区
b. 生成幂等 token(poolName + provider + region + instanceType)
c. 检查是否已有同 token 的实例在创建中(防止重入)
d. 调用 Provider.CreateInstances
e. 如果返回 InsufficientInstanceCapacity:
- fallback 到次优机型(需策略允许)
- 如果所有 fallback 都失败,记录 event,等待下一轮
f. 创建成功但状态为 Pending:
- 不立即视为成功,设最小等待时间 60s
- 60s 后如果仍为 Pending,视为创建失败,重试
5. 缩容逻辑:
a. 按策略选择要缩的实例(优先缩高价的、空闲的)
b. 调用 Provider.TerminateInstances
c. 等待实例状态变为 Terminated
d. 清理对应 k8s Node
6. 更新 pool.status
→ 包括 instances、conditions、totalCostThisMonth
}
生产踩坑1:Reconcile 重入问题:创建实例的 API 调用发出去了但超时没返回,下一轮 Reconcile 又来了,不知道上一次创建成功了没有。如果直接再创建一次,就会出现重复实例。
解决办法:每次创建请求生成幂等 token,创建前查状态缓存里有没有同 token 的记录。各云厂商的幂等支持不同——阿里云的 ClientToken 参数原生支持幂等,腾讯云需要在应用层用 Redis 记录请求 ID 做 24 小时去重。
生产踩坑2:CR 被删除但云实例还在。用户删了 GPUSpotPool CR,但 Finalizer 逻辑没跑完 Operator 就挂了重启,云实例变成孤儿。
解决办法:
和云厂商实际实例列表,不在任何 CR 管理中的实例标记为孤儿,根据策略决定是否回收
这是整个项目最核心也最容易出问题的模块。
中断检测方式因云厂商而异:
| 云厂商 | 中断通知方式 | 提前量 | 可靠性 |
| AWS Spot | Instance Metadata Service 的 spot/instance-action 端点 | 2 分钟 | 高,但只能轮询 |
| 阿里云抢占式 | 无主动通知 | 0 | 必须靠主动轮询实例状态 |
| 腾讯云波动型 | 无主动通知 | 0 | 同上 |
| 腾讯云计划型 | 站内信/事件总线 | 按计划时间 | 较可靠 |
| 华为云竞价 | 无主动通知 | 0 | 主动轮询 |
实际实现中,中断检测必须同时用两种机制:
适用生产情况:千亿级基座模型预训练、长周期深度学习分布式训练,依托低价GPU竞价实例降本,核心诉求是 杜绝中断重跑、保障训练进度无损、实现全自动自愈续算,解决原生k8s无法适配竞价实例生命周期、中断无感知、进度不可控的核心难题。
大家要注意的关键点:Operator仅承担 中断信号投递、优雅等待窗口管控、节点自愈重建、状态可观测 能力,不替代业务逻辑。训练任务必须自主实现 "定期自动快照+中断信号触发强制保存" 机制,二者配合才能实现生产级无损训练。
适用生产工况:AI推理集群、大规模离线算力集群,单可用区部署数十台GPU竞价实例,突发云厂商区域级Spot资源回收,批量实例同步触发中断,极易引发API限流、重建拥堵、业务大面积抖动,核心诉求是 化解批量中断冲击、规避云厂商限流、保障集群快速平稳恢复。
生产核心痛点:以AWS为例,单地域 API 创建实例限流为2次/秒,若数十台故障实例集中向原可用区发起重建请求,会直接触发限流报错,导致重建任务排队积压、算力缺口持续扩大,推理服务超时飙升、离线训练任务长时间停滞。
实际的完整链路:
落地效果:彻底解决单AZ批量中断引发的集群雪崩、重建拥堵问题,大规模算力集群故障恢复有序可控,业务恢复时长缩短80%以上,保障推理、离线计算业务稳定性。
适用生产工况:8卡及以上多卡NVLink分布式训练场景,依赖全互联GPU拓扑保障NCCL通信效率,竞价实例随机重建易出现拓扑碎片化问题,核心诉求是 保障分布式训练通信性能稳定,杜绝重建后算力大幅衰减。
生产核心痛点:标准 8卡 NVLink 全互联拓扑是大模型分布式训练的最优架构,一旦竞价实例回收重建,集群可能被分配 4+4 跨 NUMA 节点的碎片化拓扑,无全链路 NVLink 互联,直接导致 NCCL allreduce 通信效率下降 30% 以上,整体训练速度大幅降级,严重影响训练迭代效率。
实际的完整链路:
落地效果:彻底解决竞价实例重建后的拓扑碎片化、性能衰减问题,常规场景下保持100%最优训练性能,资源不足降级场景下,将性能损耗控制在5%以内,实现低成本竞价资源与高性能分布式训练的完美兼容。
打分函数需要考虑的实际约束,不是简单的价格排序:
type ScheduleScore struct {
Provider string
Region string
InstanceType string
PriceScore float64 // 价格越低分越高,满分 100
InventoryScore float64 // 库存越充分越高,满分 100
StabilityScore float64 // 历史中断率越低分越高,满分 100
TopologyScore float64 // GPU 拓扑亲和性,满分 100
}
// CostFirst 模式
func (s *CostFirstScorer) Score(candidates []ScheduleScore) []ScoredCandidate {
for i := range candidates {
c := &candidates[i]
// 价格分:低于 maxPrice 的按比例打分
c.PriceScore = (s.maxPrice - c.ActualPrice) / s.maxPrice * 100
// 库存分:根据云厂商返回的库存状态,充足=100,紧张=50,极低=10
c.InventoryScore = calcInventoryScore(c.InventoryStatus)
// 综合分:CostFirst 模式价格权重 70%,库存权重 30%
c.FinalScore = c.PriceScore*0.7 + c.InventoryScore*0.3
}
// 按综合分排序
sort.Slice(candidates, func(i, j int) bool {
return candidates[i].FinalScore > candidates[j].FinalScore
})
return candidates
}
适用生产工况:常态化AI算力调度场景,集群默认开启CostFirst成本优先调度策略,优先选用低价GPU竞价实例,日常高频遇到优选机型、指定可用区库存耗尽问题,核心诉求是资源不足时自动容错降级、不阻塞业务扩容、异常可观测可告警,适配云厂商竞价资源库存动态波动的生产特性。
生产核心痛点:竞价实例库存具备极强的随机性、时效性,是生产环境每日高频遇见的问题。传统固定机型、固定可用区调度模式下,首选低价机型一旦返回
InsufficientInstanceCapacity
库存不足错误,调度会直接失败,导致算力扩容停滞、训练/推理任务排队阻塞,原生k8s无多级降级、重试、告警机制,严重影响算力业务连续性。
实际的完整链路:
落地效果:彻底解决竞价实例库存动态波动导致的调度失败、任务阻塞问题,实现 "同AZ重试→跨AZ重试→跨机型降级→重试告警" 的全链路容错,极大提升算力调度成功率,90%以上瞬时库存不足问题可通过自动降级自愈,无需人工干预,保障业务持续稳定运行。
适用生产工况:多云混合GPU算力集群(腾讯云、阿里云、华为云、AWS混部),企业长期规模化运行AI训练、推理业务,核心诉求是 打破单GPU单价误区,实现全维度真实成本最优调度,精准控制多云算力总成本,避免表面低价、隐性成本超支的问题。
生产核心痛点:传统算力调度仅对比GPU实例小时单价,成本计算维度单一,与企业实际账单严重不符。云端GPU算力总成本包含GPU算力、系统盘、数据盘、公网带宽、跨云VPC打通、内网互通等多项隐性费用。经常出现 "单卡单价更低的云厂商,叠加配套资源费用后,整体成本反而更高" 的情况,导致调度策略失真、降本效果不达预期。
举个例子:腾讯云A100竞价实例单价4.2元/小时,看似低于阿里云同规格5.1元/小时,但业务需搭配500G高性能云盘、10M固定公网带宽,产生额外存储、带宽月度费用;而阿里云同规格实例自带200G免费系统盘,无额外基础存储成本。叠加隐性费用后,两家厂商真实总成本基本持平,仅看GPU单价会导致调度决策严重偏差。
实际的完整链路:
落地效果:实现多云算力真实账单级成本最优调度,彻底规避单一单价调度的决策误区,精准识别隐性成本损耗,整体AI算力综合成本再优化10%-20%,让降本策略落地到真实账单,而非理论价格。
适用生产工况:长期驻留的GPU竞价实例集群,包含常态化推理服务、持续迭代的微调训练任务,核心诉求是实时管控动态价格波动,杜绝静默成本超支,保障算力运行成本始终在预算阈值内。
生产核心痛点:云厂商竞价实例价格为动态波动值,无固定定价。日常运行中常出现机型日间低价、夜间涨价,或高峰期价格翻倍的情况。集群若无实时价格监控与治理能力,运行中的实例会静默涨价,长期累积导致整体算力成本严重超限,人工巡检滞后、整改不及时,成本管控完全不可控。
实际的完整链路:
轮询间隔的设计不是拍脑袋的,要参考云厂商 API 限流:
| 云厂商 | API 限流 | 实例状态查询延迟 | 建议轮询间隔 |
|---|---|---|---|
| 腾讯云 | 20 次 /min | 5-10s | 30s(留 50% 余量) |
| 阿里云 | 100 次 /min(按 UID) | 3-5s | 15s |
| AWS | 2 次 /sec(按 Region) | 2-5s | 10s |
| 华为云 | 60 次 /min | 5-8s | 20s |
多厂商并行轮询,每个厂商独立 goroutine,互不影响。某个厂商 API 不可用时,只影响该厂商的实例状态更新,不影响其他厂商。
type CloudGPUProvider interface {
// 创建竞价实例,返回实例 ID 和状态
// idempotencyToken 用于幂等控制
CreateInstances(ctx context.Context, req CreateInstancesRequest) ([]InstanceResult, error)
// 销毁实例
TerminateInstances(ctx context.Context, instanceIDs []string) error
// 查询实例状态
GetInstanceStatus(ctx context.Context, instanceIDs []string) ([]InstanceStatus, error)
// 查询竞价实例当前价格
GetSpotPrice(ctx context.Context, region, instanceType string) (float64, error)
// 查询库存状态
GetInventoryStatus(ctx context.Context, region, instanceType string) (InventoryStatus,
error)
// 获取中断通知(仅 AWS 支持,其他厂商返回空列表)
GetInterruptNotices(ctx context.Context, region string) ([]InterruptNotice, error)
}
波动型实例没有中断通知,只能靠轮询状态发现实例被回收。计划型实例会通过事件总线提前通知,但需要用户在控制台配置事件订阅。
适配层处理:
func (p *TencentCloudProvider) GetInterruptNotices(ctx context.Context, region string)
([]InterruptNotice, error) {
// 波动型:无法主动获取,返回空
// 计划型:通过云 API DescribeScheduledInstances 查询计划回收
if p.InstanceMode == "fluctuate" {
return []InterruptNotice{}, nil
}
return p.queryPlannedInterruptions(ctx, region)
}
阿里云抢占式实例有一个其他厂商没有的特性:创建后第一个小时不会被中断。这意味着刚创建的实例至少有 60 分钟的安全窗口。
适配层处理:
func (p *AliCloudProvider) GetInstanceStatus(ctx context.Context, instanceIDs []string)
([]InstanceStatus, error) {
statuses := p.queryInstanceStatus(ctx, instanceIDs)
for i, s := range statuses {
// 首小时内标记为稳定状态
if time.Since(s.CreatedAt) < time.Hour {
s.InterruptRisk = "low" // 首小时内中断风险极低
s.MinRemainingUptime = time.Hour - time.Since(s.CreatedAt)
}
}
return statuses, nil
}
调度器可以利用这个特性:对于 StableFirst 策略,优先调度阿里云实例,因为至少有 1 小时保证。
AWS 是唯一提供中断通知的主流厂商,通过实例元数据服务的 spot/instance-action 端点获取。
适配层处理:
func (p *AWSProvider) GetInterruptNotices(ctx context.Context, region string)
([]InterruptNotice, error) {
// 方式 1:通过 EC2 DescribeInstances 过滤 State=stopping 的 Spot 实例
// 方式 2:在节点上通过 DaemonSet 轮询元数据服务(更及时)
// 两者结合:DaemonSet 负责实时检测,CloudSync 负责兜底
return p.describeSpotInterruptions(ctx, region)
}
每个云厂商的 API 都会偶发超时、限流、内部错误。适配层必须统一处理:
type RetryConfig struct {
MaxRetries int // 最大重试次数,默认 3
InitialDelay time.Duration // 首次重试延迟,默认 1s
MaxDelay time.Duration // 最大重试延迟,默认 30s
RetryableErrors []string // 可重试的错误码
}
// 腾讯云可重试的错误码
// ResourceInsufficient → 不可重试(换机型)
// InternalError → 可重试
// RequestLimitExceeded → 可重试,但需加长延迟
// AWS 可重试的错误码
// InsufficientInstanceCapacity → 不可重试(换机型/AZ)
// RequestLimitExceeded → 可重试
// ServiceUnavailable → 可重试
竞价 Spot 节点会统一打上专属污点,相当于标记为 可中断节点。只有配置了对应容忍度(toleration)的 AI 作业,才允许调度到这类节点;以此规避数据库等不可中断业务被误调度,保障核心服务稳定。
# Spot 节点自动打的污点
taints:
- key: spotgpu.zuoyang.io/spot-instance
effect: NoSchedule
- key: spotgpu.zuoyang.io/interrupting # 中断中
effect: NoSchedule # 阻止新 Pod 调度到即将回收的节点
Pod 侧需要加容忍:
tolerations:
- key: spotgpu.zuoyang.io/spot-instance
operator: Exists
effect: NoSchedule
# 不容忍 interrupting 污点,中断中的节点不会调度新 Pod
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: gpu-training-high
value: 1000000
preemptionPolicy: PreemptLowerPriority
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: gpu-inference-medium
value: 500000
适用生产工况:集群内同时运行高优先级大模型训练任务、中优先级推理 / 离线任务,基于 k8s PriorityClass 实现资源抢占调度;业务全部部署在 Spot 竞价节点上,依赖 Operator 完成中断检测、断点保存、自动续算,兼顾资源利用率与训练数据完整性。
生产核心痛点:当 Spot 节点资源紧张时,k8s 调度器会按照优先级规则,用高优先级 Pod
抢占低优先级 Pod。若抢占动作发生在训练进程执行 checkpoint
断点保存的窗口期内,Pod
会被强制终止,直接导致模型权重、运行状态写入不完整,断点文件损坏,后续任务无法正常续跑。同时原生 PodDisruptionBudget(PDB)仅能拦截 kubectl drain
等主动驱逐,无法阻拦调度器的优先级抢占行为,单纯依靠 PDB
无法彻底解决该问题。
实际的完整链路:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: checkpoint-protection-<node-name>
spec:
minAvailable: 1
selector:
matchLabels:
spotgpu.zuoyang.io/checkpoint-in-progress: "true"
落地效果:
适用生产工况:基于 GPU 竞价节点构建 AI 训练、推理集群,节点上线后依赖 NVIDIA Device Plugin 完成 GPU 资源注册与上报。集群自动化扩缩容、竞价实例重建场景频繁,要求节点就绪后可立刻承接业务 Pod 调度,保障算力流转连续性。
生产核心痛点:新节点完成初始化并进入Ready状态后,NVIDIA Device Plugin 仍存在 10~30
秒的资源上报延迟。此阶段节点未正常上报nvidia.com/gpu资源,若 k8s
调度器将 GPU 业务 Pod 调度至该节点,会因无法匹配 GPU
资源配额,直接导致 Pod 启动失败、任务异常。
实际的完整链路:
taints:
- key: node.alpha.spotgpu.zuoyang.io/gpu-not-ready
effect: NoSchedule
该污点会拒绝所有新 Pod 调度至当前节点。
落地效果:
| 指标名称 | 指标类型 | 指标含义 |
|---|---|---|
| spotgpu_pool_current_instances | Gauge | 当前运行实例数 |
| spotgpu_pool_interrupt_total | Counter | 累计中断次数 |
| spotgpu_pool_hourly_cost | Gauge | 当前小时成本 |
| spotgpu_pool_rebuild_latency_seconds | Histogram | 从实例中断到重建就绪的耗时 |
| spotgpu_schedule_score | Gauge | 最近一次调度打分值 |
| spotgpu_provider_api_errors_total | Counter | 云厂商 API 调用失败累计次数 |
| 事件名称 | 事件说明 |
|---|---|
| SpotInstanceCreated | 竞价实例创建成功 |
| SpotInstanceInterrupted | 实例接收到云厂商中断通知 |
| SpotInstanceRebuildStarted | 中断实例触发重建流程 |
| SpotInstanceRebuildCompleted | 重建完成,新实例正常就绪 |
| SpotPriceExceeded | 实例实时价格超出预设阈值 |
| ScaleUpFailed | 扩容失败,事件附带具体失败原因 |
# 中断率突增
- alert: SpotGPUHighInterruptRate
expr: rate(spotgpu_pool_interrupt_total[5m]) > 0.5
for: 5m
labels:
severity: warning
annotations:
summary: "资源池 {{ $labels.pool }} 中断率过高"
# 资源池容量不足
- alert: SpotGPUPoolUnderCapacity
expr: spotgpu_pool_current_instances < spotgpu_pool_min_instances
for: 10m
labels:
severity: critical
# 成本超限
- alert: SpotGPUCostOverBudget
expr: spotgpu_pool_hourly_cost > spotgpu_pool_max_hourly_cost
for: 30m
labels:
severity: warning
选一个云厂商(建议腾讯云或阿里云,API 文档中文化、国内网络延迟低),验证:
验证标准:中断后 5 分钟内任务恢复运行,checkpoint 数据完整。
选择可容忍中断的离线训练任务,观察:
预期数据:成本下降 70-85%,中断率 5-20%(取决于时段和区域),重建成功率 > 95%。
等我有时间了,再开下一篇 进入编码实战:Kubebuilder 项目初始化、CRD 定义与代码生成、Provider 接口实现、Reconcile 调谐逻辑
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。