乐于分享
好东西不私藏

AiOps实战:YiFlow潮汐算力池

AiOps实战:YiFlow潮汐算力池

    当GPU竞价实例像潮水一样退去,大模型服务怎么办?这里分享我如何通过Kubebuilder自研的Operator,让算力池自动"涨潮",并通过Kong网关实现多实例负载均衡。以Qwen2-0.5B为例跑通全流程,附控制器代码解读。本文的理论基础均建立在已实际跑通的demo案例。此次GPU实例基于腾讯云,但架构本身不限定云厂商,可迁移到任何支持API调度的实例平台。

一、为什么需要"潮汐"算力?

做AI推理的同学都知道,私有化部署大模型有两个痛点:

痛点一:机器太贵。 一块A100按量计费一小时几十块,长期跑推理成本扛不住。

痛点二:竞价实例太不稳定。 竞价实例(Spot)价格只有按量的1~3折,但平台随时可能回收,模型服务说没就没。

这就引出了一个核心问题:能不能既享受竞价实例的低价,又保证LLM服务的高可用?

我的思路是——让算力像潮汐一样流动,但服务始终稳定。

具体来说:

  • 维护一个"算力池",里面始终保持固定数量的实例(比如n台);

  • 某台实例被回收了,立刻自动创建新实例补齐;

  • 用户始终通过统一的网关地址访问,后端实例的增减对使用者完全透明。

这就是YiFlowTideGPU模块的由来。

二、整体架构:探测-补偿-同步的闭环设计

用户请求   │   ▼┌─────────────────┐│   Kong 网关层    │  ← 统一入口,负载均衡└────────┬────────┘         │         ▼┌─────────────────┐│  TideGPU Operator│  ← 核心:探测 → 补偿 → 同步│  (Reconcile循环) │└────────┬────────┘         │    ┌────┴────┐    ▼         ▼┌──────┐  ┌──────┐│CVM-1 │  │CVM-2 │  ← 实例池,被回收时自动补齐└──────┘  └──────┘    │         │    └────┬────┘         │         ▼    ┌────────────┐    │ 状态变化反馈 │  ← 新IP/旧IP变化,触发Kong同步    └────────────┘

关键设计:

  • Kong网关:固定入口,负责负载均衡和路由,接收用户请求,按 Upstream 配置分发到存活实例

  • TideGPU Operator:Kubernetes集群内的"调度大脑",每 n 秒执行探测→补偿→同步,维持期望状态

  • 实例池:实际跑Ollama推理的后端节点,由 Operator 直接创建/销毁,IP 变化实时反馈到 Kong。

注:因当前 GPU 竞价实例资源紧张,本次验证使用 标准型 S5 CPU 实例 模拟 GPU 场景,架构逻辑完全等价。云厂商和实例类型均可替换,只要提供对应的 API 即可。

三、核心设计:一台"母机"生万物

3.1、 镜像工厂:避免每台机器重复装模型

    Ollama部署大模型有个麻烦事:模型文件大,如果每台新实例都现拉模型,启动时间可能要几分钟,这在竞价实例频繁回收的场景下是不可接受的。

我的做法是:

  • 创建一台"母机";

  • 在母机上安装Ollama,并拉取LLM模型。本次实验以 qwen2:0.5b为例;

  • 将这台机器打成自定义镜像,得到imageId;

  • 将这个镜像ID作为TideGPU CR的参数。

这样,Operator每次创建新实例时,直接基于这个镜像启动,开机即服务,无需等待模型下载。

# tidegpu_v1_tidepool.yamlspec:  imageId: img-xxxxx          # 预装Ollama + qwen2:0.5b的镜像  instanceType"S5.2XLARGE16"  # 本次验证用CPU模拟GPU  minimum2                    # 池中最少2  maximum2                    # 池中最多2

四、TidePool CRD:用声明式定义算力池

TidePool是我们自定义的Kubernetes资源,描述了一个算力池的期望状态:

