- 后端
- API网关
- LLM 网关
- 人工智能
- 大模型
- 本地部署
【免费下载链接】llama-swap
Reliable model swapping for any local OpenAI/Anthropic compatible server - llama.cpp, vllm, etc
导读
在 llama-swap 的 kubeswap 架构里,模型是按需加载、随 TTL 或交换事件被驱逐的:一个模型 Pod 今天被删除,下一次请求时才被重建,且可能落在另一个节点上。这决定了存放模型权重的共享缓存卷必须能被所有后端可能落脚的节点挂载——在实践中就是ReadWriteMany(RWX)。本文围绕 storage-options-kubernetes.md 展开,讲清 RWX 的驱动选择、单节点环境下用 RWO 加节点固定(node-selector)的逃生方案、以及完全不使用 PVC 的 emptyDir 按需拉取方案,并结合 kubeswap 源码说明它创建、采纳和校验 PVC 的完整行为。
为什么共享模型缓存必须是 ReadWriteMany
llama-swap 通过kubeswap serve为每个模型维护一个 Kubernetes Deployment。模型的生命周期由路由器驱动:
- 按需加载:收到请求、且该模型当前没有运行中的 Pod 时,kubeswap 才创建 Deployment,Pod 拉起后端服务(llama-server、sd-server、whisper-server、audiocpp_server 等);
- TTL 或 swap-in 驱逐:空闲超过
ttl、或同组内另一个模型需要抢占资源时,kubeswap 通过cmdStop(即kubeswap delete ...)删除 Pod; - 下一次请求重建:被驱逐的模型在下次请求时重新调度,调度器可以把它放到任何满足条件的节点上。
关键在于"可能换节点"这一环:如果共享模型缓存 PVC 是ReadWriteOnce(RWO),它只能被一个节点挂载。被驱逐的模型重新调度到另一个节点时,新 Pod 无法挂载该卷,会一直卡在ContainerCreating状态并报卷错误,而路由侧只能干等healthCheckTimeout超时——这既拖慢了请求,也让健康检查逻辑白白空转。
因此,只要你的模型可能在不同节点之间漂移,共享缓存就必须是ReadWriteMany:让每个后端可能落脚的节点都能挂载同一份权重。
提供 RWX 的存储驱动
文档给出的 RWX 驱动选型表如下:
| 驱动 | 说明 |
|---|---|
| Longhorn | 示例中的默认选项;通过其 NFS/replica 模式提供 RWX,适合家庭实验室(homelab) |
| CephFS(Rook) | 原生 RWX;集群已经跑 Rook 时的自然选择 |
| Azure Files | 原生 RWX;AKS 上的托管方案 |
| AWS EFS | 原生 RWX;EKS 上的托管方案 |
| NFS Server | 通过 NFS CSI 驱动或静态 PV 提供 RWX |
与之相对,云厂商的块存储(AWS EBS、GCP Persistent Disk、Azure Managed Disks)只支持ReadWriteOnce——不要为共享缓存选择它们。块存储适合单个节点独占的场景,无法满足"权重卷要随模型在节点间漂移"的需求。
存储规则与 GPU 无关:CPU-only 集群同样适用
这套选型规则并不依赖 GPU:
- 路由器头端(head-end)本身不需要 GPU;
- 不带
--gpu的模型会得到一个纯 CPU 的 Pod,可以调度到任意节点——kubeswap-kubernetes.md 示例中的 whisper 与 TTS 模型正是这样运行的(distil-whisper-lgv3只挂--volume pvc:llama-swap-models:/models:ro,qwen3-tts-06b以--backend cpu运行); - 一个 CPU 模型被驱逐后同样可能在任何 CPU 节点重建,所以多 CPU 节点共享缓存同样需要 RWX;
- 只有单节点的 CPU 环境才可以用 RWO 或本地卷,配合下面介绍的节点固定(pin)手段。
RWO 逃生方案:把后端 Pod 固定到单一节点
如果你的集群只有一个 GPU 节点(家庭实验室的常见形态),RWO 块存储完全可用——前提是每个后端都必须落在那个节点上。用--node-selector把 Pod 钉死即可:
cmd: >- kubeswap serve --listen 127.0.0.1:${PORT} --model lfm25-230m --namespace llama-swap --image ghcr.io/mostlygeek/llama-swap:unified-vulkan --gpu amd.com/gpu=1 --node-selector kubernetes.io/hostname=gpu-node # every backend, same node --volume pvc:llama-swap-models:/models:ro -- --model /models/model.gguf --port 8080固定之后,RWO PVC(EBS、PD、Longhorn RWO)在 Pod 运行的任何位置都可以挂载,因为 Pod 永远只会被调度到gpu-node这一个节点。一旦去掉固定,Pod 可能漂移,你又回到了必须使用 RWX 的场景。
从源码看,--node-selector是key=value形式、可重复传入的 flag(serve.go),渲染时被直接写入 Deployment 的pod.spec.nodeSelector;objects_test.go 中TestKubeswap_DeploymentSpecMatchesNodeSelector还验证了 node-selector 的漂移会被严格模式检测到并触发替换。
零存储逃生方案:emptyDir + 按需拉取
小模型可以完全跳过 PVC:每个 Pod 在首次加载时把权重下载进自己的emptyDir。Helm chart 的默认演示配置就是这么做的——values.yaml 中模型只挂了emptydir:model-cache:/models和emptydir:slots:/slots,并用-hf QuantFactory/SmolLM2-135M-Instruct-GGUF:Q4_0从 Hugging Face 拉取约 135MB 的权重:
--volume emptydir:model-cache:/models -- --model /models/SmolLM2-135M-Instruct-Q4_0.gguf -hf QuantFactory/SmolLM2-135M-Instruct-GGUF:Q4_0代价是每次重新加载都要重新下载:对 135MB 的演示模型可以接受,但对 16GB 级别的图像模型来说非常痛苦。emptyDir 的生命周期与 Pod 绑定,Pod 一旦被驱逐(TTL、swap-in、节点回收),缓存随之消失。
顺带一提,kubeswap 的--volume语法支持三种卷类型:pvc:name:path[:ro]、emptydir:name:path[:ro]和hostpath:nodePath:mountPath[:ro](可重复传入,serve.go)。其中hostpath会绕过 namespace 边界直接读写节点文件系统,kubeswap 在 serve.go 中对此给出明确警告,共享缓存场景下应优先使用 PVC。
kubeswap 对 PVC 的处理行为
结合 serve.go 与 objects.go 的实现,kubeswap 对 PVC 的处理遵循三条规则:
1. 缺失的 PVC 由kubeswap serve自动创建
当--volume pvc:<name>引用的 PVC 不存在时,kubeswap 调用renderPVC创建它,三个参数共同决定卷的形态:
| 参数 | 默认值 | 说明 |
|---|---|---|
--pvc-size | 1Gi | 创建的 PVC 的容量(见 serve.go) |
--pvc-class | 集群默认 StorageClass | 创建 PVC 时使用的存储类,空值即不指定,交给集群默认(见 serve.go) |
--pvc-access-mode | rwo | 取rwo或rwx,决定 PVC 的 accessModes(见 serve.go) |
在 objects.go 中,rwo映射为ReadWriteOnce,rwx映射为ReadWriteMany。当你预期模型缓存要在节点间漂移时,务必传--pvc-access-mode rwx——默认的rwo创建的卷无法被第二个节点挂载。
2. 已存在的 PVC 原样采纳,绝不重新标记
如果 PVC 已经存在(比如操作员预置的共享缓存),kubeswap 直接采纳而不做任何 relabel,也不会替换它——lifecycle_test.go 的TestKubeswap_EnsureResourcesAdoptsExistingPVC验证了这一点。因此,正确的做法是把缓存 PVC 声明在 Helm chart 的extraResources下,让 helm 负责跟踪(详见下节)。
3. 读写挂载与访问模式不匹配时,启动即报错
采纳既有 PVC 时,kubeswap 会校验其 access modes 能否支撑当前挂载方式(objects.go 的pvcAllowsReadWrite:ReadWriteOnce、ReadWriteOncePod、ReadWriteMany均可,ReadOnlyMany不可以)。如果模型以读写方式挂载(--volume pvc:name:/path,不带:ro)了一个只读的 PVC,kubeswap serve会直接失败,错误信息中指明 claim 名称和修复办法(加:ro或改用读写 PVC),而不是让 Pod 卡在ContainerCreating里。这一行为在 lifecycle_test.go 的TestKubeswap_EnsureResourcesRejectsReadOnlyPVCForWritableMount中覆盖了完整的分支:ROX PVC + 读写挂载报错、ROX PVC +:ro挂载可用、RWO/RWOP/RWX PVC + 读写挂载全部可用。
:ro挂载对任何 access mode 都成立——把共享缓存以只读方式挂载,把 KV slot 等可变状态放到 emptyDir 上(即--volume emptydir:slots:/slots+--slot-save-path /slots,如 kubeswap-kubernetes.md 所示),这是文档推荐的多节点部署形态。
用 chart 的 extraResources 声明缓存 PVC
既然 kubeswap 采纳已有 PVC 而非自行管理,让 helm 跟踪它才是正统做法——PVC 作为extraResources原样渲染并纳入 helm 生命周期管理。文档给出的完整示例:
# extraResources in the chart values — the idiomatic home for the cache PVC extraResources: - apiVersion: v1 kind: PersistentVolumeClaim metadata: name: llama-swap-models spec: accessModes: [ReadWriteMany] storageClassName: longhorn resources: requests: storage: 35Gi配合多引擎示例配置(kubeswap-kubernetes.md),llama-swap-models这个 RWX PVC 被所有模型以--volume pvc:llama-swap-models:/models:ro只读挂载,35Gi 的容量足以容纳 LLM、图像模型、whisper 与 TTS 的一整套权重(该示例中权重布局包括 LFM2.5-230M、krea-2-turbo、Qwen3-VL、wan/ideogram VAE、distil-large-v3、qwen3-tts 等文件)。
决策速览:你的集群该选哪种方案
- 多节点、模型可能漂移(GPU 或 CPU 集群)→ RWX 共享缓存:Longhorn / CephFS / Azure Files / AWS EFS / NFS,PVC 挂
:ro,slot 状态放 emptyDir; - 单 GPU 节点(homelab)→ 可以用 RWO 块存储,但必须用
--node-selector把所有后端固定到同一节点;去掉固定即回到 RWX 需求; - 小模型、可容忍重复下载→ 完全不建 PVC,
--volume emptydir:model-cache:/models+-hf repo:file按需拉取,135MB 级别的演示模型可接受,16GB 级别不推荐; - 任何形态都注意:期望缓存跨节点移动时给创建型 PVC 传
--pvc-access-mode rwx;已有 PVC 放extraResources里让 helm 跟踪;读写挂载与 PVC 访问模式不匹配会在启动阶段直接报错,不要指望 Pod 卡住后还能自愈。
延伸阅读
- kubeswap 多引擎完整配置示例:RWX 共享缓存 + 四类后端引擎的可直接复制的 config.yaml;
- kubeswap 命令参考与设计说明:
--volume、--pvc-*等全部 flag 的取值与默认值; - kubeswap Helm chart values:默认 emptyDir 演示配置与
extraResources、结构化配置的完整注释; - PVC 处理与卷渲染源码:
renderPVC、pvcAllowsReadWrite与renderVolumes的实现; - PVC 采纳与拒绝行为测试:既有 PVC 采纳、ROX 拒绝、RWO/RWOP/RWX 放行等全部用例。
- 后端
- API网关
- LLM 网关
- 人工智能
- 大模型
- 本地部署
【免费下载链接】llama-swap
Reliable model swapping for any local OpenAI/Anthropic compatible server - llama.cpp, vllm, etc
相关推荐
KServe PVC 初始化:用 Kubernetes Job 预下载 HuggingFace 模型权重,为 LLMInferenceService 提供高性能模型存储
KServe PVC 初始化:用 Kubernetes Job 预下载 HuggingFace 模型权重,为 LLMInferenceService 提供高性能
模型推理服务云原生后端微服务MLOps人工智能Unlock Music:浏览器中的音乐格式解密工具
Unlock Music:浏览器中的音乐格式解密工具 你是否曾经在不同音乐平台下载了歌曲,却发现这些文件只能在特定应用中播放?当你想在车载音响、本地播放器或其他
前端音频处理Odysseus记忆与技能系统:让AI代理随时间进化的持久化学习
Odysseus记忆与技能系统:让AI代理随时间进化的持久化学习 在当今AI技术飞速发展的时代, Odysseus记忆与技能系统 为自托管AI工作空间带来了革命
人工智能AI 应用后端前端RAGAI AgentMCP 服务本地部署深度研究
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考