- 后端
- 云原生
- 运维
- 虚拟化
- 人工智能
- AI 应用
【免费下载链接】cloudpods
开源、云原生的多云管理及混合云融合平台
在 CloudPods 的多云管理平台中,本地 GPU 推理集群与统一 API 网关是两套天然分离的资源:llm侧负责在物理机上拉起 vLLM/Ollama/SGLang 推理实例(llm_deployment),而aiproxy侧负责以 OpenAI 兼容协议对外提供统一的model寻址、密钥鉴权与选路能力。CloudPods 通过auto_register_aiproxy开关与LLMAiproxySyncTask同步任务,把两者打通——创建部署后无需手工维护 catalog,副本 running 后网关侧会自动生成对应的ai_provider、ai_model、ai_routing资源。本文基于仓库文档docs/llm/llm-deployment-aiproxy.md,并结合pkg/llm/models/llm_aiproxy_sync.go、pkg/llm/tasks/llm/llm_aiproxy_sync_task.go等源码实现,完整讲清这一自动关联机制的命令行用法、字段对应关系、命名规则与客户端接入方式。
机制总览:默认开启的自动关联
创建llm_deployment时默认开启auto_register_aiproxy。创建任务完成后会调度LLMAiproxySyncTask;副本变为 running 后,自动在 aiproxy 中创建 catalog 与路由资源。
从源码结构可以印证这一链路。在 创建任务 中,当AutoRegisterAiproxy为 true 时调用model.StartAiproxySyncTask(...),该任务最终由 LLMAiproxySyncTask 消费:OnInit中根据参数分三种路径——带unregister参数时调用UnregisterDeploymentAiproxy做清理;带llm_id时先对该副本执行SyncLlmInstance再做整体ReconcileDeploymentAiproxy;否则直接执行整体调和。部署模型定义 中AutoRegisterAiproxy *bool字段的注释也明确写着:"registers running replicas with aiproxy catalog when true (default true; pass false to disable)"。
创建 deployment(默认已关联 aiproxy)
最基础的操作是使用climc创建推理部署:
climc llm-deployment-create my-qwen \ --llm-sku-id <sku> \ --net <net> \ --replicas 2这条命令走的是LLMDeploymentCreateInput的 Mode A(引用已有 SKU)路径:--llm-sku-id指定复用已存在的 LLM SKU,--net指定实例网络,--replicas指定 SLLM 推理实例副本数。由于未传--auto-register-aiproxy=false,AutoRegisterAiproxy取默认值 true,副本拉起后会自动进入 aiproxy 同步流程。
若不希望该部署暴露到网关,可显式关闭自动关联:
climc llm-deployment-create my-qwen \ --auto-register-aiproxy=false \ --llm-sku-id <sku> \ --net <net>该命令由 climc 资源命令注册代码 提供,同一个资源命令组还注册了register-aiproxy、unregister-aiproxy、restart、syncstatus等 Perform 动作。此外AutoRegisterAiproxy也可在创建后通过 update 输入auto_register_aiproxy字段修改(见 LLMDeploymentUpdateInput)。
同步任务内部做了什么
理解自动关联的关键在于ReconcileDeploymentAiproxy的调和逻辑,其源码位于 llm_aiproxy_sync.go:
- 状态置位:先调用
SetAiproxySyncStatus将aiproxy_sync_status置为syncing(仅状态变化时落库,并写 ops logaiproxy_sync事件); - 枚举 running 副本:
listRunningDeploymentLlms按llm_deployment_id + status=running查询 SLLM 实例;若一个都没有,则清空 bindings 并回落到pending状态("waiting for running replicas"); - 逐副本 upsert catalog:
buildRoutingModelItems对每个副本调用SyncLlmInstance,内部依次执行:mapLLMTypeToProviderKey将 SKU 的llm_type映射为 aiproxy 的provider_key(vllm/ollama/sglang,其他类型报不支持);resolveLlmAccessBaseURL通过GetLLMAccessUrlInfo取推理实例的内网访问地址,优先InternalUrl,回退LoginUrl;collectUpstreamModelKeys收集该副本实际 serve 的模型 key(见下文"upstream model key");upsertAiProvider按llm_id过滤做"存在即更新、不存在即创建"的幂等操作,同时写入llm_deployment_id、llm_id、config.base_url;ensureAiKeyForProvider保证该 provider 下存在一条 enabled 的ai_key——若无用户自建 key,则创建占位 key,secret固定为常量aiproxyPlaceholderAPIKey = "unused"(见 llm_aiproxy_sync.go 顶部注释:本地 vLLM/Ollama/SGLang 一般不校验真实密钥,而上游转发 resolve 需要一条 secret 非空的 enabled key);upsertAiModel按ai_provider_id + model_key幂等创建/更新每个 catalog model;- 最终按副本顺序生成
ai_routing_model条目,priority 从 10 起步、每副本递增 10;
- 写入路由:
ensureAiRouting创建或更新 deployment 级ai_routing(初始priority=100,model_pattern置空),再经applyRoutingModels对 routing 执行set-models动作批量挂载各副本的 provider/model 条目; - 持久化与状态收敛:
persistDeploymentAiproxyBindings把aiproxy_routing_id与aiproxy_bindings写回 deployment 行,并按各副本绑定结果计算整体同步状态:全部成功为synced,部分失败为partial(附各副本last_error明细,可由AiproxySyncFailureReason汇总),全部失败为failed。
值得注意的幂等设计:若 deployment 尚未记录aiproxy_routing_id,同步时会按llm-dep-{slug(name)}名称查找已有ai_routing并转为更新路径(findAiRoutingIdByName+Update),避免同名 deployment 重建或并发同步时触发 409 重名错误。
手动重同步 / 取消注册
climc llm-deployment-register-aiproxy my-qwen climc llm-deployment-unregister-aiproxy my-qwen两条命令分别对应LLMAiproxySyncTask的unregister=false与unregister=true分支:
register-aiproxy走整体ReconcileDeploymentAiproxy,会重新调和所有 running 副本的 provider/model 资源,并对已注册资源同步更新命名(命名规则见下文),适合部署改名、副本变更后刷新网关侧资源;unregister-aiproxy调用UnregisterDeploymentAiproxy:先删除 aiproxy 侧全部关联资源(routing、按llm_deployment_id/llm_id找到的 provider 及其级联 model/key),再执行clearDeploymentAiproxyRegistrationState——将auto_register_aiproxy置为 false、清空aiproxy_routing_id与aiproxy_bindings、aiproxy_sync_status置为disabled。此后 deployment 的status保持副本健康态(ready/partial/deploying等),不再被网关同步结果覆盖。
从源码结构看,副本停止、重启这类离开 running 态的操作并不会删除网关 catalog(shouldUnsyncAiproxyOnLeaveRunning恒返回 false,routing ID 保持稳定),catalog 只在删除 deployment 或手动 unregister 时才被清理。
aiproxy_sync_status 状态机
网关同步进度通过独立字段aiproxy_sync_status表达,与副本健康态解耦,取值定义在 llm_deployment.go 的AIPROXY_SYNC_STATUS_*常量组中:
| 状态 | 含义 |
|---|---|
disabled | 自动关联已关闭(默认值即空串时按 disabled 处理) |
pending | 已开启但尚无 running 副本 / 等待首次同步 |
syncing | 同步任务执行中 |
synced | 所有 running 副本均已注册成功 |
partial | 部分副本注册成功,其余失败(可查 bindings 中的last_error) |
failed | 同步整体失败 |
每个副本粒度还有aiproxy_bindings[].sync_status(pending/synced/failed),AiproxyInstanceBinding结构(定义见)包含llm_id、client_model_alias、ai_provider_id、ai_provider_name、base_url、sync_status、last_error七个字段,是排查单副本注册问题的直接入口。
查看对应关系
deployment 详情中的aiproxy_bindings列出每个副本的客户端 model 别名与 provider id:
climc llm-deployment-show my-qwenshow输出对应的 LLMDeploymentDetails 结构中包含auto_register_aiproxy、aiproxy_routing_id、aiproxy_sync_status等字段。aiproxy 侧也可按来源 id 反查 provider:
climc ai-provider-list --llm-deployment-id <deployment_id> climc ai-provider-list --llm-id <llm_id>deployment 与 routing 的关联保存在llm_deployment.aiproxy_routing_id,这是双向追溯的核心外键。
字段对应关系
| llm 侧 | aiproxy 侧 |
|---|---|
llm_deployment.aiproxy_routing_id | ai_routing.id |
llm.id | ai_provider.llm_id |
llm_deployment.id | ai_provider.llm_deployment_id |
| 每个同步出的 provider(无用户自建 enabled key 时) | ai_key(占位secret=unused,名{ai_provider.name}-key) |
upstream served model name(vLLM/SGLang 为 model 目录 basename,Ollama 为name:tag) | ai_model.model_key |
{deployment_name}-{upstream_model_key} | ai_routing.model_key与aiproxy_bindings[].client_model_alias(同值;客户端 model 精确匹配,选路与列表优先) |
{ai_routing.model_key}/{upstream_model_key} | 层级 model id(routing 有model_key且挂多个 catalog model 时推荐;GET /v1/models与请求解析均支持) |
| (手工通配/前缀规则) | ai_routing.model_pattern |
| (LLM 自动注册时留空) | ai_routing_model.model_pattern |
几点由源码印证的关键细节:
- upstream model key 的推导:
upstreamModelKeyForBackend对 vLLM/SGLang 取挂载模型全名最后一段(即--served-model-name使用的目录 basename),Ollama 则为name:tag形式,与后端实际 serve 的模型 id 保持一致; - 别名生成:
deploymentClientModelAlias返回{deployment_name}-{model_key}(副本/部署名为空时回退为 id),deploymentRoutingModelKey与其同值,即ai_routing.model_key与client_model_alias永远一致; - 关联不存冗余外键:
ai_routing/ai_routing_model不保存llm_deployment_id/llm_id,而是通过ai_provider(持有llm_deployment_id/llm_id)与ai_routing_model.ai_provider_id间接关联推理实例,因此清理逻辑只需按 provider 维度级联即可。
客户端访问
接入网关需先创建项目级ai_virtual_key(网关入口鉴权,不是上游 provider secret),再通过 aiproxy 的 OpenAI 兼容接口访问。LLM 自动注册会为每个同步出的ai_provider确保一条占位ai_key(secret=unused,本地 vLLM/Ollama/SGLang 一般不校验真实密钥);上游转发依赖该ai_key,与客户端 Bearer 携带的 virtual key 是两套独立的密钥。
请求解析链路在 chat_completions.go 中概括为:ai_virtual_key → project ai_routing → ai_routing_model → ai_key (by catalog model_key)。
使用扁平 model id
curl -k "$AIPROXY/openai/v1/chat/completions" \ -H "Authorization: Bearer $VK" \ -d '{"model":"<deployment_name>-<upstream_model_key>","messages":[{"role":"user","content":"hi"}]}'model填 deployment 详情里aiproxy_bindings[].client_model_alias(格式{deployment_name}-{upstream_model_key},同一 deployment 下各副本相同),与ai_routing.model_key同值。LLM 自动注册/重同步会写入model_key并清空model_pattern。
使用层级 model id
当同一ai_routing下挂多个 catalog model 时,可用斜杠分隔的层级形式精确定位到某条 model:
curl -k "$AIPROXY/openai/v1/chat/completions" \ -H "Authorization: Bearer $VK" \ -d '{"model":"<ai_routing.model_key>/<upstream_model_key>","messages":[{"role":"user","content":"hi"}]}'多副本选路规则
仅发扁平ai_routing.model_key(无/)时,若 routing 下有多条ai_routing_model,默认选用priority 最低(最高优先级)的条目。从同步源码看,副本按顺序写入时 priority 依次为 10、20、30……(buildRoutingModelItems中priority := 10; ...; priority += 10),因此编号靠前的副本天然获得更高优先级。
删除 deployment 与资源清理
删除llm_deployment时会自动清理关联的 aiproxy 资源(ai_provider、ai_model、ai_routing):provider 按llm_deployment_id/llm_id查找,routing 按llm_deployment.aiproxy_routing_id删除,无需auto_register_aiproxy为 true。
对应实现为 DeleteDeploymentAiproxyResources:先按dep.AiproxyRoutingId删除 routing,再按llm_deployment_id过滤列出全部 provider 逐一删除,同时遍历该 deployment 下的每个 SLLM 实例按llm_id兜底清理可能遗留的 provider;单条资源删除失败仅记 warning 不阻断整体流程,保证清理操作的健壮性。
命名规则
自动注册时 aiproxy catalog 资源名称基于推理部署名与副本实例名({deployment_name}-{index}),不再使用 UUID 后缀:
| aiproxy 资源 | 命名规则 | 示例(部署my-qwen,副本my-qwen-0) |
|---|---|---|
ai_routing.name | llm-dep-{slug(deployment.name)} | llm-dep-my-qwen |
ai_provider.name | llm-{slug(llm.name)} | llm-my-qwen-0 |
ai_key.name | {ai_provider.name}-key | llm-my-qwen-0-key |
ai_model.name | llm-{slug(llm.name)}-{slug(model_key)} | llm-my-qwen-0-qwen3-0-6b |
slug(...)将名称规范为小写并将/、.等特殊字符转为-。部署名或副本名为空时回退为对应 id。
实现上,上述规则分别对应 llm_aiproxy_sync.go 中的aiRoutingNameForDeployment(llm-dep-前缀)、aiProviderNameForLlm(llm-前缀)、aiKeyNameForProvider(-key后缀)、aiModelNameForLlm(provider 名 + slug 化 model key)四个函数;底层的slugAiproxyName仅保留[a-z0-9]字符,将-、_、.、/、:折叠为单个-并修剪首尾连字符,与文档描述完全一致。
对已注册资源执行climc llm-deployment-register-aiproxy会同步更新上述名称,即重命名 deployment/副本后重同步即可让网关侧资源名跟随更新。
小结
CloudPods 的 LLM 部署与 aiproxy 网关自动关联机制,本质是一个以llm_deployment为源、以LLMAiproxySyncTask为驱动的最终一致性调和流程:aiproxy_sync_status独立表达网关侧进度,aiproxy_bindings记录逐副本绑定结果,幂等 upsert + 名称回查保证了重建与并发场景下不产生重复资源,而{deployment_name}-{upstream_model_key}这一稳定别名让客户端可以用同一个 OpenAI 兼容model字段无缝访问任意副本。实际运维中建议以llm-deployment-show输出的aiproxy_sync_status与aiproxy_bindings[].sync_status为第一观察面,遇到partial/failed时结合last_error与 aiproxy 侧 provider/model 资源状态定位问题。
- 后端
- 云原生
- 运维
- 虚拟化
- 人工智能
- AI 应用
【免费下载链接】cloudpods
开源、云原生的多云管理及混合云融合平台
相关推荐
在 Laradock 中部署 LiteLLM:一个 OpenAI 兼容的 LLM 网关,统一路由多 Provider
在 Laradock 中部署 LiteLLM:一个 OpenAI 兼容的 LLM 网关,统一路由多 Provider 导读 本文讲解如何在 Laradock 中
后端开发工具DevOpsAIBrix 快速上手:在 Kubernetes 集群中部署并调用 LLM 推理网关
AIBrix 快速上手:在 Kubernetes 集群中部署并调用 LLM 推理网关 本指南完整复现 AIBrix 的 Quickstart 流程:从在 Kub
人工智能大模型云原生模型推理服务LLM 网关API网关弹性伸缩litellm Docker部署:容器化LLM网关解决方案
litellm Docker部署:容器化LLM网关解决方案 在AI开发中,你是否正面临多模型API集成繁琐、环境配置复杂、部署流程重复的问题?本文将详细介绍如何
后端API网关LLM 网关大模型人工智能
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考