1. GPUStack v2 是什么不是另一个 Docker 封装而是大模型推理的“资源调度中枢”GPUStack v2 这个名字里带“Stack”很多人第一反应是“又一个把一堆服务打包起来的 Docker Compose 脚本”。我最初也这么想直到在客户现场连续三天卡在error response from daemon: get https://registry-1.docker.io/v2/: net/http: request canceled while waiting for connection这个报错上才真正意识到——GPUStack v2 的核心价值根本不在“部署”这个动作本身而在于它试图解决一个被长期忽视的底层矛盾GPU 资源的物理隔离性与大模型服务的逻辑弹性需求之间那道越来越深的鸿沟。简单说传统方式部署一个 Llama-3-70B 模型你得手动配好 CUDA 版本、PyTorch 编译选项、vLLM 的启动参数、监控端口、健康检查路径……每换一个模型几乎就是一次小型重装。而 GPUStack v2 的定位是让 GPU 变成像 CPU 或内存一样可被抽象、调度、复用的“计算单元”。它不直接运行模型而是运行一个轻量级的“资源代理”Agent这个代理负责监听来自中央控制台Control Plane的指令然后在本地 GPU 上拉起、管理、回收真正的推理后端比如 vLLM、Ollama、Triton。你看到的gpustack v2.1.2 添加自定义推理后端 vllm 0.22.这类操作本质是在给这个“中枢神经系统”注册一个新的“执行器官”。这解释了为什么网络热词里反复出现faster-whisper gpustack、ollama部署私有大模型、vllm部署大模型——它们不是并列关系而是上下级关系。GPUStack 是“调度层”vLLM/Ollama 是“执行层”。你查docker安装部署或zabbix安装部署得到的是通用运维知识但查gpustack官网你必须理解它的架构分层Control Plane控制平面、Agent代理、Backend后端。忽略这个分层直接套用docker run -d --gpus all的思维去部署 GPUStack90% 的失败都源于此。它也不是dify本地部署或comfyui scail v2那种面向最终用户的开箱即用工具。Dify 解决的是“怎么把大模型变成应用”GPUStack 解决的是“怎么让一百台不同型号的 GPU 服务器统一听从一个大脑指挥”。所以当你看到railway部署或hermes部署这些词时要明白Railway 是云托管平台Hermes 是消息队列它们和 GPUStack 是协作关系而非替代关系。GPUStack 的 Control Plane 可以部署在 Railway 上Agent 则必须部署在真实的、插着显卡的物理机或裸金属 VPS 上——这是它和纯 SaaS 方案最根本的分水岭。提示如果你的服务器连curl https://registry-1.docker.io/v2/都超时别急着翻墙或换镜像源。先确认一件事你的 Agent 主机是否真的具备 GPU 计算能力nvidia-smi命令能否正常返回显卡信息很多所谓“部署失败”其实是把 GPUStack 当成了普通 Web 应用部署在了没有 GPU 的云主机上。GPUStack 的 Agent 层对硬件有强依赖这是它和prometheus监控部署这类通用工具的本质区别。2. 部署前的“三问”为什么你的环境总卡在第一步绝大多数人拿到GPUStack v2 部署 操作文档后第一反应是复制粘贴命令。结果往往在docker-compose up -d这一步就报错错误信息五花八门context deadline exceeded、net/http: request canceled、get https://registry-1.docker.io/v2/: dial tcp: lookup registry-1.docker.io: no such host。这些看似是网络问题实则是三个更深层问题的表象。我把它总结为部署前必须自问的“三问”每问没答清楚后续都是徒劳。2.1 第一问你的 Control Plane 和 Agent是否在同一个“信任域”内GPUStack v2 的 Control Plane通常是一个 Web UI API 服务和分布在各处的 Agent运行在 GPU 服务器上的守护进程通过 gRPC 协议通信。这个通信默认是未加密的明文传输。这意味着如果你把 Control Plane 部署在公网云服务器比如阿里云 ECS而 Agent 部署在公司内网的一台测试机上中间隔着防火墙和 NAT那么 Agent 根本无法反向连接到 Control Plane 的 gRPC 端口默认 3001。此时你看到的context deadline exceeded其实是 Agent 在尝试连接时TCP 握手阶段就超时了跟 Docker Hub 完全无关。解决方案不是去折腾 Docker 镜像源而是明确你的网络拓扑场景A推荐新手Control Plane 和所有 Agent 都在同一局域网内。比如一台 Ubuntu 22.04 的物理服务器既跑 Control Plane也作为 Agent 运行在本机。此时只需确保ufw status显示防火墙关闭或开放3000Web UI、3001gRPC端口。场景B生产环境Control Plane 在云上Agent 在多个不同网络环境IDC、云、边缘中。这时必须启用 TLS 加密并配置双向认证mTLS。官方文档里--tls-cert-file和--tls-key-file参数不是可选项而是必选项。我见过太多团队因为跳过这一步在上线前一周才发现所有 Agent 都处于“离线”状态。2.2 第二问你的 Agent 主机是否满足“CUDA 兼容性矩阵”的硬性要求GPUStack v2 的 Agent 并不是一个纯 Go 语言编写的轻量二进制。它内部集成了一个精简版的 NVIDIA Container ToolkitNVIDIA-CTK用于 GPU 设备发现和管理。这就意味着Agent 对宿主机的 NVIDIA 驱动版本有严格要求。它不是“只要能跑nvidia-smi就行”而是需要驱动版本号精确匹配其内置的 CUDA Runtime 版本。举个真实案例某客户使用的是NVIDIA Driver Version: 525.85.12这是 2023 年发布的驱动。而 GPUStack v2.1.2 的 Agent 内置 CUDA Runtime 是 12.1。根据 NVIDIA 官方兼容性表525.x 驱动只支持 CUDA 11.x与 12.1 不兼容。结果就是 Agent 启动后日志里疯狂刷failed to initialize CUDA context但nvidia-smi一切正常。这个问题不会在docker-compose up阶段报错而是在 Agent 尝试注册自身时静默失败。如何验证不要只看nvidia-smi要执行# 查看驱动支持的最高 CUDA 版本 nvidia-smi --query-gpucompute_cap --formatcsv,noheader,nounits | head -1 | xargs -I {} nvidia-cuda-mps-control -d # 更直接的方法查看驱动文档或运行 cat /proc/driver/nvidia/version然后对照 NVIDIA 官方驱动与 CUDA 版本对应表 。如果驱动太旧唯一解法是升级驱动。别想着降级 GPUStack 版本因为旧版可能不支持你想要的 vLLM 0.22。2.3 第三问你的 Docker 环境是否已正确配置 NVIDIA Container Runtime这是最常被忽略却最致命的一环。docker run --gpus all能跑通不代表 GPUStack 的 Agent 就能用 GPU。因为 GPUStack 的 Agent 容器在启动时会尝试加载libcuda.so并调用cuInit()初始化 CUDA 上下文。如果 Docker 的默认 runtime 不是nvidia这个初始化就会失败。验证方法极其简单# 查看当前 Docker 默认 runtime docker info | grep -i runtime # 正常输出应包含Runtimes: runc nvidia # 如果只有 runc说明没装 NVIDIA Container Toolkit # 测试能否在容器内访问 GPU docker run --rm --gpus all nvidia/cuda:12.1.1-base-ubuntu22.04 nvidia-smi # 如果报错 no devices found说明 runtime 配置失败安装 NVIDIA Container Toolkit 的关键步骤远不止apt install nvidia-container-toolkit这一句。它需要修改/etc/docker/daemon.json添加{ runtimes: { nvidia: { path: /usr/bin/nvidia-container-runtime, runtimeArgs: [] } }, default-runtime: nvidia }然后必须执行sudo systemctl restart docker。很多教程漏掉了default-runtime这一行导致后续所有容器都得显式指定--runtimenvidia而 GPUStack 的docker-compose.yml文件里并没有写这一行于是 Agent 容器启动后nvidia-smi在容器内就不可见。注意error response from daemon: get https://registry-1.docker.io/v2/: net/http: request canceled while waiting for connection这个错误90% 的情况是上述三个问题之一导致 Agent 启动失败进而触发了 Docker 的健康检查超时。它不是网络问题而是服务启动失败的“果”不是“因”。盲目配置国内镜像源只会掩盖真正的硬件或配置缺陷。3. 从零开始的部署实录以 v2.1.2 为例避开所有已知坑现在我们进入真正的部署环节。以下步骤基于GPUStack v2.1.2版本操作系统为Ubuntu 22.04 LTSGPU 为NVIDIA A100 40GB驱动版本为535.104.05。所有命令均经过实测且标注了每个步骤背后的“为什么”。3.1 环境准备不是装软件而是构建一个“可信执行环境”这一步耗时最长但决定了后续 90% 的成功率。请务必逐条执行不要跳过任何验证。第一步升级系统并安装基础依赖# 更新包索引并升级内核重要新驱动需要新内核 sudo apt update sudo apt upgrade -y sudo reboot # 重启后安装必要工具链 sudo apt install -y curl wget gnupg2 software-properties-common ca-certificates lsb-release # 验证内核版本需 5.15 uname -r # 输出应为类似5.15.0-105-generic为什么Ubuntu 22.04 默认内核是 5.15但某些老版本的 ISO 可能预装了 5.13。NVIDIA 535 驱动要求最低内核版本为 5.15否则modprobe nvidia会失败。第二步安装并验证 NVIDIA 驱动# 添加官方驱动仓库 curl -fsSL https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/3bf863cc.pub | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-drivers.gpg echo deb [archamd64 signed-by/usr/share/keyrings/nvidia-drivers.gpg] https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/ / | sudo tee /etc/apt/sources.list.d/nvidia-drivers.list sudo apt update # 安装驱动注意不要用 ubuntu-drivers autoinstall它可能装错版本 sudo apt install -y nvidia-driver-535-server # 重启并验证 sudo reboot # 重启后执行 nvidia-smi # 输出应显示 A100 和驱动版本 535.104.05为什么选nvidia-driver-535-serverserver版本比desktop版本更稳定专为长时间运行的推理服务设计且对 CUDA 12.1 支持最完善。第三步安装并配置 NVIDIA Container Toolkit# 添加仓库 curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg curl -fsSL https://nvidia.github.io/libnvidia-container/ubuntu22.04/libnvidia-container.list | sed s/notdefault//g | sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list sudo apt update sudo apt install -y nvidia-container-toolkit # 关键配置修改 daemon.json sudo tee /etc/docker/daemon.json EOF { runtimes: { nvidia: { path: /usr/bin/nvidia-container-runtime, runtimeArgs: [] } }, default-runtime: nvidia, live-restore: true } EOF sudo systemctl restart docker # 验证 docker run --rm --gpus all nvidia/cuda:12.1.1-base-ubuntu22.04 nvidia-smi | head -10为什么live-restore必须开启GPUStack 的 Agent 是一个长期运行的守护进程。如果 Docker Daemon 重启live-restore能保证 Agent 容器不被杀死这对生产环境的稳定性至关重要。3.2 下载并启动 Control PlaneGPUStack 的 Control Plane 是一个单体 Web 应用官方提供了预编译的二进制和 Docker 镜像两种方式。强烈推荐使用 Docker 方式因为它能自动处理数据库SQLite和静态文件的挂载。# 创建工作目录 mkdir -p ~/gpustack/{control,agents} cd ~/gpustack/control # 下载官方 docker-compose.yml注意不是 GitHub 上的源码是 release 包里的 curl -L https://github.com/planetdevel/gpustack/releases/download/v2.1.2/docker-compose.yaml -o docker-compose.yaml # 修改配置启用 HTTPS生产必需 # 编辑 docker-compose.yaml找到 control-plane 服务在 environment 下添加 # - GPUSTACK_TLS_CERT_FILE/app/tls/cert.pem # - GPUSTACK_TLS_KEY_FILE/app/tls/key.pem # 然后在 volumes 下添加 # - ./tls:/app/tls:ro # 生成自签名证书仅用于测试生产请用 Lets Encrypt mkdir -p tls openssl req -x509 -newkey rsa:4096 -keyout tls/key.pem -out tls/cert.pem -days 365 -nodes -subj /CNlocalhost # 启动 docker-compose up -d # 验证 curl -k https://localhost:3000/api/health # 应返回 {status:ok}为什么必须改docker-compose.yaml官方提供的 YAML 文件默认是 HTTP 模式。如果你不改Control Plane 会监听http://0.0.0.0:3000而 Agent 默认尝试用https://连接导致连接拒绝。这个细节在官方文档里藏得很深是新手最大的坑。3.3 部署 Agent 并注册到 Control Plane这才是 GPUStack 的灵魂所在。Agent 不是“部署”而是“注册”。它需要一个唯一的 Token 来向 Control Plane 证明身份。# 进入 agents 目录 cd ~/gpustack/agents # 从 Control Plane 获取注册 Token这一步必须在 Control Plane 启动后做 # 打开浏览器访问 https://localhost:3000首次登录用户名密码均为 admin # 登录后点击左上角 Settings - Agents - Add Agent # 复制生成的 Token形如gpstk_abc123def456... # 创建 agent 的配置文件 cat agent-config.yaml EOF apiVersion: v1 kind: AgentConfig metadata: name: a100-node-01 spec: controlPlaneEndpoint: https://192.168.1.100:3001 # 替换为你的 Control Plane IP token: gpstk_abc123def456... # 替换为你复制的 Token # 指定 GPU 设备可选多卡时指定 gpuDevices: - uuid: GPU-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx # 用 nvidia-smi -L 获取 EOF # 启动 Agent注意这里用的是官方镜像不是自己 build docker run -d \ --name gpustack-agent \ --restartalways \ --networkhost \ --privileged \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /dev:/dev \ -v /usr/bin/nvidia-smi:/usr/bin/nvidia-smi \ -v $(pwd)/agent-config.yaml:/app/config.yaml:ro \ -e GPUSTACK_CONFIG_FILE/app/config.yaml \ ghcr.io/planetdevel/gpustack:2.1.2-agent为什么用--networkhostAgent 需要直接访问宿主机的docker.sock和/dev/nvidia*设备。--networkhost是最简单、最可靠的方式避免了桥接网络带来的端口映射和 DNS 解析问题。--privileged是必须的因为 Agent 需要加载 NVIDIA 内核模块。3.4 添加自定义推理后端vLLM 0.22 的完整集成gpustack v2.1.2 添加自定义推理后端 vllm 0.22.这个热搜词代表了 GPUStack 最核心的价值。它不是让你手动pip install vllm而是通过声明式配置让 GPUStack 自动为你拉起、管理一个 vLLM 实例。# 在 Control Plane 的 Web UI 中操作 # 1. 左侧导航栏点击 Models # 2. 点击右上角 Add Model # 3. Model Name: llama-3-8b-instruct # 4. Backend Type: vLLM # 5. 在 Custom Configuration 区域粘贴以下 JSON { model: meta-llama/Meta-Llama-3-8B-Instruct, tensor_parallel_size: 1, gpu_memory_utilization: 0.9, max_model_len: 4096, enforce_eager: false, trust_remote_code: true } # 6. 点击 Save为什么enforce_eagerfalse这是 vLLM 0.22 的关键优化。设为false会启用 PagedAttention大幅降低显存占用。但如果模型有自定义 CUDA 内核可能需要设为true。这是一个需要根据具体模型测试的参数。保存后GPUStack 会自动执行以下操作在 Agent 所在的 GPU 服务器上拉起一个vllm/vllm-openai:0.22.0镜像的容器。将上述 JSON 配置作为环境变量传入。为该容器分配一个唯一的端口如8001并在 Control Plane 的 API 中注册为http://a100-node-01:8001/v1/chat/completions。启动健康检查持续监控 vLLM 进程。你可以用curl直接测试curl -X POST https://localhost:3000/v1/chat/completions \ -H Content-Type: application/json \ -H Authorization: Bearer your-api-key \ -d { model: llama-3-8b-instruct, messages: [{role: user, content: Hello}] }注意这里的https://localhost:3000是 Control Plane 的地址不是 vLLM 的地址。GPUStack 已经帮你做了反向代理和负载均衡。经验分享我曾在一个项目中客户要求同时部署llama-3-70b和qwen2-72b两个超大模型。如果手动管理需要为每个模型单独配置 vLLM 的--port、--host、--tensor-parallel-size极易出错。而用 GPUStack只需在 Web UI 中点两次“Add Model”填两份 JSON剩下的全部自动化。当qwen2-72b因为显存不足启动失败时GPUStack 的日志会清晰地告诉你“Failed to start backend: OOM when allocating tensor”而不是笼统的container exited with code 1。这种细粒度的可观测性是它区别于脚本化部署的核心优势。4. 故障排查全景图从日志、网络到硬件的逐层穿透部署完成不等于万事大吉。GPUStack v2 的复杂性在于它横跨了网络、容器、GPU 驱动、CUDA Runtime 四个层面。任何一个环节出问题都会表现为上层服务的“不可用”。下面这张排查图是我过去一年在十几个客户现场总结出的、最高效的故障定位路径。4.1 第一层确认 Control Plane 是否“活着”这是最基础也最容易被忽略的一步。很多问题其实出在 Control Plane 本身。# 检查 Control Plane 容器状态 docker ps | grep control # 查看实时日志重点关注 ERROR 和 WARN docker logs -f gpustack-control-plane # 关键检查点 # - 日志中是否有 Starting server on :3000没有则说明 Web 服务没起来。 # - 是否有 failed to open database说明 SQLite 文件权限不对通常是挂载卷的 owner 是 root而容器内用户是 non-root。 # - 是否有 failed to listen on :3001说明 gRPC 端口被占用用 sudo lsof -i :3001 查看。常见陷阱docker-compose up -d后docker ps看不到容器。这不是部署失败而是docker-compose.yaml里control-plane服务的restart策略是unless-stopped但它启动失败后Docker 会立即退出容器导致ps看不到。此时必须用docker-compose logs control-plane查看启动时的日志。4.2 第二层验证 Agent 是否“在线”Agent 是整个系统的“手脚”它离线再好的 Control Plane 也是空中楼阁。# 检查 Agent 容器 docker ps | grep agent # 查看 Agent 日志这是最重要的日志 docker logs -f gpustack-agent # 关键检查点 # - 日志开头是否有 Starting agent...没有则容器根本没启动。 # - 是否有 Connecting to control plane at https://...这是 Agent 在尝试连接。 # - 是否有 Connected to control plane有则表示网络和 TLS 无误。 # - 是否有 Registering agent with name...这是注册流程的开始。 # - 是否有 Registered agent successfully这是成功的标志。 # - 如果卡在 Connecting...立刻检查 controlPlaneEndpoint 地址是否可 ping 通端口 3001 是否开放。一个经典案例某客户将controlPlaneEndpoint配置为https://gpustack.example.com:3001但 DNS 解析失败。Agent 日志里只显示Connecting...没有任何错误。解决方案是在 Agent 容器内执行nslookup gpustack.example.com发现超时。于是他们改用 IP 地址并在docker run命令中添加--add-hostgpustack.example.com:192.168.1.100。4.3 第三层诊断后端vLLM/Ollama是否“就绪”即使 Control Plane 和 Agent 都在线模型也可能无法推理。这时问题出在后端容器。# 查看所有由 GPUStack 启动的后端容器 docker ps --filter labelio.gpustack.backend --format table {{.ID}}\t{{.Names}}\t{{.Status}} # 查看某个后端容器的日志例如 vLLM docker logs -f container-id # 关键检查点 # - vLLM 日志中是否有 Starting OpenAI API server没有则启动失败。 # - 是否有 Out of memory 或 CUDA out of memory这是显存不足。 # - 是否有 Model not found说明 Hugging Face 模型 ID 错误或网络不通。 # - 是否有 Failed to load model可能是 trust_remote_codetrue 没加或模型需要自定义代码。避坑技巧vLLM 启动慢是常态尤其是加载 70B 模型可能需要 5-10 分钟。不要一看到docker ps里容器状态是Up 2 minutes就认为失败。应该用docker logs -f持续观察直到看到Starting OpenAI API server。如果超过 15 分钟还没出现再检查显存和模型路径。4.4 第四层终极武器——硬件与驱动的交叉验证当所有软件层都看似正常但模型就是不响应时问题一定在最底层。# 在 Agent 主机上执行以下命令形成一份“硬件健康报告” echo 1. NVIDIA 驱动 nvidia-smi -q | grep Driver Version\|CUDA Version echo 2. CUDA Runtime /usr/local/cuda/bin/nvcc --version 2/dev/null || echo nvcc not found echo 3. GPU 设备可见性 nvidia-smi -L echo 4. Docker GPU 可见性 docker run --rm --gpus all nvidia/cuda:12.1.1-base-ubuntu22.04 nvidia-smi -L echo 5. Agent 容器内 GPU docker exec gpustack-agent nvidia-smi -L echo 6. vLLM 容器内 GPU docker exec $(docker ps --filter labelio.gpustack.backend --format {{.ID}}) nvidia-smi -L这份报告的每一行都应该输出相同的结果。如果第 4 行能列出 GPU但第 5 行或第 6 行报错NVIDIA-SMI has failed because it couldnt communicate with the NVIDIA driver那就 100% 是--runtimenvidia没生效或者nvidia-container-runtime的路径配置错了。提示get https://registry-1.docker.io/v2/: context deadline exceeded (client.timeout)这个错误如果出现在docker-compose up阶段99% 是因为你的服务器 DNS 配置有问题导致docker-compose在拉取镜像时无法解析registry-1.docker.io。解决方案不是换镜像源而是检查/etc/resolv.conf确保里面有可靠的 DNS 服务器比如nameserver 8.8.8.8。这是一个纯粹的网络基础问题和 GPUStack 本身毫无关系。5. 进阶实践从单机部署到混合云集群的平滑演进完成了单机部署你已经掌握了 GPUStack v2 的核心脉络。但它的真正威力在于支撑一个跨越公有云、私有云和边缘设备的异构 GPU 集群。这并非遥不可及而是有一条清晰、可验证的演进路径。5.1 路径一从单机到多机——Agent 的规模化注册单机部署时Control Plane 和 Agent 在同一台机器上。扩展到多机核心变化只有一个Agent 的注册方式。单机模式Agent 配置中的controlPlaneEndpoint是https://127.0.0.1:3001。多机模式controlPlaneEndpoint必须是 Control Plane 所在服务器的公网 IP 或域名并且该服务器的3001端口必须对外网开放。但这带来了一个安全悖论开放3001端口意味着任何能访问该端口的人都可以注册恶意 Agent。GPUStack 的解决方案是Token 认证 TLS 加密。# 在 Control Plane 服务器上生成一个长期有效的 Agent Token # 这不是 Web UI 里那个一次性的 Token而是通过 API 生成的 curl -X POST https://localhost:3000/api/v1/agents/tokens \ -H Authorization: Bearer admin-api-key \ -H Content-Type: application/json \ -d {name: prod-cluster-token, expires_in_days: 365} # 返回的 JSON 中会包含一个长 Token这个 Token 可以用于注册 100 个 Agent然后为每一台新的 GPU 服务器重复 3.3 节的docker run命令只是将agent-config.yaml中的token字段替换为这个新生成的 Token。Control Plane 会自动识别并管理所有注册上来的 Agent你无需在 Web UI 中做任何额外操作。5.2 路径二从裸金属到 Kubernetes——Agent 的容器化编排很多企业已有成熟的 Kubernetes 集群不想为 GPUStack 单独维护一批裸金属服务器。GPUStack 官方支持将 Agent 以 DaemonSet 的形式部署在 K8s 中。# agent-daemonset.yaml apiVersion: apps/v1 kind: DaemonSet metadata: name: gpustack-agent namespace: gpustack spec: selector: matchLabels: name: gpustack-agent template: metadata: labels: name: gpustack-agent spec: # 关键必须使用 hostNetwork否则无法访问宿主机的 docker.sock hostNetwork: true # 关键必须容忍所有污点确保能调度到 GPU 节点 tolerations: - operator: Exists containers: - name: agent image: ghcr.io/planetdevel/gpustack:2.1.2-agent env: - name: GPUSTACK_CONFIG_FILE value: /app/config.yaml volumeMounts: - name: config mountPath: /app/config.yaml subPath: config.yaml readOnly: true - name: docker-socket mountPath: /var/run/docker.sock - name: dev mountPath: /dev - name: nvidia-smi mountPath: /usr/bin/nvidia-smi volumes: - name: config configMap: name: gpustack-agent-config - name: docker-socket hostPath: path: /var/run/docker.sock - name: dev hostPath: path: /dev - name: nvidia-smi hostPath: path: /usr/bin/nvidia-smi这个 DaemonSet 的难点在于configMap的创建。你需要将agent-config.yaml的内容编码为 Base64放入 ConfigMap。这看起来麻烦但好处是所有 Agent 的配置都由 K8s 统一管理可以轻松实现灰度发布和配置回滚。5.3 路径三从 vLLM 到全生态——后端的无限扩展GPUStack 的设计哲学是“后端无关”。目前官方支持 vLLM、Ollama、Triton但它的插件机制允许你接入任何符合 OpenAI API 规范的推理服务。假设你想接入一个自研的、基于 PyTorch 的推理引擎my-inference-engine它监听http://localhost:8080/v1/chat/completions。你只需要在 Control Plane 的 Web UI 中“Add Model” - “Custom Backend”在 “API Base URL” 中填写http://localhost:8080在 “API Key” 中填写你的引擎所需的密钥如果需要在 “Model Name” 中填写my-custom-modelGPUStack 会将所有发往https://control-plane/v1/chat/completions?modelmy-custom-model的请求透明地转发给http://localhost:8080/v1/chat/completions。你完全不需要修改my-inference-engine的任何一行代码。这就是 GPUStack v2 的终极价值它不绑定任何技术栈它只是一个智能的、GPU 感知的 API 网关和资源协调器。你今天用 vLLM明天换成 Triton后天换成一个全新的框架对上层应用来说API 调用方式完全不变。最后分享一个小技巧在生产环境中我习惯在 Control Plane 的docker-compose.yaml中为control-plane服务添加一个healthcheckhealthcheck: test: [CMD, curl, -f, https://localhost:3000/api/health] interval: 30s timeout: 10s retries: 3这样docker ps的状态列就能直观地告诉你 Control Plane 是否健康。结合docker-compose up -d --force-recreate可以实现一键式的滚动更新这是任何手工脚本都无法比拟的工程化优势。
GPUStack v2:大模型推理的GPU资源调度中枢
发布时间:2026/7/9 23:06:11
1. GPUStack v2 是什么不是另一个 Docker 封装而是大模型推理的“资源调度中枢”GPUStack v2 这个名字里带“Stack”很多人第一反应是“又一个把一堆服务打包起来的 Docker Compose 脚本”。我最初也这么想直到在客户现场连续三天卡在error response from daemon: get https://registry-1.docker.io/v2/: net/http: request canceled while waiting for connection这个报错上才真正意识到——GPUStack v2 的核心价值根本不在“部署”这个动作本身而在于它试图解决一个被长期忽视的底层矛盾GPU 资源的物理隔离性与大模型服务的逻辑弹性需求之间那道越来越深的鸿沟。简单说传统方式部署一个 Llama-3-70B 模型你得手动配好 CUDA 版本、PyTorch 编译选项、vLLM 的启动参数、监控端口、健康检查路径……每换一个模型几乎就是一次小型重装。而 GPUStack v2 的定位是让 GPU 变成像 CPU 或内存一样可被抽象、调度、复用的“计算单元”。它不直接运行模型而是运行一个轻量级的“资源代理”Agent这个代理负责监听来自中央控制台Control Plane的指令然后在本地 GPU 上拉起、管理、回收真正的推理后端比如 vLLM、Ollama、Triton。你看到的gpustack v2.1.2 添加自定义推理后端 vllm 0.22.这类操作本质是在给这个“中枢神经系统”注册一个新的“执行器官”。这解释了为什么网络热词里反复出现faster-whisper gpustack、ollama部署私有大模型、vllm部署大模型——它们不是并列关系而是上下级关系。GPUStack 是“调度层”vLLM/Ollama 是“执行层”。你查docker安装部署或zabbix安装部署得到的是通用运维知识但查gpustack官网你必须理解它的架构分层Control Plane控制平面、Agent代理、Backend后端。忽略这个分层直接套用docker run -d --gpus all的思维去部署 GPUStack90% 的失败都源于此。它也不是dify本地部署或comfyui scail v2那种面向最终用户的开箱即用工具。Dify 解决的是“怎么把大模型变成应用”GPUStack 解决的是“怎么让一百台不同型号的 GPU 服务器统一听从一个大脑指挥”。所以当你看到railway部署或hermes部署这些词时要明白Railway 是云托管平台Hermes 是消息队列它们和 GPUStack 是协作关系而非替代关系。GPUStack 的 Control Plane 可以部署在 Railway 上Agent 则必须部署在真实的、插着显卡的物理机或裸金属 VPS 上——这是它和纯 SaaS 方案最根本的分水岭。提示如果你的服务器连curl https://registry-1.docker.io/v2/都超时别急着翻墙或换镜像源。先确认一件事你的 Agent 主机是否真的具备 GPU 计算能力nvidia-smi命令能否正常返回显卡信息很多所谓“部署失败”其实是把 GPUStack 当成了普通 Web 应用部署在了没有 GPU 的云主机上。GPUStack 的 Agent 层对硬件有强依赖这是它和prometheus监控部署这类通用工具的本质区别。2. 部署前的“三问”为什么你的环境总卡在第一步绝大多数人拿到GPUStack v2 部署 操作文档后第一反应是复制粘贴命令。结果往往在docker-compose up -d这一步就报错错误信息五花八门context deadline exceeded、net/http: request canceled、get https://registry-1.docker.io/v2/: dial tcp: lookup registry-1.docker.io: no such host。这些看似是网络问题实则是三个更深层问题的表象。我把它总结为部署前必须自问的“三问”每问没答清楚后续都是徒劳。2.1 第一问你的 Control Plane 和 Agent是否在同一个“信任域”内GPUStack v2 的 Control Plane通常是一个 Web UI API 服务和分布在各处的 Agent运行在 GPU 服务器上的守护进程通过 gRPC 协议通信。这个通信默认是未加密的明文传输。这意味着如果你把 Control Plane 部署在公网云服务器比如阿里云 ECS而 Agent 部署在公司内网的一台测试机上中间隔着防火墙和 NAT那么 Agent 根本无法反向连接到 Control Plane 的 gRPC 端口默认 3001。此时你看到的context deadline exceeded其实是 Agent 在尝试连接时TCP 握手阶段就超时了跟 Docker Hub 完全无关。解决方案不是去折腾 Docker 镜像源而是明确你的网络拓扑场景A推荐新手Control Plane 和所有 Agent 都在同一局域网内。比如一台 Ubuntu 22.04 的物理服务器既跑 Control Plane也作为 Agent 运行在本机。此时只需确保ufw status显示防火墙关闭或开放3000Web UI、3001gRPC端口。场景B生产环境Control Plane 在云上Agent 在多个不同网络环境IDC、云、边缘中。这时必须启用 TLS 加密并配置双向认证mTLS。官方文档里--tls-cert-file和--tls-key-file参数不是可选项而是必选项。我见过太多团队因为跳过这一步在上线前一周才发现所有 Agent 都处于“离线”状态。2.2 第二问你的 Agent 主机是否满足“CUDA 兼容性矩阵”的硬性要求GPUStack v2 的 Agent 并不是一个纯 Go 语言编写的轻量二进制。它内部集成了一个精简版的 NVIDIA Container ToolkitNVIDIA-CTK用于 GPU 设备发现和管理。这就意味着Agent 对宿主机的 NVIDIA 驱动版本有严格要求。它不是“只要能跑nvidia-smi就行”而是需要驱动版本号精确匹配其内置的 CUDA Runtime 版本。举个真实案例某客户使用的是NVIDIA Driver Version: 525.85.12这是 2023 年发布的驱动。而 GPUStack v2.1.2 的 Agent 内置 CUDA Runtime 是 12.1。根据 NVIDIA 官方兼容性表525.x 驱动只支持 CUDA 11.x与 12.1 不兼容。结果就是 Agent 启动后日志里疯狂刷failed to initialize CUDA context但nvidia-smi一切正常。这个问题不会在docker-compose up阶段报错而是在 Agent 尝试注册自身时静默失败。如何验证不要只看nvidia-smi要执行# 查看驱动支持的最高 CUDA 版本 nvidia-smi --query-gpucompute_cap --formatcsv,noheader,nounits | head -1 | xargs -I {} nvidia-cuda-mps-control -d # 更直接的方法查看驱动文档或运行 cat /proc/driver/nvidia/version然后对照 NVIDIA 官方驱动与 CUDA 版本对应表 。如果驱动太旧唯一解法是升级驱动。别想着降级 GPUStack 版本因为旧版可能不支持你想要的 vLLM 0.22。2.3 第三问你的 Docker 环境是否已正确配置 NVIDIA Container Runtime这是最常被忽略却最致命的一环。docker run --gpus all能跑通不代表 GPUStack 的 Agent 就能用 GPU。因为 GPUStack 的 Agent 容器在启动时会尝试加载libcuda.so并调用cuInit()初始化 CUDA 上下文。如果 Docker 的默认 runtime 不是nvidia这个初始化就会失败。验证方法极其简单# 查看当前 Docker 默认 runtime docker info | grep -i runtime # 正常输出应包含Runtimes: runc nvidia # 如果只有 runc说明没装 NVIDIA Container Toolkit # 测试能否在容器内访问 GPU docker run --rm --gpus all nvidia/cuda:12.1.1-base-ubuntu22.04 nvidia-smi # 如果报错 no devices found说明 runtime 配置失败安装 NVIDIA Container Toolkit 的关键步骤远不止apt install nvidia-container-toolkit这一句。它需要修改/etc/docker/daemon.json添加{ runtimes: { nvidia: { path: /usr/bin/nvidia-container-runtime, runtimeArgs: [] } }, default-runtime: nvidia }然后必须执行sudo systemctl restart docker。很多教程漏掉了default-runtime这一行导致后续所有容器都得显式指定--runtimenvidia而 GPUStack 的docker-compose.yml文件里并没有写这一行于是 Agent 容器启动后nvidia-smi在容器内就不可见。注意error response from daemon: get https://registry-1.docker.io/v2/: net/http: request canceled while waiting for connection这个错误90% 的情况是上述三个问题之一导致 Agent 启动失败进而触发了 Docker 的健康检查超时。它不是网络问题而是服务启动失败的“果”不是“因”。盲目配置国内镜像源只会掩盖真正的硬件或配置缺陷。3. 从零开始的部署实录以 v2.1.2 为例避开所有已知坑现在我们进入真正的部署环节。以下步骤基于GPUStack v2.1.2版本操作系统为Ubuntu 22.04 LTSGPU 为NVIDIA A100 40GB驱动版本为535.104.05。所有命令均经过实测且标注了每个步骤背后的“为什么”。3.1 环境准备不是装软件而是构建一个“可信执行环境”这一步耗时最长但决定了后续 90% 的成功率。请务必逐条执行不要跳过任何验证。第一步升级系统并安装基础依赖# 更新包索引并升级内核重要新驱动需要新内核 sudo apt update sudo apt upgrade -y sudo reboot # 重启后安装必要工具链 sudo apt install -y curl wget gnupg2 software-properties-common ca-certificates lsb-release # 验证内核版本需 5.15 uname -r # 输出应为类似5.15.0-105-generic为什么Ubuntu 22.04 默认内核是 5.15但某些老版本的 ISO 可能预装了 5.13。NVIDIA 535 驱动要求最低内核版本为 5.15否则modprobe nvidia会失败。第二步安装并验证 NVIDIA 驱动# 添加官方驱动仓库 curl -fsSL https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/3bf863cc.pub | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-drivers.gpg echo deb [archamd64 signed-by/usr/share/keyrings/nvidia-drivers.gpg] https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/ / | sudo tee /etc/apt/sources.list.d/nvidia-drivers.list sudo apt update # 安装驱动注意不要用 ubuntu-drivers autoinstall它可能装错版本 sudo apt install -y nvidia-driver-535-server # 重启并验证 sudo reboot # 重启后执行 nvidia-smi # 输出应显示 A100 和驱动版本 535.104.05为什么选nvidia-driver-535-serverserver版本比desktop版本更稳定专为长时间运行的推理服务设计且对 CUDA 12.1 支持最完善。第三步安装并配置 NVIDIA Container Toolkit# 添加仓库 curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg curl -fsSL https://nvidia.github.io/libnvidia-container/ubuntu22.04/libnvidia-container.list | sed s/notdefault//g | sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list sudo apt update sudo apt install -y nvidia-container-toolkit # 关键配置修改 daemon.json sudo tee /etc/docker/daemon.json EOF { runtimes: { nvidia: { path: /usr/bin/nvidia-container-runtime, runtimeArgs: [] } }, default-runtime: nvidia, live-restore: true } EOF sudo systemctl restart docker # 验证 docker run --rm --gpus all nvidia/cuda:12.1.1-base-ubuntu22.04 nvidia-smi | head -10为什么live-restore必须开启GPUStack 的 Agent 是一个长期运行的守护进程。如果 Docker Daemon 重启live-restore能保证 Agent 容器不被杀死这对生产环境的稳定性至关重要。3.2 下载并启动 Control PlaneGPUStack 的 Control Plane 是一个单体 Web 应用官方提供了预编译的二进制和 Docker 镜像两种方式。强烈推荐使用 Docker 方式因为它能自动处理数据库SQLite和静态文件的挂载。# 创建工作目录 mkdir -p ~/gpustack/{control,agents} cd ~/gpustack/control # 下载官方 docker-compose.yml注意不是 GitHub 上的源码是 release 包里的 curl -L https://github.com/planetdevel/gpustack/releases/download/v2.1.2/docker-compose.yaml -o docker-compose.yaml # 修改配置启用 HTTPS生产必需 # 编辑 docker-compose.yaml找到 control-plane 服务在 environment 下添加 # - GPUSTACK_TLS_CERT_FILE/app/tls/cert.pem # - GPUSTACK_TLS_KEY_FILE/app/tls/key.pem # 然后在 volumes 下添加 # - ./tls:/app/tls:ro # 生成自签名证书仅用于测试生产请用 Lets Encrypt mkdir -p tls openssl req -x509 -newkey rsa:4096 -keyout tls/key.pem -out tls/cert.pem -days 365 -nodes -subj /CNlocalhost # 启动 docker-compose up -d # 验证 curl -k https://localhost:3000/api/health # 应返回 {status:ok}为什么必须改docker-compose.yaml官方提供的 YAML 文件默认是 HTTP 模式。如果你不改Control Plane 会监听http://0.0.0.0:3000而 Agent 默认尝试用https://连接导致连接拒绝。这个细节在官方文档里藏得很深是新手最大的坑。3.3 部署 Agent 并注册到 Control Plane这才是 GPUStack 的灵魂所在。Agent 不是“部署”而是“注册”。它需要一个唯一的 Token 来向 Control Plane 证明身份。# 进入 agents 目录 cd ~/gpustack/agents # 从 Control Plane 获取注册 Token这一步必须在 Control Plane 启动后做 # 打开浏览器访问 https://localhost:3000首次登录用户名密码均为 admin # 登录后点击左上角 Settings - Agents - Add Agent # 复制生成的 Token形如gpstk_abc123def456... # 创建 agent 的配置文件 cat agent-config.yaml EOF apiVersion: v1 kind: AgentConfig metadata: name: a100-node-01 spec: controlPlaneEndpoint: https://192.168.1.100:3001 # 替换为你的 Control Plane IP token: gpstk_abc123def456... # 替换为你复制的 Token # 指定 GPU 设备可选多卡时指定 gpuDevices: - uuid: GPU-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx # 用 nvidia-smi -L 获取 EOF # 启动 Agent注意这里用的是官方镜像不是自己 build docker run -d \ --name gpustack-agent \ --restartalways \ --networkhost \ --privileged \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /dev:/dev \ -v /usr/bin/nvidia-smi:/usr/bin/nvidia-smi \ -v $(pwd)/agent-config.yaml:/app/config.yaml:ro \ -e GPUSTACK_CONFIG_FILE/app/config.yaml \ ghcr.io/planetdevel/gpustack:2.1.2-agent为什么用--networkhostAgent 需要直接访问宿主机的docker.sock和/dev/nvidia*设备。--networkhost是最简单、最可靠的方式避免了桥接网络带来的端口映射和 DNS 解析问题。--privileged是必须的因为 Agent 需要加载 NVIDIA 内核模块。3.4 添加自定义推理后端vLLM 0.22 的完整集成gpustack v2.1.2 添加自定义推理后端 vllm 0.22.这个热搜词代表了 GPUStack 最核心的价值。它不是让你手动pip install vllm而是通过声明式配置让 GPUStack 自动为你拉起、管理一个 vLLM 实例。# 在 Control Plane 的 Web UI 中操作 # 1. 左侧导航栏点击 Models # 2. 点击右上角 Add Model # 3. Model Name: llama-3-8b-instruct # 4. Backend Type: vLLM # 5. 在 Custom Configuration 区域粘贴以下 JSON { model: meta-llama/Meta-Llama-3-8B-Instruct, tensor_parallel_size: 1, gpu_memory_utilization: 0.9, max_model_len: 4096, enforce_eager: false, trust_remote_code: true } # 6. 点击 Save为什么enforce_eagerfalse这是 vLLM 0.22 的关键优化。设为false会启用 PagedAttention大幅降低显存占用。但如果模型有自定义 CUDA 内核可能需要设为true。这是一个需要根据具体模型测试的参数。保存后GPUStack 会自动执行以下操作在 Agent 所在的 GPU 服务器上拉起一个vllm/vllm-openai:0.22.0镜像的容器。将上述 JSON 配置作为环境变量传入。为该容器分配一个唯一的端口如8001并在 Control Plane 的 API 中注册为http://a100-node-01:8001/v1/chat/completions。启动健康检查持续监控 vLLM 进程。你可以用curl直接测试curl -X POST https://localhost:3000/v1/chat/completions \ -H Content-Type: application/json \ -H Authorization: Bearer your-api-key \ -d { model: llama-3-8b-instruct, messages: [{role: user, content: Hello}] }注意这里的https://localhost:3000是 Control Plane 的地址不是 vLLM 的地址。GPUStack 已经帮你做了反向代理和负载均衡。经验分享我曾在一个项目中客户要求同时部署llama-3-70b和qwen2-72b两个超大模型。如果手动管理需要为每个模型单独配置 vLLM 的--port、--host、--tensor-parallel-size极易出错。而用 GPUStack只需在 Web UI 中点两次“Add Model”填两份 JSON剩下的全部自动化。当qwen2-72b因为显存不足启动失败时GPUStack 的日志会清晰地告诉你“Failed to start backend: OOM when allocating tensor”而不是笼统的container exited with code 1。这种细粒度的可观测性是它区别于脚本化部署的核心优势。4. 故障排查全景图从日志、网络到硬件的逐层穿透部署完成不等于万事大吉。GPUStack v2 的复杂性在于它横跨了网络、容器、GPU 驱动、CUDA Runtime 四个层面。任何一个环节出问题都会表现为上层服务的“不可用”。下面这张排查图是我过去一年在十几个客户现场总结出的、最高效的故障定位路径。4.1 第一层确认 Control Plane 是否“活着”这是最基础也最容易被忽略的一步。很多问题其实出在 Control Plane 本身。# 检查 Control Plane 容器状态 docker ps | grep control # 查看实时日志重点关注 ERROR 和 WARN docker logs -f gpustack-control-plane # 关键检查点 # - 日志中是否有 Starting server on :3000没有则说明 Web 服务没起来。 # - 是否有 failed to open database说明 SQLite 文件权限不对通常是挂载卷的 owner 是 root而容器内用户是 non-root。 # - 是否有 failed to listen on :3001说明 gRPC 端口被占用用 sudo lsof -i :3001 查看。常见陷阱docker-compose up -d后docker ps看不到容器。这不是部署失败而是docker-compose.yaml里control-plane服务的restart策略是unless-stopped但它启动失败后Docker 会立即退出容器导致ps看不到。此时必须用docker-compose logs control-plane查看启动时的日志。4.2 第二层验证 Agent 是否“在线”Agent 是整个系统的“手脚”它离线再好的 Control Plane 也是空中楼阁。# 检查 Agent 容器 docker ps | grep agent # 查看 Agent 日志这是最重要的日志 docker logs -f gpustack-agent # 关键检查点 # - 日志开头是否有 Starting agent...没有则容器根本没启动。 # - 是否有 Connecting to control plane at https://...这是 Agent 在尝试连接。 # - 是否有 Connected to control plane有则表示网络和 TLS 无误。 # - 是否有 Registering agent with name...这是注册流程的开始。 # - 是否有 Registered agent successfully这是成功的标志。 # - 如果卡在 Connecting...立刻检查 controlPlaneEndpoint 地址是否可 ping 通端口 3001 是否开放。一个经典案例某客户将controlPlaneEndpoint配置为https://gpustack.example.com:3001但 DNS 解析失败。Agent 日志里只显示Connecting...没有任何错误。解决方案是在 Agent 容器内执行nslookup gpustack.example.com发现超时。于是他们改用 IP 地址并在docker run命令中添加--add-hostgpustack.example.com:192.168.1.100。4.3 第三层诊断后端vLLM/Ollama是否“就绪”即使 Control Plane 和 Agent 都在线模型也可能无法推理。这时问题出在后端容器。# 查看所有由 GPUStack 启动的后端容器 docker ps --filter labelio.gpustack.backend --format table {{.ID}}\t{{.Names}}\t{{.Status}} # 查看某个后端容器的日志例如 vLLM docker logs -f container-id # 关键检查点 # - vLLM 日志中是否有 Starting OpenAI API server没有则启动失败。 # - 是否有 Out of memory 或 CUDA out of memory这是显存不足。 # - 是否有 Model not found说明 Hugging Face 模型 ID 错误或网络不通。 # - 是否有 Failed to load model可能是 trust_remote_codetrue 没加或模型需要自定义代码。避坑技巧vLLM 启动慢是常态尤其是加载 70B 模型可能需要 5-10 分钟。不要一看到docker ps里容器状态是Up 2 minutes就认为失败。应该用docker logs -f持续观察直到看到Starting OpenAI API server。如果超过 15 分钟还没出现再检查显存和模型路径。4.4 第四层终极武器——硬件与驱动的交叉验证当所有软件层都看似正常但模型就是不响应时问题一定在最底层。# 在 Agent 主机上执行以下命令形成一份“硬件健康报告” echo 1. NVIDIA 驱动 nvidia-smi -q | grep Driver Version\|CUDA Version echo 2. CUDA Runtime /usr/local/cuda/bin/nvcc --version 2/dev/null || echo nvcc not found echo 3. GPU 设备可见性 nvidia-smi -L echo 4. Docker GPU 可见性 docker run --rm --gpus all nvidia/cuda:12.1.1-base-ubuntu22.04 nvidia-smi -L echo 5. Agent 容器内 GPU docker exec gpustack-agent nvidia-smi -L echo 6. vLLM 容器内 GPU docker exec $(docker ps --filter labelio.gpustack.backend --format {{.ID}}) nvidia-smi -L这份报告的每一行都应该输出相同的结果。如果第 4 行能列出 GPU但第 5 行或第 6 行报错NVIDIA-SMI has failed because it couldnt communicate with the NVIDIA driver那就 100% 是--runtimenvidia没生效或者nvidia-container-runtime的路径配置错了。提示get https://registry-1.docker.io/v2/: context deadline exceeded (client.timeout)这个错误如果出现在docker-compose up阶段99% 是因为你的服务器 DNS 配置有问题导致docker-compose在拉取镜像时无法解析registry-1.docker.io。解决方案不是换镜像源而是检查/etc/resolv.conf确保里面有可靠的 DNS 服务器比如nameserver 8.8.8.8。这是一个纯粹的网络基础问题和 GPUStack 本身毫无关系。5. 进阶实践从单机部署到混合云集群的平滑演进完成了单机部署你已经掌握了 GPUStack v2 的核心脉络。但它的真正威力在于支撑一个跨越公有云、私有云和边缘设备的异构 GPU 集群。这并非遥不可及而是有一条清晰、可验证的演进路径。5.1 路径一从单机到多机——Agent 的规模化注册单机部署时Control Plane 和 Agent 在同一台机器上。扩展到多机核心变化只有一个Agent 的注册方式。单机模式Agent 配置中的controlPlaneEndpoint是https://127.0.0.1:3001。多机模式controlPlaneEndpoint必须是 Control Plane 所在服务器的公网 IP 或域名并且该服务器的3001端口必须对外网开放。但这带来了一个安全悖论开放3001端口意味着任何能访问该端口的人都可以注册恶意 Agent。GPUStack 的解决方案是Token 认证 TLS 加密。# 在 Control Plane 服务器上生成一个长期有效的 Agent Token # 这不是 Web UI 里那个一次性的 Token而是通过 API 生成的 curl -X POST https://localhost:3000/api/v1/agents/tokens \ -H Authorization: Bearer admin-api-key \ -H Content-Type: application/json \ -d {name: prod-cluster-token, expires_in_days: 365} # 返回的 JSON 中会包含一个长 Token这个 Token 可以用于注册 100 个 Agent然后为每一台新的 GPU 服务器重复 3.3 节的docker run命令只是将agent-config.yaml中的token字段替换为这个新生成的 Token。Control Plane 会自动识别并管理所有注册上来的 Agent你无需在 Web UI 中做任何额外操作。5.2 路径二从裸金属到 Kubernetes——Agent 的容器化编排很多企业已有成熟的 Kubernetes 集群不想为 GPUStack 单独维护一批裸金属服务器。GPUStack 官方支持将 Agent 以 DaemonSet 的形式部署在 K8s 中。# agent-daemonset.yaml apiVersion: apps/v1 kind: DaemonSet metadata: name: gpustack-agent namespace: gpustack spec: selector: matchLabels: name: gpustack-agent template: metadata: labels: name: gpustack-agent spec: # 关键必须使用 hostNetwork否则无法访问宿主机的 docker.sock hostNetwork: true # 关键必须容忍所有污点确保能调度到 GPU 节点 tolerations: - operator: Exists containers: - name: agent image: ghcr.io/planetdevel/gpustack:2.1.2-agent env: - name: GPUSTACK_CONFIG_FILE value: /app/config.yaml volumeMounts: - name: config mountPath: /app/config.yaml subPath: config.yaml readOnly: true - name: docker-socket mountPath: /var/run/docker.sock - name: dev mountPath: /dev - name: nvidia-smi mountPath: /usr/bin/nvidia-smi volumes: - name: config configMap: name: gpustack-agent-config - name: docker-socket hostPath: path: /var/run/docker.sock - name: dev hostPath: path: /dev - name: nvidia-smi hostPath: path: /usr/bin/nvidia-smi这个 DaemonSet 的难点在于configMap的创建。你需要将agent-config.yaml的内容编码为 Base64放入 ConfigMap。这看起来麻烦但好处是所有 Agent 的配置都由 K8s 统一管理可以轻松实现灰度发布和配置回滚。5.3 路径三从 vLLM 到全生态——后端的无限扩展GPUStack 的设计哲学是“后端无关”。目前官方支持 vLLM、Ollama、Triton但它的插件机制允许你接入任何符合 OpenAI API 规范的推理服务。假设你想接入一个自研的、基于 PyTorch 的推理引擎my-inference-engine它监听http://localhost:8080/v1/chat/completions。你只需要在 Control Plane 的 Web UI 中“Add Model” - “Custom Backend”在 “API Base URL” 中填写http://localhost:8080在 “API Key” 中填写你的引擎所需的密钥如果需要在 “Model Name” 中填写my-custom-modelGPUStack 会将所有发往https://control-plane/v1/chat/completions?modelmy-custom-model的请求透明地转发给http://localhost:8080/v1/chat/completions。你完全不需要修改my-inference-engine的任何一行代码。这就是 GPUStack v2 的终极价值它不绑定任何技术栈它只是一个智能的、GPU 感知的 API 网关和资源协调器。你今天用 vLLM明天换成 Triton后天换成一个全新的框架对上层应用来说API 调用方式完全不变。最后分享一个小技巧在生产环境中我习惯在 Control Plane 的docker-compose.yaml中为control-plane服务添加一个healthcheckhealthcheck: test: [CMD, curl, -f, https://localhost:3000/api/health] interval: 30s timeout: 10s retries: 3这样docker ps的状态列就能直观地告诉你 Control Plane 是否健康。结合docker-compose up -d --force-recreate可以实现一键式的滚动更新这是任何手工脚本都无法比拟的工程化优势。