2025-08-20 06:00:00
在上一篇《Volcano初探:批处理调度引擎的云原生实践》中,我们通过Helm快速部署了Volcano集群,并成功运行了首个测试任务,验证了其基础调度能力。本文将进一步探索Volcano的GPU虚拟化功能,聚焦如何通过HAMi vGPU 技术实现GPU资源的细粒度共享与硬隔离。
批处理调度引擎 Volcano 支持 GPU 虚拟化功能,该功能主要由 HAMi 提供。
HAMi vGPU 提供的 GPU 虚拟化包括 HAMi-Core 和 Dynamic MIG 两种模式:
Mode | Isolation | MIG GPU Required | Annotation | Core/Memory Control | Recommended For |
---|---|---|---|---|---|
HAMI-core | Software (VCUDA) | No | No | Yes | General workloads |
Dynamic MIG | Hardware | Yes | Yes | MIG-controlled | Performance-sensitive jobs |
如果硬件支持 MIG 同时运行的是性能敏感型任务,那么推荐使用 Dynamic MIG 模型,不支持 MIG 依旧可以使用更加通用,对硬件无要求的 HAMi-Core 模式。
本文主要以 HAMi-Core 进行演示,HAMi vGPU 如何集成到 Volcano。
使用流程:
1)创建集群
2)安装 GPU-Operator,但是不安装 DevicePlugin
3)安装 Volcano,并配置开启 vGPU 插件
4)安装 volcano-vgpu-device-plugin
5)验证
使用 KubeClipper 部署一个集群进行验证。
Kubernetes教程(十一)—使用 KubeClipper 通过一条命令快速创建 k8s 集群
参考之前的文章 GPU 环境搭建指南:使用 GPU Operator 加速 Kubernetes GPU 环境搭建,使用 GPU Operator 部署环境。
安装 Volcano,部署时需要注意 volcano 和 k8s 的版本兼容性问题,参考官方 README:Kubernetes compatibility
这里部署的 v1.12.0 版本
|
|
部署完成后 Pod 列表如下:
|
|
Volcano 部署完成之后,我们需要编辑调度器配置,开启 deviceshare 插件。
|
|
完整内容如下:
|
|
核心如下:
|
|
开启 vgpu 同时调度策略我们选择 binpack。
HAMi 调度策略可以阅读这篇文章:HAMi vGPU 原理分析 Part4:Spread&Binpack 高级调度策略实现
修改后,不需要重启,Volcano 会自动检测,当文件变化后自动 reload。
|
|
不过 k8s 将 Configmap 同步到 Pod 中也是有延迟的,不想等的话也可以手动重启下。
|
|
接下来我们部署和 Volcano 集成用到的 DevicePlugin:volcano-vgpu-device-plugin
。
DevicePlugin 原理可以阅读这篇文章:HAMi vGPU 原理分析 Part1:hami-device-plugin-nvidia 实现,大致逻辑都是一样的。
从项目 volcano-vgpu-device-plugin 根目录获取文件: volcano-vgpu-device-plugin.yml
|
|
部署
|
|
查看 Pod 列表:
|
|
查看 Node 上的 Resource 信息:
|
|
Volcano 新增了下面三个:
|
|
volcano.sh/vgpu-cores: 800
:每张 GPU 100 core,8 卡正好 800 core。
volcano.sh/vgpu-memory: 39312
:由于设置了 factor=10,因此实际代表总显存 39312 * 10 = 393120 MB。
volcano.sh/vgpu-number: 80
:默认 --device-split-count=10
,将 GPU 数量扩大了 10 倍。
说明插件部署成功。
首先启动一个简单 Pod
|
|
查看效果,vgpu-memory 申请的 1024,如下:
但是因为 factor=10,所以实际是 10240 MB。
|
|
启动一个简单的 Volcano Job 试试:
|
|
一切正常:
|
|
查看 Pod 中的 GPU 信息
|
|
日志
|
|
|
|
包括 GPU core & memory 的分配信息,以及对应 Pod 信息,例如:
|
|
直接访问 DevicePlugin Pod 的 9394 端口获取监控信息:
|
|
可以查看到该节点上的 GPU 使用情况,metrics 如下:
|
|
DevicePlugin 都只会注册一个资源,而 Volcano DevicePlugin 却注册了三个资源,如何做到的?
|
|
实际上,Volcano DevicePlugin 内部启动了三个 DevicePlugin 分别使用了三个 ResourceName:
volcano.sh/vgpu-number
volcano.sh/vgpu-memory
volcano.sh/vgpu-cores
|
|
对应 sock 文件如下:
|
|
在获取 Device 时也根据不同的 ResourceName 做了不同实现:
|
|
具体分配 Device 逻辑:
|
|
核心部分:
包括指定环境变量,以及挂载 libvgpu.so 等逻辑。
|
|
同时由于启动了三个 DevicePlugin,为了避免重复调用,Allocate 方法中根据 ResourceName 进行了判断,只有 volcano.sh/vgpu-number
时才真正执行分配逻辑。
|
|
https://github.com/volcano-sh/volcano/blob/master/pkg/scheduler/plugins/deviceshare/deviceshare.go
简单分析一下 Volcano 中的 deviceshare 插件。
这块和 HAMi 实现基本一致,可以参考以下两篇文章:
每个插件都要实现 Volcano 定义的 Plugin 接口:
|
|
核心代码在 OnSessionOpen 实现中,包含了调度的两个方法:
Predicate
NodeOrder
|
|
主要实现调度过程中的节点过滤以及打分两部分逻辑。
过滤不满足设备需求的节点
|
|
核心逻辑在 FilterNode
方法中:
|
|
过滤不满足条件的节点,并为剩余节点打分。
从 core、memory 几方面判断 Node 是否有足够资源,不满足则过滤。
|
|
根据配置的调度策略进行打分。
|
|
逻辑比较简单:
Binpack :device 内存使用率越高,得分越高
Spread: device 有被共享使用得 100 分,否则 0 分。
上一步已经为节点打好分了,这里只需要根据得分排序即可。
|
|
核心部分:
|
|
节点得分 * 权重得到最终得分。
现象:device-plugin
部署后 gpu-memory
显示为 0 就像这样:
|
|
具体原因:https://github.com/volcano-sh/devices/issues/19
相关描述:
the size of device list exceeds the bound, and ListAndWatch failed as a result。
简而言之就是超过阈值的显存就会报错,导致 DevicePlugin 无法正常上报,因此显示为 0。
解决方案:需要在启动时设置参数 --gpu-memory-factor=10
,将最小的显存块从默认 1MB 改成 10MB,就像这样:
|
|
这样最大能显示的数值就扩大了 10 倍,就可以避免该问题。
效果如下:
|
|
volcano.sh/vgpu-memory: 39312
:由于设置了 factor=10,因此实际代表总显存 39312 * 10 = 393120 MB。
当前环境是 L40S*8,单卡显存 49140,49140 * 8 = 393120,正好符合,说明一切正常。
本文主要验证了 Volcano 如何通过集成HAMi vGPU技术实现 Kubernetes 环境下的 GPU 虚拟化,重点验证了HAMi-Core 模式的完整工作流程。
解答前面的问题:Volcano DevicePlugin 如何实现同时注册三个资源的?
通过启动三个 DevicePlugin 以实现注册 volcano.sh/vgpu-number
、volcano.sh/vgpu-memory
、volcano.sh/vgpu-cores
三种资源。
推荐阅读:
2025-07-30 06:00:00
上一篇简单介绍了 Volcano 及其使用场景,然后通过 helm 部署并跑通一个最简单的 Demo。
本文主要分析 Volcano 定义的几个主要资源对象的作用,包括 Queue、PodGroup 以及 VolcanoJob。
分析前,简单描述下每个对象的作用与定位:
Queue:用于支持多租户场景下的资源分配与任务调度。通过队列,用户可以实现多租资源分配、任务优先级控制、资源抢占与回收等功能,显著提升集群的资源利用率和任务调度效率。
PodGroup: 一组强关联的 Pod 集合,这主要解决了 Kubernetes 原生调度器中单个 Pod 调度的限制。通过将相关的 Pod 组织成 PodGroup,Volcano 能够更有效地处理那些需要多个 Pod 协同工作的批处理工作负载任务。
VolcanoJob:Volcano 自定义的 Job 资源类型,它扩展了 Kubernetes 的 Job 资源。VolcanoJob 不仅包括了 Kubernetes Job 的所有特性,还加入了对批处理作业的额外支持,使得 Volcano 能够更好地适应高性能和大规模计算任务的需求,更加适用于机器学习、大数据、科学计算等高性能计算场景。
三者关系如下图所示:
Queue 里面有多个 PodGroup,PodGroup 包含多个 Pod,其中的 Pod 由 VolcanoJob 生成。
一个 VolcanoJob 通常关联一个 PodGroup(自动或手动创建),该 Job 创建的 Pod 也会属于该 PodGroup,Volcano 以 PodGroup 为单位进行统一调度,并从关联的 Queue 中分配资源。
Queue 是Volcano的核心概念之一,用于支持多租户场景下的资源分配与任务调度。通过队列,用户可以实现多租资源分配、任务优先级控制、资源抢占与回收等功能,显著提升集群的资源利用率和任务调度效率。
即:Queue 是容纳一组 PodGroup 的队列,也是该组 PodGroup 获取集群资源的划分依据。
volcano 启动后,会默认创建名为 default 的 queue,weight 为 1。后续下发的 job,若未指定 queue,默认属于default queue。
对于非关键或临时性任务,可以放心使用 default
queue;
对于重要业务,务必创建专属Queue并设置合适的 weight
、priority
和 reclaimable
等配置。
一个简单的 Queue 的 完整 yaml 如下:
|
|
guarantee,可选:表示该 queue 为自己队列中的所有 PodGroup 预留的资源,其他队列无法使用该部分资源。
deserved,可选:用于指定该 queue 的应得资源总量
对于 capacity 插件可以直接配置 deserved 直观指定
对于 proportion 插件,会根据 weight 自动计算 deserved
weight,可选:表示该 queue 在集群资源划分中所占的相对比重,该 queue 应得资源总量为 (weight/total_weight) * total_resource。
该字段只有在 proportion 插件开启时可按需配置,若不设置 weight,则默认设置为1
其中, total_weight 表示所有的 queue 的 weight 总和,total_resource 表示集群的资源总量。weight 是一个软约束,取值范围为[1, 2^31-1]
假设有两个 queue,weight 分别为 2 和 1,那么各自可以使用集群 2/3 和 1/3 的资源。
capability,可选:表示该 queue 内所有 podgroup 使用资源量之和的上限,它是一个硬约束。
若不设置该字段,则队列的 capability 会设置为 realCapability(集群的资源总量减去其他队列的总 guarantee值)
一般建议设置 capability 来指定资源上限,以保证集群稳定。
reclaimable,可选:表示该 queue 在资源使用量超过该 queue 所应得的资源份额时,是否允许其他 queue 回收该 queue 使用超额的资源,默认值为true。
priority,可选:表示该 queue 的优先级,在资源分配和资源抢占/回收时,更高优先级的队列将会优先分配/抢占/回收资源
parent,可选:用来指定 queue 的父队列,若未指定 parent,则默认会作为 root queue 的子队列
Queue 的字段主要是用于资源限制,包括预留资源量、应得资源量、上限资源量以及是否允许资源回收等核心配置:
guarantee
:硬下限,是绝对约束,Queue 为自己队列中所有 PodGroup 预留的资源,其他 Queue 无法占用这部分资源。
deserved
/weight
:软性限制,根据开启不同插件,通过 deserved
(弹性基准线) 或 weight
(动态比例) 实现,二者互斥(选其一);
capability
: 硬上限,该 Queue 内所有 PodGroup 使用资源量之和的上限。
reclaimable
:是否允许回收超额资源(> deserved 部分)(默认 true,允许回收超额部分)。
Queue 包括以下状态
Open:该 queue 当前处于可用状态,可接收新的 PodGroup
Closed:该 queue 当前处于不可用状态,不可接收新的 podgroup
Closing:该 queue 正在转化为不可用状态,不可接收新的 podgroup
Unknown:该 queue 当前处于不可知状态,可能是网络或其他原因导致 queue 的状态暂时无法感知
即:Queue 只有在 Open 状态时可以接收新的 podgroup。
支持多维度资源配额控制(CPU、内存、GPU、NPU等)
支持动态资源配额调整
提供三级资源配置机制:
capability: 队列资源使用上限
deserverd: 资源应得量(在无其他队列提交作业时,该队列内作业所占资源量可超过 deserverd 值,当有多个队列提交作业且集群资源不够用时,超过 deserverd 值的资源量可以被其他队列回收)
guarantee: 资源预留量(预留资源只可被该队列所使用,其他队列无法使用)
建议及注意事项:
进行三级资源配置时,需遵循: guarantee <= deserverd <= capability;
guarantee / capability 可按需配置
deserverd 配置建议:在平级队列场景,所有队列的 deserverd 值总和等于集群资源总量;在层级队列场景,子队列的 deserverd 值总和等于父队列的 deserverd 值,但不能超过父队列的 deserverd 值。
在开启 capacity 插件时需要配置 deserverd 值
在开启 proportion 插件时,会根据 weight 自动计算 deserverd 值
capability 配置注意事项:在层级队列场景,子队列的 capability 值不能超过父队列的 capability 值,若子队列的capability 未设置,则会继承父队列的 capability 值。
资源借用:允许队列使用其他队列的空闲资源
资源回收:当资源紧张时,优先回收超额使用的资源
资源抢占:确保高优先级任务的资源需求
Volcano 提供了两个核心的队列调度插件:
capability 插件支持通过显式配置 deserverd 值来设置队列资源应得量
proportion 插件过配置队列的 Weight 值来自动计算队列资源应得量,无需显式配置 deserverd 值
|
|
例如 当前启用的是 proportion
插件:
|
|
capacity 插件通过精确的资源配置来进行配额控制,例如:
|
|
proportion 插件通过权重比例自动计算队列的 deserverd 值,当集群资源发生变化时,proportion 插件会自动根据权重比例重新计算各队列的 deserverd 值,无需人工干预。
|
|
当集群总资源为 total_resource
时,每个队列的 deserverd 值计算公式为:
|
|
其中,queue_weight
表示当前队列的权重,total_weight
表示所有队列权重之和,total_resource
表示集群总资源量。
capacity 插件和 proportion 插件必须二选一,不能同时使用,二者都有有各自的优缺点:
选择哪个插件主要取决于你是想直接设置资源量(capacity 插件)还是通过权重自动计算(proportion 插件):
动态集群选 proportion
:若集群频繁扩缩容(如使用 Cluster Autoscaler),优先选 proportion
避免人工维护成本
精细控制选 capacity
:需独立管理 CPU/GPU/内存等资源时(如为 AI 队列单独分配 A100 GPU),选 capacity
实现维度级控制。
Volcano v1.9.0 版本后推荐使用 capacity 插件,因为它提供了更直观的资源配置方式。
背景:
集群 CPU 总量为 4C
已默认创建名为 default 的 queue,weight 为 1
集群中无任务运行
操作:
当前情况下,default queue 可是使用全部集群资源,即 4C。
再创建名为 test 的 queue,weight 为 3。此时,default weight:test weight = 1:3,即 default queue 可使用 1C,test queue 可使用 3C
创建名为 p1 和 p2 的 podgroup,分别属于 default queue 和 test queue
分别向 p1 和 p2 中投递 job1 和 job2,资源申请量分别为 1C 和 3C,两个 job 均能正常工作
由于 Queue 能使用的资源时在 Queue 创建时通过 Weight 按比例划分的,后续有新 Queue 创建时,总 Weight 必然会增加,因此所有旧 Queue 能使用的资源比例肯定会减少。因此就会出现新 Queue 把旧 Queue 多使用的资源驱逐的情况。
背景:
集群 CPU 总量为 4C
已默认创建名为 default 的 queue,weight 为 1
集群中无任务运行
操作:
当前情况下,default queue 可使用全部集群资源,即 4C
创建名为 p1 的 podgroup,属于 default queue。
分别创建名为 job1 和 job2 的 job,属于 p1,资源申请量分别为 1C 和 3C,job1 和 job2 均能正常工作
创建名为 test 的 queue,weight 为 3。此时,default weight:test weight = 1:3,即 default queue 可使用 1C,test queue 可使用 3C。但由于 test queue 内此时无任务,job1 和 job2 仍可正常工作,不会被驱逐。
创建名为 p2 的 podgroup,属于 test queue。
创建名为 job3 的 job,属于 p2,资源申请量为 3C。此时,job2 将被驱逐,将资源归还给 job3,即 default queue 将 3C 资源归还给 test queue。
Weight 只能按比例划分资源,如果不希望 Queue 占用太多资源,那么可以使用 capability 进行硬限制,限制该 Queue 能使用的 CPU、Memory、GPU 等等资源的数量。
背景:
集群 CPU 总量为 4C
已默认创建名为 default 的 queue,weight 为 1
集群中无任务运行
操作:
创建名为 test 的 queue,capability 设置 cpu 为 2C,即 test queue 使用资源上限为 2C
创建名为 p1 的 podgroup,属于 test queue
分别创建名为 job1 和 job2 的 job,属于 p1,资源申请量分别为 1C 和 3C,依次下发。由于 capability 的限制,job1 正常运行,job2 处于 pending 状态
使用 capability 可以严格限制 queue 中任务使用的资源,但是可能会造成集群资源的浪费,因此使用 weight 还是 capability 需要根据实际情况选择。
reclaimable: true
意味着该队列超量使用的资源(超过其按weight
/deserved
应得的份额)可以被回收以满足更高优先级队列的需求或本队列内更高优先级PodGroup的需求。
reclaimable: false
则表示即使超量使用,资源也不可被回收
如果我们的任务优先级比较高,或者不希望被打断,则可以设置 reclaimable: false
,避免因为资源问题被回收。
背景:
集群 CPU 总量 为4C
已默认创建名为 default 的 queue,weight 为 1
集群中无任务运行
操作:
创建名为 test 的 queue,reclaimable 设置为 false,weight 为 1。此时,default weight:test weight = 1:1,即default queue 和 test queue 均可使用 2C。
创建名为 p1、p2 的 podgroup,分别属于 test queue 和 default queue
创建名为 job1 的 job,属于 p1,资源申请量 3C,job1 可正常运行。此时,由于 default queue 中尚无任务,test queue 多占用 1C。
创建名为 job2 的 job,属于 p2,资源申请量 2C,任务下发后处于 pending 状态,因为 test queue 的 reclaimable 为 false 导致该 queue 不归还多占的资源,job 无法启动。
Weight 为软约束,只是定义该 Queue 可以使用的资源份额, 是可以超过的(即:占用其他 Queue 未使用的部分资源),需要和 reclaimable 配合使用,当 Queue 使用资源超过 Weight 后,若 reclaimable 为 true 则其他 Queue 申请资源时,调度器会把当前 Queue 中低优先级 Pod 驱逐掉以释放资源。
Capability 为硬约束,不可以超过,资源不够时 Queue 下的 Pod 会直接 Pending
Guarantee 为保留资源,这部分资源不会被其他 queue 抢占。
Priority 优先级, 资源分配和资源抢占/回收生效。
使用 weight
(proportion插件) 或 deserved
(capacity插件) 是为了实现资源共享和弹性,最大化利用集群资源,但可能导致重要任务资源被压缩。
使用 capability
是为了严格限制一个队列的资源消耗上限(例如防止某个租户过度消耗),避免影响其他队列或系统组件,但可能导致集群整体利用率不高(该队列用不完,其他队列不能用)。
使用 guarantee
是为了给关键队列提供资源保障,确保其最低资源需求总能满足。
实际生产环境往往是组合使用:为关键业务Queue设置 guarantee
+ capability
+ 高 priority
+ reclaimable: false
;为普通业务Queue设置 weight
/deserved
+ capability
+ reclaimable: true
PodGroup
是一组强关联的 Pod 集合,这主要解决了 Kubernetes 原生调度器中单个 Pod 调度的限制。通过将相关的 Pod 组织成 PodGroup,Volcano 能够更有效地处理那些需要多个 Pod 协同工作的批处理工作负载任务。
一个简单的 PodGroup yaml 如下:
|
|
minMember:表示该 podgroup 下最少需要运行的 pod 或任务数量。如果集群资源不满足 miniMember 数量任务的运行需求,调度器将不会调度任何一个该 podgroup 内的任务。
queue:表示该 podgroup 所属的 queue。
priorityClassName:表示该 podgroup 的优先级,用于调度器为该 queue 中所有 podgroup 进行调度时进行排序。
system-node-critical
和 system-cluster-critical
是2个预留的值,表示最高优先级。
不特别指定时,默认使用 default 优先级或 zero 优先级。
minResources:表示运行该 podgroup 所需要的最少资源。当集群可分配资源不满足 minResources 时,调度器将不会调度任何一个该 podgroup 内的任务。
以下为 status 部分
phase:表示该 podgroup 当前的状态。
conditions:表示该 podgroup 的具体状态日志,包含了 podgroup 生命周期中的关键事件。
running:表示该 podgroup 中当前处于 running 状态的 pod 或任务的数量。
succeed:表示该 podgroup 中当前处于 succeed 状态的 pod 或任务的数量。
failed:表示该 podgroup 中当前处于 failed 状态的 pod 或任务的数量。
pending:表示该 podgroup 已经被 volcano 接纳,但是集群资源暂时不能满足它的需求。一旦资源满足,该 podgroup 将转变为 running 状态。
inqueue:表示该 podgroup 已经通过了调度器的校验并入队,即将为它分配资源。inqueue 是一种处于 pending和 running 之间的中间状态。
running:表示该 podgroup 至少有 minMember 个 pod 或任务处于 running 状态。
unknown:表示该 podgroup 中 minMember 数量的 pod 或任务分为 2 种状态,部分处于 running 状态,部分没有被调度。没有被调度的原因可能是资源不够等。调度器将等待 controller 重新拉起这些 pod 或任务。
状态流转如下图所示:
Volcano 会为每个 VolcanoJob 自动创建一个同名 PodGroup,若需自定义 PodGroup,可通过 spec.podGroup
字段指定。
自动创建的 PodGroup 取值如下:
minMember 默认为 vcjob 中所有 Task 的副本数总和,如果 vcjob 中指定了 minAvailable 则直接使用 minAvailable 填充
minResources 则是所有 Task 资源总和,当 minAvailable
存在时,minResources 按 前 minAvailable 个 Pod 的资源总和计算,且顺序由 Task 在 VolcanoJob 中的定义顺序决定
举个栗子🌰:
下面这个 vcjob 包含两个 task,nginx1 和 nginx2,副本数分别为 2 和 4,申请资源也不同。
|
|
当我们不指定 vcjob 中的minAvailable 参数时自动创建的 PodGroup 如下:
|
|
minMember 为 6,包括了 vcjob 中两个 task 的副本数之和
minResources 则是包括了两个 task 对应的 6 个 Pod 所需资源总和
当我们指定 vcjob 中的 minAvailable=3 时自动创建的 PodGroup 如下:
|
|
minMember 为 3,直接使用 minAvailable 填充
minResources 则是只计算了前 3 个 Pod 所需的资源
minResources 是按照 task 定义的先后顺序计算的,这 3 个 Pod 包括 nginx2 中的两个和 nginx1 中的 1 个,如果调整 task 的顺序,minResources 也会变化。
例如,将 task nginx1 放前面,生成的 PodGroup 如下:
|
|
这里计算的 3 个 Pod 都是 nginx1 中的 Pod。
在某些场景下,可能需要至少启动 N 个 Pod 本次任务才会成功,如机器学习训练,这种情况下适合使用 minMember 字段。
例如:本次训练要启动 6 个 Pod,如果不能都启动那整体任务肯定会失败,这种情况直接把 minMember 设置为 6,这样就可以保证只有 6 个 Pod 都可以启动时才会真正调度。
priorityClassName 用于 podgroup 的优先级排序,可用于任务抢占调度场景。它本身也是一种资源。
在某些场景下,任务的运行必须满足最小资源要求,不满足则不能运行该任务,如某些大数据分析场景。这种情况下适合使用 minResources 字段。
对于下面这个 vcjob,task 会启动 3 个 Pod,同时 minAvailable 也设置为 3。
|
|
该 vcjob 创建后自动生成的 PodGroup 如下:
|
|
除了通过 vcjob 创建工作负载之外,也可以使用 k8s 原生工作负载。
|
|
通过 Annoations scheduling.volcano.sh/queue-name
指定 queue
并通过 schedulerName 指定调度为 volcano
VolcanoJob
是 Volcano 自定义的 Job 资源类型,它扩展了 Kubernetes 的 Job 资源。VolcanoJob 不仅包括了 Kubernetes Job 的所有特性,还加入了对批处理作业的额外支持,使得 Volcano 能够更好地适应高性能和大规模计算任务的需求,更加适用于机器学习、大数据、科学计算等高性能计算场景。
一个简单的 VolcanoJob yaml 如下:
|
|
schedulerName:表示该 job 的 pod所 使用的调度器,默认值为 volcano,也可指定为 default-scheduler。它也是 tasks.template.spec.schedulerName 的默认值。
如果指定为 default-scheduler
,则意味着该Job的Pod将由Kubernetes默认调度器调度,将无法利用Volcano提供的高级调度策略(如Gang Scheduling, Fair-Share, Queue, Preemption等)
强烈建议对于需要Volcano特性的Job保持 schedulerName: volcano
minAvailable:表示运行该 job 所要运行的最少 pod 数量。只有当 job 中处于 running 状态的 pod 数量不小于minAvailable 时,才认为该 job 运行正常。
volumes:表示该j ob 的挂卷配置。volumes 配置遵从 kubernetes volumes 配置要求。
tasks.replicas:表示某个 task pod 的副本数。
tasks.template:表示某个 task pod 的具体配置定义。
tasks.policies:表示某个 task 的生命周期策略。
policies:表示 job 中所有 task 的默认生命周期策略,在 tasks.policies 不配置时使用该策略。
plugins:表示该 job 在调度过程中使用的插件。
queue:表示该 job 所属的队列。
priorityClassName:表示该 job 优先级,在抢占调度和优先级排序中生效。
maxRetry:表示当该 job 可以进行的最大重启次数。
相比之下,VolcanoJob 的字段都比较容易理解,大分部含义和 K8s 原生对象中的字段含义一致。
pending:表示 job 还在等待调度中,处于排队的状态。
aborting:表示 job 因为某种外界原因正处于中止状态,即将进入 aborted 状态。
aborted:表示 job 因为某种外界原因已处于中止状态。
running:表示 job 中至少有 minAvailable 个 pod 正在运行状态。
restarting:表示 job 正处于重启状态,正在中止当前的 job 实例并重新创建新的实例。
completing:表示 job 中至少有 minAvailable 个数的 task 已经完成,该 job 正在进行最后的清理工作。
completed:表示 job 中至少有 minAvailable 个数的 task 已经完成,该 job 已经完成了最后的清理工作。
terminating:表示 job 因为某种内部原因正处于终止状态,正在等到 pod 或 task 释放资源。
terminated:表示 job 因为某种内部原因已经处于终止状态,job 没有达到预期就结束了。
failed:表示 job 经过了 maxRetry 次重启,依然没有正常启动。
以 tensorflow 为例,创建一个具有 1 个 ps 和 2 个 worker 的工作负载。
|
|
以 MindSpore 为例,创建一个具有 8 个 pod 副本的工作负载,要求 1 个可用即可。
|
|
Queue 实现多租户资源隔离与弹性分配
通过权重(weight
)或容量(deserved
)策略动态划分集群资源,支持资源预留(guarantee
)、硬上限(capability
)和弹性复用(reclaimable
)。例如:
多租户隔离:为不同团队/项目分配独立队列,避免资源抢占
弹性分配:空闲时超用资源,紧张时按比例回收,提升集群利用率
层级扩展:支持父/子队列嵌套,实现部门级资源再分配
PodGroup 确保强关联 Pod 的原子调度(Gang Scheduling)
通过 minMember
和 minResources
实现 All-or-Nothing 调度,解决分布式任务(如AI训练)的协同启动问题。例如:
原子性保障:若集群无法满足最小Pod数,则全部不调度,避免死锁
优先级控制:通过 priorityClassName
实现任务抢占,保障高优先级作业
VolcanoJob 定义复杂作业结构与生命周期策略
多任务模板:支持异构Pod角色(如TensorFlow的PS/Worker),通过 tasks
字段声明依赖关系
增强生命周期:基于 policies
配置故障恢复(如Pod失败重启)、超时终止(如 PodPending: 10m
自动终止)
插件扩展:集成 ssh/svc/env
等插件,提供跨Pod互信、服务发现等分布式能力
一句话描述:Queue 是资源池,实现多租户资源隔离与动态分配;PodGroup 是原子调度单元,确保强关联 Pod 的协同调度(All-or-Nothing);VolcanoJob 是批作业抽象,定义多角色任务拓扑与生命周期策略。
2025-07-23 06:00:00
还在为 Kubernetes 大规模批处理任务调度烦恼?Volcano——CNCF 官方批处理调度引擎,提供 Gang Scheduling、队列优先级、异构设备支持等高级特性。本文以最小化实践带你完成 Volcano 安装到验证。
官方描述:Volcano is a batch system built on Kubernetes. 基于 Kubernetes 的高性能工作负载调度引擎。
支持 Kubernetes 原生负载及主流计算框架 (如TensorFlow、Spark、PyTorch、Ray、Flink等) 的一体化作业调度。
强大的批处理调度能力:通过 VcJob 完美支持 Ray、TensorFlow、PyTorch、MindSpore、Spark、Flink 等主流 AI 和大数据框架
完整的 Kubernetes 工作负载支持:可直接调度 Deployment、StatefulSet、Job、DaemonSet 等原生工作负载
或者说 Volcano 之前有哪些痛点。
随着各种新兴高性能计算需求的持续增长,Job 的调度和管理能力变得必要和复杂。下面罗列了一些共性需求:
调度算法的多样性
调度性能的高效性
无缝对接主流计算框架
对异构设备的支持
Volcano 正是针对这些需求应运而生的。同时,Volcano 继承了 Kubernetes 接口的设计风格和核心概念。用户可以在充分享受 Volcano 的高效性和便利性的同时不用改变任何以前使用 Kubernetes 的习惯。
场景 | 原生 Kubernetes | Volcano | 优势 |
---|---|---|---|
分布式任务调度 | Pod 独立调度 | Gang Scheduling (All-or-Nothing) | 避免死锁,提高集群利用率 |
资源争用处理 | 简单 FIFO | 优先级队列+公平共享 | 保障关键任务资源 |
GPU 调度 | 基础设备分配 | 拓扑感知+虚拟化 | 提升 GPU 利用率 30%+ |
批量作业 | Job 资源限制弱 | 作业级资源配额 | 精准控制批量任务资源占用 |
现在以一个分布式训练场景为例,说明为什么需要 Volcano。
在 AI 中大规模任务比较常见,如果按照 k8s 默认的以 Pod 为单位进行调度,可能会出现资源浪费,甚至死锁的情况。
例如:启动一个 PyTorchJob 包括 1 Master 2 Worker,yaml 如下:
|
|
按照 K8s 默认的以 Pod 为单位进行调度,可能出现只有部分 Pod 能成功调度并启动的情况,这样的结果对该 Job 来说都是无意义的。
如果有多个这样的 Job 可能都只调度了一部分 Pod,最终集群资源耗尽,所有 Job 都在等待其他 Job 释放资源。
我们需要该 Job 在调度时满足满足 Gang 调度语义,任务的所有工作负载必须全部运行或全部不运行(All or nothing),从而避免 Pod 的任意调度导致集群资源的浪费。
可以看到 Volcano 完全构建于 Kubernetes 之上,与 Kubernetes 天然兼容,并为高性能计算而生,遵循 Kubernetes的设计理念和风格。
Volcano 包括以下组件:
Volcano Scheduler:通过一系列的action和plugin调度Job,并为它找到一个最适合的节点。与Kubernetes default-scheduler相比,Volcano与众不同的 地方是它支持针对Job的多种调度算法。
Volcano ControllerManager: Volcano controllermanager管理CRD资源的生命周期。它主要由 Queue ControllerManager、 PodGroupControllerManager、 VCJob ControllerManager构成。
Volcano Admission: Volcano admission 负责对 CRD API资源进行校验。
CRD:Job、Queue、PodGroup 等自定义资源。
vcctl:Volcano vcctl是Volcano的命令行客户端工具。
Volcano 通过 CRD 扩展了 K8s 对象,然后自定义 Controller 和 Scheduler 进行管理以及调度。
部署时需要注意 volcano 和 k8s 的版本兼容性问题,参考官方 README:Kubernetes compatibility
这里我们部署 Volcano 的 1.12.0 版本。
官方提供了 chart,直接使用 helm 安装即可
|
|
安装完成,验证 Volcano 组件的状态
|
|
查看相关 crd
|
|
接下来我们跑一个简单的 Demo 测试一下 Volcano 的能力,确认 Volcano 正确运行。
首先创建一个名为 test 的 Queue
|
|
再创建一个名为job-1
的Volcano Job。
|
|
可以看到,这个 Volcano Job 和 K8s 原生 Job 类似,不过也多了几个字段:
|
|
minAvailable:需要满足最小数量后才进行调度
schedulerName:指定调度器
queue:指定 Job 所属的队列,就是前面我们创建的队列
policies:一些特殊策略
检查 Job 的状态
|
|
检查 PodGroup 的状态
|
|
检查 Queue 的状态
|
|
最后检查 Pod 状态
|
|
能够正常启动,说明 Volcano 能够正常使用,具体各个 CRD 对象的作用,后面在详细分析。
Volcano 是 Kubernetes 生态中一个强大的批处理调度系统,通过高级调度算法、增强的 Job 管理能力、异构设备支持和多框架集成等等,解决了 Kubernetes 在处理高性能工作负载时的局限性。
本文主要是介绍了什么是 Volcano 以及使用场景,然后通过 helm 部署 Volcano 并运行了一个最简单的 Demo。
后续详细介绍 Volcano 的各个 CRD 的作用,调度策略,以及 HAMi vGPU 和 Volcano 如何搭配使用。
2025-07-02 06:00:00
还在为 Kubernetes 集群故障排查头疼?试试 K8sGPT—— 这款基于 AI 的智能诊断工具,能自动扫描集群异常,并通过 OpenAI、DeepSeek 等模型生成 step-by-step 解决方案。本文手把手教你用 CLI 或 Operator 模式部署,从安装到实战验证,让 K8s 运维效率飙升!
K8sGPT 是一款基于 AI 的 Kubernetes 智能诊断工具,自动扫描集群异常并通过 AI 生成解决方案。它支持多类 AI 后端(OpenAI、本地模型等),提供两种核心使用模式:
Cli 方式安装:安装 cli 工具方式使用,通过 kubeconfig 连接集群,即时诊断问题
Operator 方式安装:通过在集群中安装 Operator 方式使用,这种方式非常适合持续监控集群,并且可以与 Prometheus 和 Alertmanager 等现有监控集成
在 k8s 环境部署,推荐使用 Operator 方式安装。
k8sgpt 的能力由 cli 工具提供,可以直接使用 brew 安装:
|
|
或者到 Releases 界面下载
|
|
k8sgpt 支持多种 AI Provider,具体如下:
|
|
常见的 openai、ollama 等,也支持使用 localai 对接任意满足 OpenAI API 格式的外部模型。
这里我们使用 localai Provider 来对接 DeepSeek:
|
|
并将其设置为 默认 Provider
|
|
运行 k8sgpt analyze
扫描集群中的问题:
|
|
也可以指定 kubeconfig 访问远程集群
|
|
默认情况下不会使用 AI,只是简单扫描集群中的问题,需要增加--explain
flag 才会与 AI 交互给出对应解决方案:
|
|
效果如下:
|
|
直接 helm 部署,命令如下:
|
|
查看 Pod 运行情况
|
|
会启动一个 k8sgpt-operator-controller-manager Pod,这样就算部署好了
首先创建一个 K8sGPT 对象
|
|
大致包含两部分信息:
1)K8sgpt 仓库地址以及版本,会使用该信息创建对应 Pod
repository:ghcr.io/k8sgpt-ai/k8sgpt
version:v0.4.1
2)对接的 LLM 信息,会使用这部分信息与 LLM 交互,这里用的是 DeepSeek
backend:localai
baseUrl:https://api.deepseek.com/v1
model:deepseek-reasoner
secret:指定存放 API Key 的 secret,如果 API 没有设置 Auth 的话不指定 secret 部分
…
需要提供一个 LLM 服务配置,不一定是 chatgpt,只要是兼容 OpenAI API 格式的服务都可以。
比如可以用 DeepSeek https://platform.deepseek.com/usage,也可以使用本地的服务
如果 LLM 服务需要认证,提前创建 secret 方式存储 API Key,secret 名称和 key 需与 K8sGPT
对象 spec 中的配置一致”,避免配置不匹配导致的异常。
|
|
K8sGPT 对象创建之后,上一步部署的 Operator 会根据信息启动一个 Pod
|
|
这个 Pod 就是真正与 AI 交互的 Pod,该 Pod 会启动一个 http 服务提供给 Controller, Controller 扫描集群故障后,通过 http 请求该服务,该服务与 AI 交互得到解决方案后返回给 Controller,最终 Controller 将其存储到 result 对象中。
接下来就可以开始验证了。
k8sgpt 会自动收集集群信息,并生成诊断结果,可以通过以下命令查看:
|
|
其中每个 result 对象对应一个问题,如果集群不存在任何问题是不会生成 result 对象的。
因为我们可以自己创造一些问题进行模拟,例如模拟服务镜像拉取失败的场景。
使用以下命令创建 deployment,由于 tag 不存在肯定会出现无法拉取镜像的问题。
|
|
和预期一样,Pod 会因为无法拉取镜像,启动失败
|
|
看下 k8sgpt 能否检测到该问题:
|
|
k8sgpt 检测到故障并生成 result 通常需要 30 秒到 2 分钟(取决于集群规模)。
已经有新的 result 了,看起来没什么问题,result 对象名称由 namespace 和对象名(这里就是 PodName)组成。
k8sgpt 会自动将 error 信息发送给 AI,并将结果写入 result 中的 details 字段,内容如下:
|
|
格式化之后内容如下:
|
|
details 部分包含了具体问题,以及解决方案。
这里 AI 让我们编辑 deployment,将镜像 tag 修改为有效值。
按照 AI 给出的解决方案修复:
|
|
调整镜像后 Pod 正常启动
|
|
修复后,故障(result 对象)消失
|
|
k8sgpt 是如何工作的呢?
分为以下步骤:
1)初始化 Analysis:配置 Kubernetes 客户端、AI 后端等等
2)运行 Analysis
运行自定义 Analysis(如果有配置)
运行内置 Analysis
3)请求 AI 生成处理方案(如果指定 explain)
4)结构化返回诊断报告
完整代码如下:
|
|
k8sgpt 中包括多个内置 Analyzer
以 Pod Analyzer 为例,流程也比较简单:
1)使用 k8s client 获取 Pod 列表
2)然后遍历检查每个 Pod 的状态,获取错误信息
3)最终将错误信息结构化为 Result 格式返回
完整代码如下:
|
|
在上一步 k8sgpt 拿到了集群中的异常信息,例如:
|
|
接下来就拿这个错误信息给 AI 生成解决方案,这里k8sgpt 用到的 prompt 模板如下:
以下是用于处理 Kubernetes 错误的 Prompt 模板:
|
|
这也是为什么我们看到的 Result 中的 detail 是这样的:
|
|
格式化之后
|
|
K8sGPT 作为一款基于 AI 的 Kubernetes 智能诊断工具,核心价值在于自动化集群故障检测与 AI 驱动的解决方案生成,大幅降低了 K8s 故障排查的技术门槛。
两种部署模式适配不同场景:CLI 模式适合临时诊断(如手动触发集群扫描),Operator 模式适合持续监控(结合 Prometheus 等工具实现实时告警与自动修复建议)。
工作流程清晰高效:通过内置的 Analyzer 扫描集群资源(Pod、Deployment 等)的异常状态,结合 LLM 模型(如 DeepSeek、OpenAI)生成结构化的解决方案,输出格式统一(错误描述 + 步骤化修复建议),便于快速落地。
灵活性强:支持多类 AI 后端(本地模型、云服务模型等)
注意:对于复杂故障(如网络策略冲突、持久化存储异常),AI 生成的解决方案可能需要结合人工验证,不可完全依赖自动化结果。
2025-06-18 04:00:00
在大模型时代,高质量训练数据的重要性不言而喻。本文全面介绍了 Easy Dataset 这一强大工具,它能帮助开发者和数据科学家从各种文档中快速构建结构化的问答数据集,大幅简化大模型训练数据的准备过程。
Easy Dataset 是一个强大的大模型数据集创建工具,有以下特点:
智能文档处理:支持 PDF、Markdown、DOCX、TXT 等多种格式智能识别和处理
智能文本分割:支持多种智能文本分割算法、支持自定义可视化分段
智能问题生成:从每个文本片段中提取相关问题
领域标签:为数据集智能构建全局领域标签,具备全局理解能力
答案生成:使用 LLM API 为每个问题生成全面的答案、思维链(COT)
灵活编辑:在流程的任何阶段编辑问题、答案和数据集
多种导出格式:以各种格式(Alpaca、ShareGPT)和文件类型(JSON、JSONL)导出数据集
广泛的模型支持:兼容所有遵循 OpenAI 格式的 LLM API
用户友好界面:为技术和非技术用户设计的直观 UI
自定义系统提示:添加自定义系统提示以引导模型响应
Github 地址:https://github.com/ConardLi/easy-dataset
这里简单使用 Docker 方式启动。
拉取镜像
|
|
部署
|
|
访问 17171 端口即可进入 Easy Dataset 界面。
|
|
在正式生成数据集之前还需要做一点准备工作。
EasyDataset 中以项目为单位进行管理,因此进入界面后需要先创建一个项目
项目创建后需要先配置一个模型才能正常使用,更多 -> 项目设置 -> 模型配置
Easy Dataset 支持 OpenAI 标准协议 的模型接入,兼容 Ollama,用户仅需配置 模型名称、API地址、密钥 即可完成适配,内置模型库预填主流厂商端点。
这里我们使用 DeepSeek,只需要填写 API Key 即可
完成后验证一下模型是否可用
配置好模型之后,就可以开始生成数据集了。
从原始文件到最终的数据集大概分为以下几个步骤:
1)文献处理:支持 Markdown、PDF、World、Excel 等格式文件,将上传的文档智能切分为小的文本块
2)问题生成:利用大模型,从文本块中提取问题
3)构造答案:利用大模型,根据问题以及对应文本块内容信息生成答案
4)导出数据集:以各种格式(Alpaca、ShareGPT)和文件类型(JSON、JSONL)导出数据集
Easy Dataset 支持多种类型(PDF、World、Excel)的文档,系统会自动解析并分割文档内容。
这里我们使用一个 PDF 进行测试:
注意:点击上传之前需要先选择模型,如界面右上角所示。
选择好之后点击上传并处理文件,系统就会开始解析并分割文档。
分割后如下:
切分参数也可以在 项目设置->任务配置 进行配置
接下来则是从分割好的文本块中提取问题,这块会用到配置的大模型来处理。
点击 自动提取问题,系统会后台异步自动查询待生成问题的文本块,并提取问题。
点击后可在任务管理中心可以看到处理进度
跳转到问题管理界面,即可查看本次生成的问题
问题生成参数也可以在 项目设置->任务配置 进行配置
Easy DataSet 会根据问题 + 问题对应的文本块 + 领域标签来一起生成答案,来保障答案和文献本身的相关性。
同样的,点击 自动生成数据集,Easy Dataset 会自动待生成答案的问题,并为其生成答案。
查看任务进度
答案生成完成后,就可以在 数据集管理 界面查看生成的数据集了,例如:
对于不满意的数据集,可以手动修改或者为 AI 提供修改建议,让 AI 进行修改。
修改完成后点击 确认保留 即可将该数据集标记为 已确认。
确认数据集操作并不是必须的,仅通过是否含有已确认标签便于进行区分。
数据集处理完成后,即可导出。
选择文件格式:支持 JSON、JSONL、Excel 三种格式
选择数据集风格:固定风格支持 Alpaca、ShareGPT
导出后的数据集内容如下:
|
|
至此,我们就完成了数据集生成工作,可以将该数据集用于后续的模型微调了。
Easy Dataset 是一个强大的大模型数据集创建工具,有以下特点:
智能文档处理:支持 PDF、Markdown、DOCX、TXT 等多种格式智能识别和处理
灵活编辑:在流程的任何阶段编辑问题、答案和数据集
多种导出格式:以各种格式(Alpaca、ShareGPT)和文件类型(JSON、JSONL)导出数据集
…
整个流程可以分为以下几个步骤:
1)文献处理:将上传的文档智能切分为小的文本块
2)问题生成:利用大模型,从文本块中提取问题
3)构造答案:利用大模型,根据文本块内容,为上一步生成的问题生成答案
4)导出数据集:以各种格式(Alpaca、ShareGPT)和文件类型(JSON、JSONL)导出数据集
通过 Easy Dataset 可以轻松的将 PDF、Markdown、DOCX、TXT 等文件构建为大模型数据集。
相较于直接将文档喂给 AI,然后通过 Prompt 引导其按照格式生成数据集,Easy Dataset 将整个过程拆分为多个步骤之后,效果会更好。
2025-06-05 04:00:00
上一篇中介绍了 Agent、Function Calling、MCP 等概念,这篇则分享如何实现自己的 MCP Server,并将其添加到 Cline 进行验证。
图源:https://blog.dailydoseofds.com/p/visual-guide-to-model-context-protocol
详细概念参考上文: 一文搞懂 Agent&Function Calling&MCP&A2A 是什么。
MCP(Model Context Protocol) 是由 Anthropic 推出的一项开放标准协议,旨在解决不同大模型与不同外部工具集成的标准化问题。官方解释是“其目标是为大型语言模型提供一种开放、标准化的方式,以便与外部数据源、工具和服务进行连接。”
它规定了上下文与请求的结构化传递方式,并要求通信格式符合 JSON-RPC 2.0 标准。
简单理解,MCP 相当于大模型领域的“HTTP 协议”,其并不绑定任何大模型,这意味着用户可以在支持 MCP 的工具中,用任何大模型调用 MCP 服务。
英文版描述:
The Model Context Protocol (MCP) is an open protocol that enables seamless integration between LLM applications and external data sources and tools. Whether you’re building an AI-powered IDE, enhancing a chat interface, or creating custom AI workflows, MCP provides a standardized way to connect LLMs with the context they need.
一句话描述:MCP 是一种开放、标准化的协议,以便大型语言模型能够与外部数据源、工具和服务进行连接。
架构如下:
MCP 涉及到 5 个组件或角色:
MCP Hosts: 真正使用 MCP 的程序,例如 Claude Desktop, IDEs 或者其他 AI 工具
MCP Clients: 实现 MCP 协议的客户端,维持和 MCP Server 之间的 1:1 连接
MCP Servers: 实现 MCP 协议的服务端,通过 MCP 协议公开某些功能,MCP服务器可以提供以下类型的功能:
Resources: 客户端可以读取的类似文件的数据(如API响应或文件内容)
Tools: 工具:可以由LLM调用的函数(经用户批准)
Prompts: 帮助用户完成特定任务的预先编写的模板
…
Local Data Sources: 本地数据源,MCP 服务可以安全访问的本地文件、数据库和其他服务
Remote Services: 远程服务,MCP 服务可以连接的外部系统
需要重点关注的就是 MCP Host、MCP Client 以及 MCP Server 三部分,Local Data Sources、Remote Services 可以看做是 MCP Server 自身实现功能的一部分。
MCP Host 就是直接跟用户交互的应用程序,例如 Claude,而 MCP Client 一般是直接集成在 MCP Host 里,这样应用程序既可以和用户交互,也可以调用 MCP Server。
MCP Server 就是提供 Resources、Tools、 Prompts 等等功能的服务端,可以简单理解为把 Function Calling 中的函数封装为了服务,满足 MCP 标准从而实现功能复用。
作为一个 Gopher,这里选择的是 MCP 的 Go 语言实现:mcp-go 这个库。
|
|
以下是一个简单的 Stdio 类型的 MCP Server,提供了一个名为 calculate
的 tool,接收 x、y 以及 operation 三个参数。
|
|
接下来就可以开始验证我们的 MCP Server 了。
SSE 类型的 MCP Server 和 Stdio 主要区别在 MCP Hosts(MCP Client) 如何与 MCP Server 做交互。
实际上代码很简单,只需要改动一点点:
Stdio 是这样启动服务的
|
|
SSE 则是这样:
|
|
然后启动服务
|
|
在验证 MCP Server 之前我们先安装一个 MCP Hosts,这里选择的是 Cline,大家也可以选择其他的。
首先在 VS Code 中安装 Cline 插件。
安装之后在侧边栏点击图标即可打开
在 Cline 最下方有一个配置模型的地方,点开填写相关信息即可。
如果没有可用模型,可以到 DeepSeek 官网 注册。
接下来就开始配置 MCP Server 了,将我们本地跑的 MCP Server 配置到 Cline 里。
在 Cline 中点击服务器图标即,再在弹出窗口中点击齿轮图标即可进入 MCP Server 配置界面
点击 Configure
之后就可以在 json 文件中配置了,我们这里启动的是 Stdio 类型的 MCP Server,也就是输入输出分别使用 Stdin 和 Stdout 的,只需要配置启动命令即可:
先 go build cmd/main.go 即可
|
|
对于 SSE 类型 MCP Server 则是
|
|
如果出现小绿点则说明 MCP Server 可以正常使用了。
至此,就可以开始验证 MCP Server 了。
问 Cline 一个问题
|
|
此时 MCP Hosts(Cline) 会自动携带问题和我们 MCP Server 的信息发送给 LLM,然后 LLM 根据思考会得出要调用前面我们配置的 mymcp 这个 MCP Server 中的 名为 calculate
的 tool,参数 x、y 以及 operation 分别是 1、1 和 add。
可以看到,最终的结果和预想的是一致的
我们点击 Approve 之后 Cline 就会去调用 MCP 了。
其中 MCP Server 返回的结果是 2.00,然后模型整合后返回最终的结果为:
|
|
至此,我们就完成了一个 Stdio 类型的 MCP Server 的开发和验证。
整个工作流程如下图所示:
该过程涉及到 3 个角色:
1)MCP Server:也就是我们实现的 MCP 服务
2)LLM:大模型,DeepSeek、ChatGPT 或者自己部署的其他模型都可以
3)Cline(MCP Hosts):在 MCP Server 和 LLM 之间交互,可以看做是一个 Agent
流程如下:
1)配置 MCP Server 到 Cline 之后,Cline 会获取该 MCP Server 提供的能力
2)用户给 Cline 发送任务
3)Cline 会带着问题和上一步获取到的信息发送给 LLM
4)LLM 根据问题和 MCP 信息决定该调用哪个 MCP Server 的哪个 tool,具体通过 <use_mcp_tool> </use_mcp_tool>
标记,例如:
|
|
5)Cline 解析 XML 并调用对应 MCP Server 拿到结果
6)Cline 将 MCP Server 结果返回给 LLM
7)LLM 整合后返回最终结果给 Cline
8)Cline 将最终结果展现给用户
MCP Hosts 会和大模型有两次交互,另外根据 MCP Hosts 实现不同,MCP 可以依赖或者不依赖 FunctionCalling。
主要就是要把本地支持的函数信息告诉 LLM,可以在system提示词里也可以使用functionCalling 放到 functiions 里面,最终效果都是一样的。
支持functionCalling的模型
输入:函数放到 functions 里
输出:output.tool_use 里面就是要调用的工具
对不支持functionCalling的模型
输入:函数放到提示词里,一般在system部分
输出:使用xml标签,比如<user_mcp_tool><use_mcp_tool/> 之类的格式指定要调用的函数的参数信息
可以对于不支持 functionCalling的 LLM 也可以达到同样的效果。
本文通过实战演示了 Anthropic 开源协议 MCP 的核心价值 —— 通过标准化接口实现大模型与外部工具的解耦集成。我们基于 Go 语言实现了两种典型部署形态的 MCP Server:
Stdio 类型:通过进程的标准输入输出完成交互,方便本地工具集成。
SSE 类型:采用 Server-Sent Events 技术实现长连接通信,支持远程接入。
从架构层面看,MCP 定义的三方角色(MCP Server 提供工具能力、LLM 负责逻辑决策、MCP Hosts 协调交互流程)形成了清晰的职责分层。这不仅让工具开发者可专注于功能实现(如本文的计算器工具),也让模型开发者能聚焦算法优化,最终通过标准化协议推动大模型生态从 “烟囱式开发” 向 “组件化协同” 演进。
随着更多工具接入 MCP 生态,这种标准化交互协议将成为连接大模型与垂直领域数据的 “数字桥梁”,为企业级 AI 应用开发提供更高效的基础设施。
最后再贴一下工作流程: