资讯动态

Apple silicon 上基于 minikube krunkit 驱动的 AI Playground:本地 GPU 共享与大模型部署实战

发布时间:2026/9/19 12:51:33 来源:尧图企业网站定制
Apple silicon 上基于 minikube krunkit 驱动的 AI Playground本地 GPU 共享与大模型部署实战【免费下载链接】minikubeRun Kubernetes locally项目地址: https://gitcode.com/gh_mirrors/mi/minikube本教程以 site/content/en/docs/tutorials/ai-playground.md 为核心脉络完整演示如何在 Apple silicon如 MacBook Pro上借助 minikube 的 krunkit 驱动构建一个本地 AI 实验环境让 Kubernetes 集群直接共享 Mac 的 GPU部署 granite 与 tinyllama 两个大语言模型并通过 Open WebUI 以 OpenAI 兼容 API 进行对话交互。读完本文你将掌握 krunkit 驱动 virtio-fs 目录挂载 generic-device-plugin 设备插件的完整用法能够快速创建、销毁 GPU 集群而无需重复下载模型。前置条件开始之前请确认环境满足以下要求一台 Apple silicon MacM1/M2/M3/M4 系列arm64 架构krunkit v1.0.0 或更高版本——负责运行轻量级虚拟机vmnet-helper v0.6.0 或更高版本——负责虚拟机网络generic-device-plugin——将宿主机设备以扩展资源形式暴露给 Podminikube v1.37.0 或更高版本krunkit 驱动要求关于驱动支持范围从仓库源码可以确认krunkit 驱动仅在darwinarm64平台上注册状态检查逻辑会直接返回 the krunkit driver is only supported on macOS arm64 machines见 pkg/minikube/registry/drvs/krunkit/krunkit.go。同时该驱动在注册表中的Priority为registry.Experimental、Default为true同文件 init 注册段这解释了为什么minikube start的输出中会明确标注 Using the krunkit (experimental) driver。安装 krunkit 与 vmnet-helper安装 krunkit通过 Homebrew 安装最新版 krunkitbrew tap slp/krunkit brew install krunkit krunkit --version安装 vmnet-helpervmnet-helper 由 minikube 官方配套维护使用官方安装脚本安装curl -fsSL https://github.com/minikube-machine/vmnet-helper/releases/latest/download/install.sh | bash /opt/vmnet-helper/bin/vmnet-helper --version从驱动源码看krunkit 驱动运行时由两个进程协作krunkit负责虚拟机本体vmnet-helper负责网络。驱动的GetState()会同时检查两个进程的状态并返回组合结果Start()的顺序也是先启动vmnet-helper、再启动krunkit见 pkg/drivers/krunkit/krunkit.go因此两者缺一不可。下载模型到宿主机关键设计思路把模型文件放在 minikube 之外。这样创建、删除集群都无需重新下载动辄数 GB 的模型权重。mkdir ~/models cd ~/models curl -LO https://huggingface.co/instructlab/granite-7b-lab-GGUF/resolve/main/granite-7b-lab-Q4_K_M.gguf?downloadtrue curl -LO https://huggingface.co/TheBloke/TinyLlama-1.1B-Chat-v1.0-GGUF/resolve/main/tinyllama-1.1b-chat-v1.0.Q8_0.gguf?downloadtrue重要提醒模型必须为GGUF格式。GGUF 是 llama.cpp 生态的量化模型格式llama-server需要以该格式加载权重。启动 minikube 集群使用 krunkit 驱动启动集群并通过--mount-string把宿主机的~/models目录挂载到虚拟机内的/mnt/modelsminikube start --driver krunkit --mount-string ~/models:/mnt/models预期输出 minikube v1.37.0 on Darwin 15.6.1 (arm64) ✨ Using the krunkit (experimental) driver based on user configuration Starting minikube primary control-plane node in minikube cluster Creating krunkit VM (CPUs2, Memory6144MB, Disk20000MB) ... Preparing Kubernetes v1.34.0 on Docker 28.4.0 ... Configuring bridge CNI (Container Networking Interface) ... Verifying Kubernetes components... ▪ Using image gcr.io/k8s-minikube/storage-provisioner:v5 Enabled addons: storage-provisioner, default-storageclass Done! kubectl is now configured to use minikube cluster and default namespace by default--mount-string 的底层实现--mount-string的官方参数说明是 Directory to mount in the guest using format /host-path:/guest-path.见 cmd/minikube/cmd/start_flags.go。对于 krunkit 这类支持 virtiofs 的驱动挂载字符串会经过 pkg/drivers/common/virtiofs/virtiofs.go 的ValidateMountString解析校验按/host-path:/guest-path格式解析并校验路径合法性拒绝重复的宿主机路径或重复的客户机路径同一路径被多次共享会直接报错校验通过后转换为Mount{HostPath, GuestPath, Tag}结构。随后驱动配置阶段pkg/minikube/registry/drvs/krunkit/krunkit.go将挂载列表写入驱动的VirtiofsMounts字段。真正启动虚拟机时startKrunkit会为每个挂载生成一条virtio-fs设备参数--device virtio-fs,sharedDir宿主机路径,mountTag挂载标签见 pkg/drivers/krunkit/krunkit.go。同时由于 krunkit 属于支持 virtiofs 挂载的驱动minikube 不会再额外创建基于 9p 的用户态挂载进程相关分支见 pkg/minikube/node/config.go整个挂载链路更干净高效。验证 GPU 是否可用krunkit 驱动会把宿主机的 GPU 以virtio-gpu设备形式暴露给虚拟机。通过 SSH 进入虚拟机检查 DRIDirect Rendering Infrastructure设备% minikube ssh -- tree /dev/dri /dev/dri |-- by-path | |-- platform-a007000.virtio_mmio-card - ../card0 | -- platform-a007000.virtio_mmio-render - ../renderD128 |-- card0 -- renderD128/dev/dri/card0与/dev/dri/renderD128的存在说明 GPU 渲染节点已经成功透传进虚拟机接下来就可以通过设备插件把它暴露给 Kubernetes 的 Pod。部署 generic-device-plugin要让 Pod 能使用 GPU需要部署 generic-device-plugin——它以 DaemonSet 形式在每个节点运行把宿主机的/dev/dri设备目录注册为扩展资源devic.es/dricat EOF | kubectl apply -f - apiVersion: apps/v1 kind: DaemonSet metadata: name: generic-device-plugin namespace: kube-system labels: app.kubernetes.io/name: generic-device-plugin spec: selector: matchLabels: app.kubernetes.io/name: generic-device-plugin template: metadata: labels: app.kubernetes.io/name: generic-device-plugin spec: priorityClassName: system-node-critical tolerations: - operator: Exists effect: NoExecute - operator: Exists effect: NoSchedule containers: - image: squat/generic-device-plugin args: - --device - | name: dri groups: - count: 4 paths: - path: /dev/dri name: generic-device-plugin resources: requests: cpu: 50m memory: 10Mi limits: cpu: 50m memory: 20Mi ports: - containerPort: 8080 name: http securityContext: privileged: true volumeMounts: - name: device-plugin mountPath: /var/lib/kubelet/device-plugins - name: dev mountPath: /dev volumes: - name: device-plugin hostPath: path: /var/lib/kubelet/device-plugins - name: dev hostPath: path: /dev updateStrategy: type: RollingUpdate EOF关键配置解读groups[0].count: 4声明该设备组最多可供4 个 Pod 并发使用/dev/dri。若需要更多 Pod 共享 GPU可调大countsystem-node-critical优先级类 全容忍NoExecute/NoSchedule确保设备插件在节点调度、节点维护等场景下始终随节点运行挂载hostPath的/var/lib/kubelet/device-plugins与/dev前者是 kubelet 与设备插件通信的标准 socket 目录后者用于访问实际设备文件privileged: true设备插件需要特权访问底层设备节点。等待 DaemonSet 就绪% kubectl get daemonset generic-device-plugin -n kube-system -w NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE generic-device-plugin 1 1 1 1 1 none 45s部署 granite 模型接下来把下载好的 granite 模型部署为llama-serverPod使用 Ramalama 镜像内含 llama-server 可执行文件并配套一个 ClusterIP Service 供集群内其他 Pod 调用。注意通过resources.limits[devic.es/dri]声明 GPU 资源cat EOF | kubectl apply -f - --- apiVersion: apps/v1 kind: Deployment metadata: name: granite spec: replicas: 1 selector: matchLabels: app: granite template: metadata: labels: app: granite name: granite spec: containers: - name: llama-server image: quay.io/ramalama/ramalama:latest command: [ llama-server, --host, 0.0.0.0, --port, 8080, --model, /mnt/models/granite-7b-lab-Q4_K_M.gguf, --alias, ibm/granite:7b, --ctx-size, 2048, --temp, 0.8, --cache-reuse, 256, -ngl, 999, --threads, 6, --no-warmup, --log-colors, auto, ] resources: limits: devic.es/dri: 1 volumeMounts: - name: models mountPath: /mnt/models volumes: - name: models hostPath: path: /mnt/models --- apiVersion: v1 kind: Service metadata: labels: app: granite name: granite spec: ports: - protocol: TCP port: 8080 selector: app: granite EOFllama-server启动参数要点参数含义--model指定 GGUF 模型文件路径来自挂载的/mnt/models--alias模型对外暴露的别名如ibm/granite:7bOpen WebUI 中以此标识模型--ctx-size 2048上下文窗口大小token 数--temp 0.8采样温度控制生成随机性--cache-reuse 256提示词缓存复用窗口-ngl 999将尽可能多的层卸载到 GPU999 表示不限层数--threads 6CPU 线程数--no-warmup跳过启动预热--log-colors auto日志着色等待 Deployment 就绪并检查 Service% kubectl get deploy granite NAME READY UP-TO-DATE AVAILABLE AGE granite 1/1 1 1 8m17s % kubectl get service granite NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE granite ClusterIP 10.105.145.9 none 8080/TCP 28m部署 tinyllama 模型tinyllama 是体积更小约 1.1B 参数的模型部署方式与 granite 完全一致仅替换模型文件、别名与 GPU 资源配额。这里声明了devic.es/dri: 3与 granite 的1合计不超过设备插件声明的总数 4验证了多个模型可同时共享同一个 GPUcat EOF | kubectl apply -f - --- apiVersion: apps/v1 kind: Deployment metadata: name: tinyllama spec: replicas: 1 selector: matchLabels: app: tinyllama template: metadata: labels: app: tinyllama name: tinyllama spec: containers: - name: llama-server image: quay.io/ramalama/ramalama:latest command: [ llama-server, --host, 0.0.0.0, --port, 8080, --model, /mnt/models/tinyllama-1.1b-chat-v1.0.Q8_0.gguf, --alias, tinyllama, --ctx-size, 2048, --temp, 0.8, --cache-reuse, 256, -ngl, 999, --threads, 6, --no-warmup, --log-colors, auto, ] resources: limits: devic.es/dri: 3 volumeMounts: - name: models mountPath: /mnt/models volumes: - name: models hostPath: path: /mnt/models --- apiVersion: v1 kind: Service metadata: labels: app: tinyllama name: tinyllama spec: ports: - protocol: TCP port: 8080 selector: app: tinyllama EOF等待 Deployment 就绪并检查 Service% kubectl get deploy tinyllama NAME READY UP-TO-DATE AVAILABLE AGE tinyllama 1/1 1 1 9m14s % kubectl get service tinyllama NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE tinyllama ClusterIP 10.98.219.117 none 8080/TCP 23m至此集群内已存在两个暴露 OpenAI 兼容 API/v1路径的模型服务。部署 Open WebUIOpen WebUI 提供了开箱即用的 Web 界面用于与 OpenAI 兼容 API如我们的 llama-server Pod交互。部署包含三部分Web UI Deployment、数据持久化 PVC、NodePort Service--- cat EOF | kubectl apply -f - apiVersion: apps/v1 kind: Deployment metadata: name: open-webui spec: replicas: 1 selector: matchLabels: app: open-webui template: metadata: labels: app: open-webui spec: containers: - name: open-webui image: ghcr.io/open-webui/open-webui:dev-slim ports: - containerPort: 8080 env: # Preconfigure OpenAI-compatible endpoints - name: OPENAI_API_BASE_URLS value: http://granite:8080/v1;http://tinyllama:8080/v1 volumeMounts: - name: open-webui-data mountPath: /app/backend/data volumes: - name: open-webui-data persistentVolumeClaim: claimName: open-webui-data --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: open-webui-data spec: storageClassName: standard accessModes: - ReadWriteOnce resources: requests: storage: 1Gi --- apiVersion: v1 kind: Service metadata: name: open-webui spec: ports: - protocol: TCP port: 8080 nodePort: 30080 selector: app: open-webui type: NodePort EOF关键配置解读OPENAI_API_BASE_URLS以分号分隔列出两个模型服务的 OpenAI 兼容 API 地址。http://granite:8080/v1与http://tinyllama:8080/v1直接使用上一节创建的 ClusterIP Service 域名Kubernetes 内置 DNS 会自动完成解析PVCopen-webui-data使用默认的standardStorageClassminikube 启动时默认启用的default-storageclassaddon 提供将聊天记录、用户数据持久化在 1Gi 卷中Pod 重建后数据不丢失Service 类型为 NodePort宿主机30080端口对外暴露便于从浏览器访问。等待 Deployment 就绪% kubectl get deploy open-webui NAME READY UP-TO-DATE AVAILABLE AGE open-webui 1/1 1 1 69s与模型对话在浏览器中打开 Open WebUI 控制台open $(minikube service open-webui --url)首次访问需要创建 Admin 账号之后即可开始使用。与 granite 模型对话在模型菜单左上角中选择 ibm/granite:7b 模型开始对话。输入一个提示词例如 Write a very technical haiku about playing with large language models with Minikube on Apple silicon Mighty model, Minikube, Silicon-powered speed, Learnings dance, ever-changing. Through data streams it weaves, Inferences wisdom, vast and deep, Apples heartbeat, in code, resounds. Exploring AIs vast frontier, Minikube, language models playground, Innovations rhythm, forever.granite 会基于模型权重实时生成回答所有推理均在本地 GPU 上进行。与 tinyllama 模型对话点击左侧 New Chat 按钮在左上角模型菜单中选择 tinyllama 模型然后输入提示词 How do you feel inside this fancy Minikube cluster? I do not have a physical body. However, based on the given text material, the author is describing feeling inside a cluster of Minikube, a type of jellyfish. The use of the word fancy suggests that the author is impressed or appreciates the intricate design of the cluster, while the adjective minikube connotes its smooth texture, delicate shape, and iridescent colors. The word cluster suggests a group of these jellyfish, while inside implies being in the vicinity or enclosed within.两个模型可以并行使用granite 用于较重的中型任务tinyllama 适合低延迟的轻量对话二者共享同一块 Mac GPU。更进一步理解 krunkit 驱动的运行机制至此已跑通完整链路下面从仓库源码补充几个理解要点方便排查问题或自行扩展1. 双进程架构与状态管理。krunkit 驱动由krunkit虚拟机与vmnet-helper网络两个进程构成。GetState()先检查 krunkit 进程通过krunkit.pid文件再检查 vmnet-helper任一异常都会反映到minikube status中pkg/drivers/krunkit/krunkit.go。运行时产物krunkit.log、serial.log、krunkit.sock等都写入机器目录遇到启动异常可查看这些日志。2. 设备透传由 krunkit 命令行参数决定。startKrunkit构造的启动参数中除了 virtio-fs 挂载外还包括内存、CPU、virtio-net通过 unixgram socket 连接 vmnet-helper、virtio-serial串口日志、以及两块virtio-blk磁盘ISO 与数据盘pkg/drivers/krunkit/krunkit.go。GPU 透传即由 krunkit 本身对 Apple silicon 的支持提供暴露为文档中验证到的 virtio-gpu 设备。3. 设备插件数量与 GPU 配额的对应关系。generic-device-plugin 中count: 4定义了资源总量Pod 的devic.es/drilimits 则是每次调度占用的份额。本教程中 granite 占 1、tinyllama 占 3正好用满配额若需部署更多模型需同步上调count。4. 模型目录隔离带来的运维收益。由于模型在宿主机~/models、通过 virtio-fs 只读共享进虚拟机minikube delete后重建集群无需重新下载模型即便更换驱动或升级 minikube模型资产也始终独立保留。常见注意事项版本对齐krunkit 驱动需要 minikube v1.37.0krunkit v1.0.0vmnet-helper v0.6.0低于这些版本可能导致驱动不可用或网络异常平台限制krunkit 驱动仅支持 macOS arm64Intel Mac 请改用其他驱动如 Docker、QEMU、VirtualBoxGPU 共享能力也随之不同模型格式务必使用 GGUF 格式模型文件llama-server 无法加载其他格式的权重资源配额若 Pod 一直处于 Pending 状态优先检查kubectl describe pod中是否有 Insufficient devic.es/dri 类事件并按需调整设备插件的count。通过以上步骤你已经在 Apple silicon 上拥有了一个完全本地、可随时重建的 Kubernetes AI 实验环境——这也是 minikube 官方教程给出的推荐玩法把模型资产与集群生命周期彻底解耦让 GPU 集群的实验成本降到最低。【免费下载链接】minikubeRun Kubernetes locally项目地址: https://gitcode.com/gh_mirrors/mi/minikube创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价