apiVersion: tidegpu.eja.dev/v1kind: TidePoolmetadata:  name: tidepool-samplespec:  secretId: "AKID..."  secretKey: "h8JD..."  region: ap-singapore  availabilityZone: ap-singapore-2  instanceType: "S5.2XLARGE16"  minimum: 2  maximum: 2  subnetId: subnet-xxxxx  vpcId: vpc-xxxxx  securityGroupIds:    - sg-xxxxx  imageId: img-xxxxx  instanceChargeType: POSTPAID_BY_HOUR  # 生产环境用SPOTPAID(竞价)  kongGatewayIP: "x.x.x.x"

关键字段解读

字段
作用
minimum/maximum
池子水位线,Operator确保实例数始终在此区间
imageId
预装Ollama的自定义镜像,新实例"开机即用"
instanceChargeType
SPOTPAID为竞价实例,POSTPAID_BY_HOUR为按量(演示用)
kongGatewayIP
Kong网关地址,Operator自动将新实例注册到这里

可移植性说明secretId/secretKeyregioninstanceType等字段可根据实际平台调整。如果是阿里云、AWS或其他私有云平台,只需替换对应的SDK调用即可,Operator的核心逻辑不变。

五、控制器核心逻辑:Reconcile循环里的"三件套"

TideGPU Operator的核心是一个Reconciler,它每n秒执行一次"巡检",做三件事:

5.1、第一件事:检查Kong网关配置

func(r *TidePoolReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {    // ... 获取TidePool对象    // 检查Kong AI代理是否就绪    err = r.checkKongAiProxy(tidepool)    // 获取当前运行中的实例    runningList, err := r.getRunningInstanceIDs(tidepool)    // ...}

checkKongAiProxy会查询Kong的Service列表,如果发现ai-YiFlow-proxy服务不存在,就自动创建完整的Kong资源:

func(r *TidePoolReconciler) createKongServiceAndRouteAndUpstream(tidepool *tidegpuv1.TidePool) error {    // 1. 创建Upstream:llama2-YiFlow-upstream    upstreamData := map[string]string{        "name""llama2-YiFlow-upstream",    }    // 2. 创建Service:ai-YiFlow-proxy,指向上游    serviceData := map[string]string{        "name":     "ai-YiFlow-proxy",        "host":     "llama2-YiFlow-upstream",        "protocol""http",        "path":     "/api/chat",    }    // 3. 创建Route:ollama-chat,路径匹配~/ollama-chat$    routeData := map[string]interface{}{        "name":  "ollama-chat",        "paths": []string{"~/ollama-chat$"},    }}

5.2、第二件事:维持实例水位

runningCount := len(runningList)switch {case runningCount < int(tidepool.Spec.Minimum):    // 实例少了,创建新的    delta := tidepool.Spec.Minimum - int32(runningCount)    err = r.runInstances(tidepool, delta)case runningCount > int(tidepool.Spec.Maximum):    // 实例多了,销毁多余的    delta := int32(runningCount) - tidepool.Spec.Maximum    err = r.terminateInstances(tidepool, delta)}

5.3 第三件事:同步Kong的Upstream Targets

这是最关键的一步。Operator需要确保:Kong知道哪些后端实例是活着的

// 同步 Kong Upstreamerr = r.syncKongUpstream(tidepool)

syncKongUpstream的逻辑非常清晰——全量比对,增量同步

1、拉取Kong当前的所有Targets;

2、 与实例列表做diff:

  • 如果某个Target对应的实例已不存在(被回收了),删除该Target;

  • 如果某个实例还没有对应的Target,新增该Target。

func(r *TidePoolReconciler) syncKongUpstream(tidepool *tidegpuv1.TidePool) error {    // 1. 获取Kong当前targets    // 2. 遍历当前targets,删除已不存在的实例    // 3. 遍历实例列表,添加缺失的targets}

六、探测机制:直接对话云平台API

TideGPU不依赖Kubernetes的Node心跳,而是直接调用云平台API探测实例状态:

func(r *TidePoolReconciler) getRunningInstanceIDs(tidepool *tidegpuv1.TidePool) ([]stringerror) {    // 创建云平台客户端    client, err := r.createCVMClient(tidepool.Spec)    // 查询实例列表    request := cvm.NewDescribeInstancesRequest()    response, err := client.DescribeInstances(request)    var runningInstanceIDs []string    var instances []tidegpuv1.Instances    for _, instance := range response.Response.InstanceSet {        // 判断实例是否在运行中(包括PENDING和STARTING状态)        if *instance.InstanceState == "RUNNING" ||            *instance.InstanceState == "PENDING" ||            *instance.InstanceState == "STARTING" {            runningInstanceIDs = append(runningInstanceIDs, *instance.InstanceId)        }        // 检查公网IP是否已分配(分配公网IP是异步的)        if len(instance.PublicIpAddresses) == 0 {            return nil, fmt.Errorf("instance %s does not have public ip", *instance.InstanceId)        }        instances = append(instances, tidegpuv1.Instances{            InstanceId: *instance.InstanceId,            PublicIp:   *instance.PublicIpAddresses[0],        })    }    // 更新TidePool的Status    tidepool.Status.Instances = instances    err = r.Status().Update(context.Background(), tidepool)}

设计要点:

要点
说明
状态判断
不仅判断RUNNING,还包括PENDINGSTARTING,避免实例创建过程中被误判为缺失
公网IP检查
云平台分配公网IP是异步的,必须等IP分配完成才能注册到Kong
Status更新
将实例信息写入tidepool.Status,供syncKongUpstream使用

可移植性说明createCVMClientDescribeInstances是本次验证用的腾讯云SDK调用。如果迁移到其他平台,只需替换这部分SDK即可,Reconcile的核心逻辑(水位检查、Kong同步)完全不变。

七、实战验证:从搭建到故障模拟

7.1、环境搭建结果

备注:Kong Manager已可访问,x.x.x.x:8002/routes 页面正常打开,Routes列表为空(尚未创建路由)。

备注: Operator自动创建了ollama-chat路由,与代码中"name":"ollama-chat"、"paths": []string{"~/ollama-chat$"}完全对应。左侧Kong Manager显示Route,右侧是IDE中的代码。

备注:Upstreamllama2-YiFlow-upstream已创建,与代码中"name": "llama2-YiFlow-upstream"对应。

7.2、实例注册到Kong

备注: 两台实例已成功注册为Kong的Target:

  • x.x.x.x:8080

  • x.x.x.x:8080

右侧Operator日志显示:Created target for x.x.x.x:8080、Created target for x.x.x.x:8080

备注:左侧云平台控制台显示两台运行中的实例(公网IP分别为x.x.x.x和x.x.x.x),右侧KongManager显示UpstreamTargets与之一一对应。

7.3、推理验证:通过Kong网关访问LLM

备注: 向http://x.x.x.x:8000/ollama-chat发起POST请求,返回200,模型成功输出诗句。证明Kong → Upstream → Ollama全链路通畅。

7.4、故障模拟:手动销毁一台实例

备注: 在云平台控制台对实例执行"销毁/退还"操作,模拟竞价实例被平台回收的真实场景。

备注: 左侧云平台控制台显示新实例已创建(公网IP变为x.x.x.x),右侧Kong Manager显示Upstream Targets已更新。下方Operator日志显示:

  • Deleted target x.x.x.x:8080

  • Created target for x.x.x.x:8080

自愈完成,服务无中断。

7.5、自愈后验证

备注: 再次向Kong网关发起请求,依然返回200,模型正常响应。新实例已完全接替被销毁的实例工作。

八、关键技术复盘

8.1、为什么用Kubebuilder而不是Terraform alone?

    Terraform擅长"一次性"基础设施编排,但竞价实例是动态变化的。Kubebuilder提供的Reconcile循环,天然适合这种"持续维持期望状态"的场景。两者结合:Terraform负责静态资源(VPC、子网、Kong网关),Operator负责动态资源(实例池)。

8.2、为什么Kong而不是Nginx?

    Kong的Admin API支持运行时动态增删UpstreamTarget,无需reload。这与Operator的"自动注册&注销"需求完美契合。如果用Nginx,每次实例变化都要改配置并reload,不够优雅。

8.3、镜像工厂的价值

    "母机打镜像"策略,将模型部署时间从"几分钟"缩短到"几十秒"。在生产环境中,可结合Packer或云厂商镜像服务,实现版本化管理。

8.4、平台可移植性

本次验证基于腾讯云,但架构设计不限定厂商:

组件
可替换方案
实例管理
阿里云ECS API / AWS EC2 API / OpenStack API / 私有云平台
镜像工厂
Packer / 云厂商镜像服务 / 容器镜像(如用Kubernetes管理)
网关层
Nginx Plus(支持API动态upstream)/ Envoy / Traefik

核心思路是统一的:Operator监听实例状态 → 自动补偿 → 同步网关后端。

8.5、当前局限与改进

局限
改进方向
Secret明文存储
接入云平台CAM角色或Vault
仅支持公网IP
生产环境用内网CLB+内网IP,降低延迟
固定Requeue间隔
改为指数退避+事件驱动,减少API调用
CPU模拟GPU
实际生产替换为GPU机型

九、总结:让不稳定的基础设施,提供稳定的服务

    TideGPU Operator的核心价值,在于用软件工程的手段,对冲基础设施的不稳定性。通过Kubebuilder的Reconcile循环,我们将竞价实例的"潮汐"特性抽象为可管理的算力池;通过Kong网关的动态负载均衡,我们将后端实例的频繁更替对用户完全屏蔽。

最终实现了:

  • 资源池化:实例按需创建,维持固定水位;

  • 服务自愈:实例被回收后自动补齐,网关自动切换;

  • 统一入口:用户无感知后端变化,始终通过固定地址访问

这在当前AI推理成本高昂的背景下,是一种务实且可落地的降本增效方案。

后续演进方向

  • 接入云平台竞价实例中断通知,实现优雅下线(保存模型状态);

  • 基于推理QPS自动扩缩容,动态调整minimum/maximum;

  • 多可用区部署,实现地域级容灾。

如果你也在探索低成本LLM私有化部署,欢迎在评论区交流竞价实例的"避坑"经验。

✅ 附:本文核心组件参考资料

  • 🛠️ 核心技术栈文档

    Kubebuilder: https://github.com/kubernetes-sigs/kubebuilder

    Kong-Admin-API: https://docs.konghq.com/gateway/api/admin-oss/

    Ollama: https://github.com/ollama/ollama

    Tencent-Cloud-SDK: https://github.com/TencentCloud/tencentcloud-sdk-go

  • 🧠 扩展阅读(云原生 & LLM)

    Kubernetes-Operator: https://kubernetes.io/docs/concepts/extend-kubernetes/operator/

    Kong-Upstream: https://docs.konghq.com/gateway/latest/how-kong-works/load-balancing/

    竞价实例最佳实践: 各云厂商官方文档

🎬 配套视频讲解

本文配套的 《GPU算力说没就没?自动续上》已发布至视频号 —— 1 分钟讲透业务痛点、闭环架构与自愈原理,适合快速建立整体认知。

👉 欢迎关注我的视频号 【子逸Tech】先看视频再读文章,理解更深。

✨ 我们坚持:

只讲自己跑通的方案,拒绝纸上谈兵。

#AIOps#Golang#Kubernetes#Kubebuilder#LLM#Kong#竞价实例#智能运维#开源项目