<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/"><channel><title>Jimmy Song – 上手篇：从零到第一块可调度的 GPU</title><link>https://jimmysong.io/zh/book/ai-infra/getting-started/</link><description>Recent content in 上手篇：从零到第一块可调度的 GPU on Jimmy Song</description><generator>Hugo -- gohugo.io</generator><language>zh</language><managingEditor>Jimmy Song</managingEditor><webMaster>Jimmy Song</webMaster><follow_challenge><feedId>51621818828612637</feedId><userId>59800919738273792</userId></follow_challenge><lastBuildDate>Mon, 14 Sep 2026 00:00:00 +0800</lastBuildDate><atom:link href="https://jimmysong.io/zh/book/ai-infra/getting-started/index.xml" rel="self" type="application/rss+xml"/><item><title>准备 GPU 运行环境：Linux、驱动与 CUDA 工具链</title><link>https://jimmysong.io/zh/book/ai-infra/getting-started/gpu-environment/</link><pubDate>Mon, 14 Sep 2026 00:00:00 +0000</pubDate><author>Jimmy Song</author><guid>https://jimmysong.io/zh/book/ai-infra/getting-started/gpu-environment/</guid><description>理解 GPU 软件栈的分层与版本规则，学会用最少的命令验证一台 GPU 机器的健康状态，并掌握“容器时代宿主机只需要驱动”这一关键事实，为 GPU 容器与 Kubernetes 接入扫清环境障碍。</description><content:encoded>
&lt;blockquote&gt;
&lt;p&gt;GPU 环境里一半的故障不是硬件坏了，而是驱动、工具链、镜像各说各话。先让机器诚实地报告它的状态，再谈优化与调度。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;本章不追求覆盖所有安装方式，只做三件事：建立 GPU 软件栈的分层心智模型、给出验证环境的最小命令集、澄清版本兼容规则。读完本章，你应当能在任何一台 GPU 机器上用五分钟判断“环境是否正常、问题出在哪一层”。&lt;/p&gt;
&lt;h2 id="gpu-软件栈的四个层次"&gt;GPU 软件栈的四个层次&lt;/h2&gt;
&lt;p&gt;从硬件到应用，GPU 软件栈可以拆成四层。这个分层是全书的基础 vocabulary：后面讨论容器注入（&lt;a href="../../software-stack/nvidia-container-toolkit/"&gt;Container Toolkit&lt;/a&gt;）、设备插件、调度时，每一步都在这几层之间搬运“访问权”。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;层次&lt;/th&gt;
&lt;th&gt;内容&lt;/th&gt;
&lt;th&gt;谁安装它&lt;/th&gt;
&lt;th&gt;出问题的典型表现&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;内核驱动&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nvidia.ko&lt;/code&gt; 内核模块，唯一能直接控制硬件的软件&lt;/td&gt;
&lt;td&gt;宿主机管理员（OS 包管理器或官方仓库）&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nvidia-smi&lt;/code&gt; 报错、设备不可见&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;用户态运行时&lt;/td&gt;
&lt;td&gt;CUDA Driver API、libcuda、&lt;code&gt;nvidia-smi&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;随驱动一起安装&lt;/td&gt;
&lt;td&gt;驱动与库版本不匹配&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;工具链与加速库&lt;/td&gt;
&lt;td&gt;nvcc 编译器、cuBLAS/cuDNN/NCCL&lt;/td&gt;
&lt;td&gt;通常在容器镜像里，而非宿主机&lt;/td&gt;
&lt;td&gt;编译失败、库找不到&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;框架层&lt;/td&gt;
&lt;td&gt;PyTorch、vLLM 等&lt;/td&gt;
&lt;td&gt;容器镜像 / 虚拟环境&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CUDA driver version is insufficient&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;figcaption class="text-center mb-3"&gt;
表 1: GPU 软件栈的四个层次
&lt;/figcaption&gt;
&lt;p&gt;一个容易混淆的点：日常说“装 CUDA”可能指三层不同的东西——驱动里的 CUDA 支持能力、&lt;code&gt;nvcc&lt;/code&gt; 为代表的工具链、cuDNN 这类加速库。云上 GPU 虚拟机通常三者都已配好；而自己管理的机器上，&lt;strong&gt;宿主机只需要第一层（驱动）&lt;/strong&gt;，这一点本章末尾会展开。&lt;/p&gt;
&lt;h2 id="三条版本规则先于一切命令"&gt;三条版本规则，先于一切命令&lt;/h2&gt;
&lt;p&gt;在敲任何命令之前，先记住三条规则。它们能解释你将来遇到的大多数版本报错：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;驱动版本决定 CUDA 上限。&lt;/strong&gt;&lt;code&gt;nvidia-smi&lt;/code&gt; 右上角显示的 &lt;code&gt;CUDA Version&lt;/code&gt; 不是“已安装的 CUDA 版本”，而是“该驱动最高支持的 CUDA 版本”。应用实际使用的 CUDA 版本可以更低。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;工具链版本 ≠ 驱动版本。&lt;/strong&gt;&lt;code&gt;nvcc --version&lt;/code&gt; 与 &lt;code&gt;nvidia-smi&lt;/code&gt; 显示的版本可以不一致，这是正常现象：nvcc 属于工具链层，nvidia-smi 属于驱动层，两者各自演进。&lt;/li&gt;
&lt;li&gt;**容器镜像自带工具链，宿主机驱动是唯一硬约束。**PyTorch 官方镜像里带着完整的 CUDA 运行时与加速库，但容器内进程最终仍要通过宿主机的内核驱动访问 GPU。因此“镜像 CUDA 版本 ≤ 驱动支持的最高版本”即可运行。&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="alert alert-note-container"&gt;
&lt;div class="alert-note-title px-2"&gt;
工程含义
&lt;/div&gt;
&lt;div class="alert-note px-2"&gt;
规则 3 是“GPU 容器化”的价值来源：版本矩阵中变化最快的部分（工具链、加速库、框架）被装进镜像随应用走，变化最慢、权限最高的部分（内核驱动）留在宿主机由平台统一管理。Kubernetes 的一切 GPU 方案都建立在这个分工上，详见&lt;a href="../../software-stack/container-gpu-problem/"&gt;容器与 GPU 的根本矛盾&lt;/a&gt;。
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="验证环境的最小命令集"&gt;验证环境的最小命令集&lt;/h2&gt;
&lt;p&gt;拿到一台 GPU 机器，先用这组命令建立基线。它们分别回答四个问题：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 1. 硬件在不在：确认 PCI 总线上能看到 NVIDIA 设备&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;lspci &lt;span class="p"&gt;|&lt;/span&gt; grep -i nvidia
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 2. 驱动好不好：查看驱动版本、支持的 CUDA 上限、显存占用&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nvidia-smi
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 3. 工具链装没装（可选，容器化路径下宿主机可以没有）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nvcc --version
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 4. 框架能不能用：在容器里跑一次真实断言（在“用 Docker 运行 GPU 工作负载”一章展开）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker run --rm --gpus all pytorch/pytorch:2.5.1-cuda12.4-cudnn9-runtime &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; python -c &lt;span class="s2"&gt;&amp;#34;import torch; print(torch.cuda.is_available())&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;nvidia-smi&lt;/code&gt; 的输出值得逐字段读一遍，它是后续所有排障的起点：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;+-----------------------------------------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;| NVIDIA-SMI 580.xx.xx Driver Version: 580.xx.xx CUDA Version: 13.0 |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;|-------------------------------+----------------------+----------------------|
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;| GPU Name Persistence-M| Bus-Id Disp.A | Memory-Usage |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;| 0 NVIDIA A10 On | 00000000:00:04.0 Off | 512MiB / 23028MiB |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;|-------------------------------+----------------------+----------------------|
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;| Processes: GPU Memory |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;| GPU PID Type Process name Usage |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;| 0 1234 C python 440MiB |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;+-----------------------------------------------------------------------------+&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;三个字段最重要：&lt;code&gt;Driver Version&lt;/code&gt;（与库版本是否匹配）、&lt;code&gt;CUDA Version&lt;/code&gt;（驱动支持的 CUDA 上限）、&lt;code&gt;Memory-Usage&lt;/code&gt;（显存占用——注意“进程列表里的占用之和 ≠ 总占用”是常态，因为还有每进程底噪与预留，详见&lt;a href="../../fundamentals/gpu-memory-management/"&gt;显存管理&lt;/a&gt;）。&lt;/p&gt;
&lt;h2 id="安装驱动推荐路径与示例"&gt;安装驱动：推荐路径与示例&lt;/h2&gt;
&lt;p&gt;如果拿到的是一台没装驱动的机器（自建机房、部分云镜像），Ubuntu 上最省心的方式是发行版驱动或 NVIDIA 官方仓库：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 方式一：发行版自动推荐（适合快速起步）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo ubuntu-drivers install
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 方式二：NVIDIA 官方 CUDA 仓库，版本可钉住（适合生产，以 580 系列为例）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 命令与版本随时间演进，以 NVIDIA 官方文档为准&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install nvidia-driver-580
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo reboot&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;重启后用 &lt;code&gt;nvidia-smi&lt;/code&gt; 确认驱动加载成功。数据中心场景建议用方式二并钉住大版本：驱动升级需要卸载正在使用的内核模块，属于变更窗口内的事，不应混在应用发布里顺带发生。&lt;/p&gt;
&lt;h2 id="常见故障速查"&gt;常见故障速查&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;现象&lt;/th&gt;
&lt;th&gt;常见原因&lt;/th&gt;
&lt;th&gt;第一反应&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nvidia-smi: command not found&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;未装驱动或未在 PATH&lt;/td&gt;
&lt;td&gt;回到上一节安装驱动&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;No devices were found&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;驱动与卡不匹配、PCI 直通未配置好（虚机场景）&lt;/td&gt;
&lt;td&gt;&lt;code&gt;lspci&lt;/code&gt; 确认设备可见；核对驱动支持的型号&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Driver/library version mismatch&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;驱动升级后旧模块仍在内存中&lt;/td&gt;
&lt;td&gt;重启，或停止 GPU 进程后重载模块&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nvidia-smi&lt;/code&gt; 正常但应用报 &lt;code&gt;insufficient&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;应用要求的 CUDA 版本高于驱动支持上限&lt;/td&gt;
&lt;td&gt;升级驱动，或换低版本 CUDA 的镜像&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;显存占用高但进程列表为空&lt;/td&gt;
&lt;td&gt;僵尸进程、MIG 配置残留&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo fuser -v /dev/nvidia*&lt;/code&gt; 找持有者&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;figcaption class="text-center mb-3"&gt;
表 2: 驱动层常见故障与第一反应
&lt;/figcaption&gt;
&lt;p&gt;虚机场景（云上 GPU 实例）还有一个高频坑：实例类型带 GPU 不等于镜像里有驱动。下一章会给出各云自带驱动的镜像选择建议。&lt;/p&gt;
&lt;h2 id="动手实验五分钟体检一台-gpu-机器"&gt;动手实验：五分钟体检一台 GPU 机器&lt;/h2&gt;
&lt;h3 id="实验目标"&gt;实验目标&lt;/h3&gt;
&lt;p&gt;在一台 GPU Linux 机器（物理机或云上实例）上完成环境体检，产出一张“环境基线卡”：硬件型号、驱动版本、CUDA 上限、当前占用。这张卡是后续所有实验的对照基线。&lt;/p&gt;
&lt;h3 id="前置条件与成本预估"&gt;前置条件与成本预估&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;一台有 NVIDIA GPU 的 Linux 机器（Ubuntu 22.04/24.04 为例）。本地机器成本为零；云上实例按小时计费，开机与验证在十分钟内完成，成本通常不足一美元（下一章会专门练习开机与关机）。&lt;/li&gt;
&lt;li&gt;具备 sudo 权限。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="操作步骤"&gt;操作步骤&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 步骤 1：确认 PCI 总线可见 GPU，记下型号&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;lspci &lt;span class="p"&gt;|&lt;/span&gt; grep -i nvidia
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 预期输出示例：0000:00:04.0 3D controller: NVIDIA Corporation GA102GL [A10]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 步骤 2：读取驱动基线&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nvidia-smi --query-gpu&lt;span class="o"&gt;=&lt;/span&gt;name,driver_version,memory.total,memory.used --format&lt;span class="o"&gt;=&lt;/span&gt;csv
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 预期输出：一行 CSV，包含型号、驱动版本、显存总量与已用量&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 步骤 3：核对 CUDA 上限，记下该数字（后续选容器镜像时要用）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nvidia-smi &lt;span class="p"&gt;|&lt;/span&gt; grep -o &lt;span class="s2"&gt;&amp;#34;CUDA Version: [0-9.]*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 步骤 4：确认没有异常的显存残留（used 应接近 0，除非你已知有任务在跑）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nvidia-smi --query-gpu&lt;span class="o"&gt;=&lt;/span&gt;memory.used --format&lt;span class="o"&gt;=&lt;/span&gt;csv&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;如果 &lt;code&gt;nvidia-smi&lt;/code&gt; 报错，先按上一节的故障速查表处理；驱动未装的机器先完成安装并重启。&lt;/p&gt;
&lt;h3 id="验证清单"&gt;验证清单&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; &lt;code&gt;lspci&lt;/code&gt; 能看到 NVIDIA 设备，型号与预期一致&lt;/li&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; &lt;code&gt;nvidia-smi&lt;/code&gt; 正常输出，记下 &lt;code&gt;driver_version&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; 记下驱动支持的 CUDA 上限，确认 ≥ 你计划使用的镜像 CUDA 版本&lt;/li&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; 空闲机器的 &lt;code&gt;memory.used&lt;/code&gt; 低于 500MiB&lt;/li&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; 把以上四个数字记入你的环境基线卡（一台机器一行）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="原理速览"&gt;原理速览&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;nvidia-smi&lt;/code&gt; 读取的是驱动通过 procfs/devfs 暴露的状态，它只依赖内核驱动与用户态管理库（NVML），不需要 CUDA 工具链。这就是为什么“nvidia-smi 正常但应用崩”几乎总是工具链/镜像层的问题，而不是驱动层的问题。NVML 与 DCGM exporter（Kubernetes 监控用）的关系见&lt;a href="../../observability/"&gt;可观测与验收&lt;/a&gt;。&lt;/p&gt;
&lt;h3 id="清理与止损"&gt;清理与止损&lt;/h3&gt;
&lt;p&gt;本实验只读不写，无资源需要清理。云上实例如果不继续用，记得按下一章的流程关机或销毁。&lt;/p&gt;
&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;本章建立了全书的环境基线：GPU 软件栈四层模型、三条版本规则、最小验证命令集。最重要的工程结论是——&lt;strong&gt;容器时代宿主机只需要维护好驱动这一层&lt;/strong&gt;，其余交给镜像。下一章&lt;a href="../first-gpu-machine/"&gt;云上第一台 GPU 机器&lt;/a&gt;解决“去哪里拿卡”：按用途选型、看懂计费、设置止损线，并完成从开机到彻底清理的第一个完整闭环。驱动与 CUDA 执行模型的深入原理，见&lt;a href="../../fundamentals/cuda-execution-model/"&gt;CUDA 执行模型&lt;/a&gt;与&lt;a href="../../software-stack/nvidia-software-stack/"&gt;NVIDIA 软件栈分层&lt;/a&gt;。&lt;/p&gt;</content:encoded></item><item><title>云上第一台 GPU 机器：选型、成本与止损线</title><link>https://jimmysong.io/zh/book/ai-infra/getting-started/first-gpu-machine/</link><pubDate>Mon, 14 Sep 2026 00:00:00 +0000</pubDate><author>Jimmy Song</author><guid>https://jimmysong.io/zh/book/ai-infra/getting-started/first-gpu-machine/</guid><description>按用途选对 GPU 机型、看懂三大云的实例形态与计费模型、设置预算告警与自动止损，并通过一台竞价实例走完从开机、验证到彻底清理的完整闭环。</description><content:encoded>
&lt;blockquote&gt;
&lt;p&gt;在云上，最贵的错误通常不是选错机型，而是忘了关机。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;上一章你学会了验证一台 GPU 机器，但那台机器从哪来？对大多数工程师，答案是云：小时级计费、免硬件采购、随开随关。本章解决三个问题——按用途选卡、看懂计费、设置止损线，最后用一个实验走完“开机到彻底清理”的完整闭环。这个闭环本身就是生产 GPU 平台运维的最小原型。&lt;/p&gt;
&lt;h2 id="先问用途再选卡"&gt;先问用途，再选卡&lt;/h2&gt;
&lt;p&gt;显存是硬约束（见&lt;a href="../../fundamentals/gpu-memory-management/"&gt;显存管理&lt;/a&gt;），所以选卡的第一步是估算工作负载的显存需求，而不是比较 TFLOPS。以大模型场景为例：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;用途&lt;/th&gt;
&lt;th&gt;显存底线（估算）&lt;/th&gt;
&lt;th&gt;典型卡&lt;/th&gt;
&lt;th&gt;计费直觉（按需，美东，仅供量级参考）&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;学习、跑通流程&lt;/td&gt;
&lt;td&gt;8–16 GiB&lt;/td&gt;
&lt;td&gt;T4 / L4&lt;/td&gt;
&lt;td&gt;$0.5–1 /小时&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7B 模型推理（bf16 权重约 14 GiB + KV Cache）&lt;/td&gt;
&lt;td&gt;16–24 GiB&lt;/td&gt;
&lt;td&gt;A10 / L4 24G&lt;/td&gt;
&lt;td&gt;$1 /小时左右&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7B 模型 LoRA 微调&lt;/td&gt;
&lt;td&gt;24–48 GiB&lt;/td&gt;
&lt;td&gt;A10 24G 起步，A100 40G 从容&lt;/td&gt;
&lt;td&gt;$1–4 /小时&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7B 全量微调（权重+梯度+优化器状态）&lt;/td&gt;
&lt;td&gt;80 GiB 或多卡&lt;/td&gt;
&lt;td&gt;A100 80G / 多卡&lt;/td&gt;
&lt;td&gt;$8 /小时以上&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;大规模预训练&lt;/td&gt;
&lt;td&gt;数百 GiB，必须多机多卡&lt;/td&gt;
&lt;td&gt;H100/H800 集群&lt;/td&gt;
&lt;td&gt;$30 /卡/小时量级&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;figcaption class="text-center mb-3"&gt;
表 1: 常见用途与机型选择
&lt;/figcaption&gt;
&lt;p&gt;两条经验：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;推理看显存与带宽，训练看显存与互连&lt;/strong&gt;。推理的性能物理层见&lt;a href="../../workloads/llm-inference-physics/"&gt;LLM 推理物理层&lt;/a&gt;；多卡训练为什么必须关心 NVLink 与拓扑，见&lt;a href="../../fundamentals/gpu-interconnect-collectives/"&gt;互连与集合通信&lt;/a&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;能用小卡验证的，不要用大卡陪跑&lt;/strong&gt;。工程流程（容器、调度、监控）的验证用 T4/A10 级别足够，验证完再上生产卡，这是成本治理的第一原则，系统的展开见&lt;a href="../../production/capacity-economics/"&gt;容量经济&lt;/a&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;另外注意消费级卡（GeForce）与数据中心卡的区别：ECC 显存、MIG 切分、NVLink、官方数据中心驱动支持，这些差异决定了生产负载不应跑在消费卡上，详见&lt;a href="../../fundamentals/gpu-generations-products/"&gt;世代、模块与产品语言&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id="三大云的-gpu-实例形态"&gt;三大云的 GPU 实例形态&lt;/h2&gt;
&lt;p&gt;各家命名不同，但映射关系清晰。选择时优先考虑两点：&lt;strong&gt;是否自带 GPU 驱动镜像&lt;/strong&gt;（省掉上一章的安装）、&lt;strong&gt;竞价实例折扣幅度&lt;/strong&gt;。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;云&lt;/th&gt;
&lt;th&gt;入门（T4/L4 级）&lt;/th&gt;
&lt;th&gt;中档（A10/A100 级）&lt;/th&gt;
&lt;th&gt;高端（H100 级）&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AWS&lt;/td&gt;
&lt;td&gt;g4dn（T4）、g6（L4）&lt;/td&gt;
&lt;td&gt;g5（A10G）、p4d（8×A100 整机）&lt;/td&gt;
&lt;td&gt;p5（8×H100 整机）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Google Cloud&lt;/td&gt;
&lt;td&gt;g2（L4）&lt;/td&gt;
&lt;td&gt;a2（A100 单卡可开）&lt;/td&gt;
&lt;td&gt;a3（H100）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Azure&lt;/td&gt;
&lt;td&gt;NCasT4_v3&lt;/td&gt;
&lt;td&gt;NC A100 v4&lt;/td&gt;
&lt;td&gt;ND H100 v5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;中国云厂商&lt;/td&gt;
&lt;td&gt;阿里云/腾讯云均有 T4 系&lt;/td&gt;
&lt;td&gt;A10、A100 系（gn/GN 等命名）&lt;/td&gt;
&lt;td&gt;H800/H20 系&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;figcaption class="text-center mb-3"&gt;
表 2: 主流云厂商的单卡级 GPU 实例
&lt;/figcaption&gt;
&lt;p&gt;三家都提供“GPU 优化镜像”（自带驱动与 CUDA），选它，开箱即得上一章的“环境基线卡”里除型号外的全部字段。价格随时区、可用区、供需浮动，上表只给量级，决策前以各云官网价格计算器为准。&lt;/p&gt;
&lt;h2 id="计费模型与止损机制"&gt;计费模型与止损机制&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;模式&lt;/th&gt;
&lt;th&gt;折扣&lt;/th&gt;
&lt;th&gt;风险&lt;/th&gt;
&lt;th&gt;适用&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;按需（On-demand）&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;短期、不确定时长的使用&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;竞价/抢占（Spot/Preemptible）&lt;/td&gt;
&lt;td&gt;通常 50%–90%&lt;/td&gt;
&lt;td&gt;可能被回收，通知窗口只有约 2 分钟&lt;/td&gt;
&lt;td&gt;无状态任务、可断点续跑的训练（需 checkpoint）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;包年包月/预留&lt;/td&gt;
&lt;td&gt;视承诺而定&lt;/td&gt;
&lt;td&gt;资金锁定&lt;/td&gt;
&lt;td&gt;已验证的稳定负载&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;figcaption class="text-center mb-3"&gt;
表 3: 三种计费模式怎么选
&lt;/figcaption&gt;
&lt;p&gt;竞价实例的回收机制值得敬畏：回收前约两分钟通知，进程被直接终止。这意味着任何跑在 Spot 上的训练&lt;strong&gt;必须&lt;/strong&gt;周期性 checkpoint——这件事的调度学代价（“你抢走的是已完成的有效工作”）在&lt;a href="../../workloads/pytorch/"&gt;PyTorch 训练&lt;/a&gt;与&lt;a href="../../control-plane/kueue/"&gt;Kueue&lt;/a&gt;两章会系统展开。&lt;/p&gt;
&lt;p&gt;比选对计费模式更重要的是止损机制。三件套，缺一不可：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;预算告警&lt;/strong&gt;：AWS Budgets、GCP 预算提醒、Azure 成本警报，阈值设在“意外但可承受”的金额。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;标签治理&lt;/strong&gt;：所有实验资源打统一标签（如 &lt;code&gt;project=gpu-infra-lab&lt;/code&gt;），事故后能按标签一键盘点与清理。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;关机纪律&lt;/strong&gt;：实验结束当次会话内关机；长期不用就销毁（terminate），不是停止（stop）。&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="alert alert-warning-container"&gt;
&lt;div class="alert-warning-title px-2"&gt;
停机 ≠ 免费
&lt;/div&gt;
&lt;div class="alert-warning px-2"&gt;
停止的实例不再收计算费，但磁盘、快照、（部分云的）公网 IP 仍按小时计费。一个忘了的 200GiB 数据盘，一个月能在账单上安静地留下十几美元。GPU 实例真正的“关机”是销毁加确认无残留资源，本章实验的最后一节专门练习这件事。
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="动手实验开一台竞价-gpu-实例并彻底清理"&gt;动手实验：开一台竞价 GPU 实例并彻底清理&lt;/h2&gt;
&lt;h3 id="实验目标"&gt;实验目标&lt;/h3&gt;
&lt;p&gt;完整走一遍“选机型 → 查现货价 → 开机 → 体检 → 销毁 → 确认零残留”。完成后，“云上 GPU”对你不再是控制台里的选项，而是一条可复现的命令流水线。&lt;/p&gt;
&lt;h3 id="前置条件与成本预估"&gt;前置条件与成本预估&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;一个云账号与已配置好的 CLI（实验以 AWS CLI 为例，其他云流程等价）。&lt;/li&gt;
&lt;li&gt;成本：以 g5.xlarge（单卡 A10G 24GiB）竞价为例，通常 $0.3–0.7/小时，实验全程约半小时，预算控制在 1 美元以内；价格随区域与供需浮动，以官网为准。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="操作步骤"&gt;操作步骤&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 步骤 1：查询当前现货价，确认在预算内（us-east-1 为例）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;aws ec2 describe-spot-price-history &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --instance-types g5.xlarge --product-descriptions &lt;span class="s2"&gt;&amp;#34;Linux/UNIX&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --start-time &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;date -u +%FT%TZ&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; --region us-east-1 &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --query &lt;span class="s1"&gt;&amp;#39;SpotPriceHistory[0].[AvailabilityZone,SpotPrice]&amp;#39;&lt;/span&gt; --output text
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 步骤 2：选定一个自带驱动的 GPU AMI（Deep Learning AMI / NVIDIA GPU-optimized），&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 在控制台或如下查询最新版&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;aws ec2 describe-images --owners amazon --region us-east-1 &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --filters &lt;span class="s2"&gt;&amp;#34;Name=name,Values=Deep Learning AMI GPU*&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --query &lt;span class="s1"&gt;&amp;#39;sort_by(Images,&amp;amp;CreationDate)[-1].ImageId&amp;#39;&lt;/span&gt; --output text
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 步骤 3：一键开机（替换 key 与安全组；terminate 模式确保回收时自动清理）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;aws ec2 run-instances &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --image-id ami-xxxxxxxxxxxxx --instance-type g5.xlarge &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --key-name my-key --region us-east-1 &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --instance-market-options &lt;span class="s1"&gt;&amp;#39;{&amp;#34;MarketType&amp;#34;:&amp;#34;spot&amp;#34;,&amp;#34;SpotOptions&amp;#34;:{&amp;#34;SpotInstanceType&amp;#34;:&amp;#34;one-time&amp;#34;,&amp;#34;InstanceInterruptionBehavior&amp;#34;:&amp;#34;terminate&amp;#34;}}&amp;#39;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --tag-specifications &lt;span class="s1"&gt;&amp;#39;ResourceType=instance,Tags=[{Key=project,Value=gpu-infra-lab}]&amp;#39;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --query &lt;span class="s1"&gt;&amp;#39;Instances[0].InstanceId&amp;#39;&lt;/span&gt; --output text
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 步骤 4：等实例 running 后 SSH 登录，执行上一章的体检流程&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# lspci | grep -i nvidia &amp;amp;&amp;amp; nvidia-smi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 自带驱动的镜像应直接输出基线卡的全部字段&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 步骤 5：销毁（是 terminate，不是 stop）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;aws ec2 terminate-instances --instance-ids i-xxxxxxxx --region us-east-1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="验证清单"&gt;验证清单&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; 步骤 1 的现货价在你预设的预算线内（先设线，再开机）&lt;/li&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; SSH 后 &lt;code&gt;nvidia-smi&lt;/code&gt; 输出正常，型号与所选实例规格一致&lt;/li&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; 已执行 &lt;code&gt;terminate&lt;/code&gt;，实例状态变为 &lt;code&gt;terminated&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; &lt;code&gt;aws ec2 describe-volumes --filters Name=tag:project,Values=gpu-infra-lab&lt;/code&gt; 无残留卷（默认 AMI 的根卷随实例删除，若输出非空，逐个删除）&lt;/li&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; 账单控制台里该实例的计费已停止（有几分钟延迟，稍后复查一次）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="原理速览"&gt;原理速览&lt;/h3&gt;
&lt;p&gt;竞价实例的底层是云厂商用折扣出售空闲容量，换回的是随时回收权：&lt;code&gt;InstanceInterruptionBehavior=terminate&lt;/code&gt; 让回收即清理，避免“停在那里继续计费”。选择自带驱动的镜像是把“环境层”也交给镜像管理，与&lt;a href="../gpu-environment/"&gt;准备 GPU 运行环境&lt;/a&gt;“宿主机只管驱动”的结论一致——只不过这次连驱动安装也省了。&lt;/p&gt;
&lt;h3 id="清理与止损"&gt;清理与止损&lt;/h3&gt;
&lt;p&gt;实验的最后两步就是清理本身。养成肌肉记忆：&lt;strong&gt;terminate 之后必须验证资源清零&lt;/strong&gt;，并周期性按 &lt;code&gt;project&lt;/code&gt; 标签盘点全账号资源。&lt;/p&gt;
&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;本章把“去哪里拿卡”变成了一条可复现的流水线：按显存需求选卡、用竞价价压低成本、用预算告警和标签兜底、以 terminate 加验证收尾。你现在拥有随时可开可关的 GPU 算力，以及一套止损纪律——这两者合起来，就是后面所有实验的资源前提。下一章&lt;a href="../docker-gpu/"&gt;用 Docker 运行 GPU 工作负载&lt;/a&gt;在这台机器上跨过第一道工程门槛：让容器合法地拿到 GPU。成本的系统性分析（自购 vs 云、利用率核算）见&lt;a href="../../production/capacity-economics/"&gt;容量经济&lt;/a&gt;。&lt;/p&gt;</content:encoded></item><item><title>用 Docker 运行 GPU 工作负载</title><link>https://jimmysong.io/zh/book/ai-infra/getting-started/docker-gpu/</link><pubDate>Mon, 14 Sep 2026 00:00:00 +0000</pubDate><author>Jimmy Song</author><guid>https://jimmysong.io/zh/book/ai-infra/getting-started/docker-gpu/</guid><description>理解 GPU 容器与普通容器的本质差别，安装配置 NVIDIA Container Toolkit，掌握 --gpus 语义与基础镜像选择，跑通第一个 GPU 容器并建立单卡性能的量级直觉。</description><content:encoded>
&lt;blockquote&gt;
&lt;p&gt;容器是 GPU 与 Kubernetes 之间的通用语。学会让容器合法地拿到 GPU，后面的调度、切分、共享才有的可谈。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;上一章的结论在实战中的形态是：&lt;strong&gt;镜像带着工具链与框架走，宿主机只负责驱动&lt;/strong&gt;。本章把这句话变成操作——装好 NVIDIA Container Toolkit，理解 &lt;code&gt;--gpus&lt;/code&gt; 的语义，选对基础镜像，最后跑通你的第一个 GPU 容器。这一章也是通往 Kubernetes 的最后一级台阶：K8s 里 GPU Pod 的注入机制与本章完全同源。&lt;/p&gt;
&lt;h2 id="gpu-容器与普通容器的差别"&gt;GPU 容器与普通容器的差别&lt;/h2&gt;
&lt;p&gt;普通容器的隔离靠 namespace 与 cgroup，进程需要的资源（CPU、内存、文件系统）都由内核直接仲裁。GPU 打破了这个假设：GPU 设备不属于容器默认可见的 &lt;code&gt;/dev&lt;/code&gt;，访问 GPU 需要的驱动库（libcuda 等）也在容器 rootfs 之外；而 cgroup 也管不住显存（详见&lt;a href="../../fundamentals/gpu-memory-management/"&gt;显存管理&lt;/a&gt;）。&lt;/p&gt;
&lt;p&gt;所以“GPU 容器”的本质是：&lt;strong&gt;在容器启动时，把指定的 GPU 设备节点与配套的驱动用户态库安全地注入进去&lt;/strong&gt;。这件事由 NVIDIA Container Toolkit 完成。它为什么必须做成运行时钩子而不是普通的 volume 挂载、以及与 CDI 的关系，在&lt;a href="../../software-stack/nvidia-container-toolkit/"&gt;Container Toolkit 与 CDI&lt;/a&gt;有完整拆解，本章直接走可用路径。&lt;/p&gt;
&lt;h2 id="安装与配置-nvidia-container-toolkit"&gt;安装与配置 NVIDIA Container Toolkit&lt;/h2&gt;
&lt;p&gt;在宿主机上（Ubuntu/Debian 为例，其他发行版与最新命令以官方文档为准）：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 1. 配置软件源&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;|&lt;/span&gt; sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sL https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;|&lt;/span&gt; sed &lt;span class="s1"&gt;&amp;#39;s#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g&amp;#39;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;|&lt;/span&gt; sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 2. 安装并注册到 Docker&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; sudo apt-get install -y nvidia-container-toolkit
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nvidia-ctk runtime configure --runtime&lt;span class="o"&gt;=&lt;/span&gt;docker
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 3. 重启 Docker 使配置生效&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl restart docker&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;前置条件只有一条：上一章验证过的、工作正常的宿主机驱动。Toolkit 不含也不替代驱动。&lt;/p&gt;
&lt;h2 id="--gpus-的语义"&gt;&lt;code&gt;--gpus&lt;/code&gt; 的语义&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;--gpus&lt;/code&gt; 回答“给这个容器哪些卡、给几张”：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker run --rm --gpus all nvidia/cuda:12.4.1-base-ubuntu22.04 nvidia-smi &lt;span class="c1"&gt;# 全部卡&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker run --rm --gpus &lt;span class="m"&gt;2&lt;/span&gt; nvidia/cuda:12.4.1-base-ubuntu22.04 nvidia-smi &lt;span class="c1"&gt;# 前两块&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker run --rm --gpus &lt;span class="s1"&gt;&amp;#39;&amp;#34;device=0,2&amp;#34;&amp;#39;&lt;/span&gt; nvidia/cuda:12.4.1-base-ubuntu22.04 nvidia-smi &lt;span class="c1"&gt;# 指定序号&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;--gpus all&lt;/code&gt; 是实验环境的默认；指定序号是一次性的调试用法。&lt;code&gt;--runtime=nvidia&lt;/code&gt; 加 &lt;code&gt;NVIDIA_VISIBLE_DEVICES&lt;/code&gt; 环境变量的旧写法仍能见到，语义等价。&lt;/p&gt;
&lt;div class="alert alert-warning-container"&gt;
&lt;div class="alert-warning-title px-2"&gt;
不要把 GPU UUID 写进应用配置
&lt;/div&gt;
&lt;div class="alert-warning px-2"&gt;
一次性 &lt;code&gt;docker run&lt;/code&gt; 里用 &lt;code&gt;device=0,2&lt;/code&gt; 无妨，但把 GPU UUID/序号写进应用的 YAML 是反模式：硬件更换、节点迁移都会让它失效。“哪块卡给谁”应当交给调度器决定，这是&lt;a href="../../control-plane/"&gt;控制面&lt;/a&gt;整部分的主题。
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="选对基础镜像"&gt;选对基础镜像&lt;/h2&gt;
&lt;p&gt;NVIDIA CUDA 镜像一个名字有三种 tag，区别常被忽视：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tag&lt;/th&gt;
&lt;th&gt;内容&lt;/th&gt;
&lt;th&gt;体积&lt;/th&gt;
&lt;th&gt;适用&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;base&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;驱动兼容层与最小 CUDA 库&lt;/td&gt;
&lt;td&gt;最小&lt;/td&gt;
&lt;td&gt;只跑已编译好的程序、&lt;code&gt;nvidia-smi&lt;/code&gt; 冒烟&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;runtime&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;base&lt;/code&gt; + CUDA 运行时库&lt;/td&gt;
&lt;td&gt;中&lt;/td&gt;
&lt;td&gt;运行 CUDA 应用（多数推理场景够用）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;devel&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;runtime&lt;/code&gt; + nvcc、头文件&lt;/td&gt;
&lt;td&gt;大&lt;/td&gt;
&lt;td&gt;需要在容器内编译 CUDA 代码（如源码装 flash-attention）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;figcaption class="text-center mb-3"&gt;
表 1: nvidia/cuda 镜像三种 tag
&lt;/figcaption&gt;
&lt;p&gt;两条选择建议：优先用&lt;strong&gt;框架官方镜像&lt;/strong&gt;（如 &lt;code&gt;pytorch/pytorch:2.5.1-cuda12.4-cudnn9-runtime&lt;/code&gt;），它替你解决了 CUDA/cuDNN/框架的版本配套；必须自建镜像时，tag 钉住完整版本号，禁止 &lt;code&gt;latest&lt;/code&gt;——镜像里的 CUDA 版本要满足&lt;a href="../gpu-environment/"&gt;准备 GPU 运行环境&lt;/a&gt;一章的规则 3（≤ 宿主机驱动支持上限）。&lt;/p&gt;
&lt;h2 id="常见故障速查"&gt;常见故障速查&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;现象&lt;/th&gt;
&lt;th&gt;常见原因&lt;/th&gt;
&lt;th&gt;第一反应&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;could not select device driver &amp;quot;&amp;quot; with capabilities: [[gpu]]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Toolkit 未安装、未注册或 Docker 未重启&lt;/td&gt;
&lt;td&gt;重做配置三步，确认 &lt;code&gt;systemctl restart docker&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;容器内 &lt;code&gt;CUDA driver version is insufficient&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;宿主机驱动低于镜像 CUDA 要求&lt;/td&gt;
&lt;td&gt;升级宿主机驱动，或换低 CUDA tag&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;容器能起但 &lt;code&gt;torch.cuda.is_available()&lt;/code&gt; 为 False&lt;/td&gt;
&lt;td&gt;忘了 &lt;code&gt;--gpus&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;检查启动命令；容器内 &lt;code&gt;ls /dev/nvidia*&lt;/code&gt; 确认设备可见&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;容器内 &lt;code&gt;nvidia-smi&lt;/code&gt; 与宿主机驱动版本不一致的报错&lt;/td&gt;
&lt;td&gt;镜像与驱动错配&lt;/td&gt;
&lt;td&gt;核对上一章基线卡的 CUDA 上限&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rootless Docker / Podman 行为异常&lt;/td&gt;
&lt;td&gt;运行时路径不同&lt;/td&gt;
&lt;td&gt;参照所用运行时的官方 GPU 文档&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;figcaption class="text-center mb-3"&gt;
表 2: GPU 容器常见故障与第一反应
&lt;/figcaption&gt;
&lt;h2 id="动手实验从裸-docker-到第一个-gpu-容器"&gt;动手实验：从裸 Docker 到第一个 GPU 容器&lt;/h2&gt;
&lt;h3 id="实验目标"&gt;实验目标&lt;/h3&gt;
&lt;p&gt;在一台宿主机驱动正常的机器上完成 Toolkit 安装配置，跑通两类冒烟测试（系统级 &lt;code&gt;nvidia-smi&lt;/code&gt;、框架级 PyTorch 断言），并用一次矩阵乘法建立单卡算力的量级直觉。&lt;/p&gt;
&lt;h3 id="前置条件与成本预估"&gt;前置条件与成本预估&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;上一章体检通过的机器（本地或云实例）；已装 Docker。&lt;/li&gt;
&lt;li&gt;磁盘空间约 5–10 GiB（镜像下载）；网络流量产生少量费用，计算成本为零（本地）或已含在实例小时价内（云）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="操作步骤"&gt;操作步骤&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 步骤 1：安装并配置 Toolkit（见上文三步命令），然后做最小验证&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker info &lt;span class="p"&gt;|&lt;/span&gt; grep -i nvidia &lt;span class="c1"&gt;# 预期看到 nvidia 运行时已注册&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 步骤 2：系统级冒烟——容器内应看到与宿主机相同的 GPU 与驱动版本&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker run --rm --gpus all nvidia/cuda:12.4.1-base-ubuntu22.04 nvidia-smi
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 步骤 3：框架级冒烟——PyTorch 真正完成一次 CUDA 调用&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker run --rm --gpus all pytorch/pytorch:2.5.1-cuda12.4-cudnn9-runtime &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; python -c &lt;span class="s2"&gt;&amp;#34;import torch; print(torch.cuda.is_available(), torch.cuda.get_device_name(0))&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 步骤 4：性能直觉——单精度大矩阵乘法，折算 TFLOPS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker run --rm --gpus all pytorch/pytorch:2.5.1-cuda12.4-cudnn9-runtime python -c &lt;span class="s2"&gt;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;import torch, time
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;x = torch.randn(8192, 8192, device=&amp;#39;cuda&amp;#39;)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;torch.cuda.synchronize(); t = time.time()
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;for _ in range(10): y = x @ x
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;torch.cuda.synchronize()
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;dt = (time.time() - t) / 10
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;print(f&amp;#39;achieved TFLOPS: {2 * 8192**3 / dt / 1e12:.1f}&amp;#39;)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="验证清单"&gt;验证清单&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; &lt;code&gt;docker info&lt;/code&gt; 能看到 nvidia 运行时&lt;/li&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; 容器内 &lt;code&gt;nvidia-smi&lt;/code&gt; 的 &lt;code&gt;Driver Version&lt;/code&gt; 与宿主机完全一致&lt;/li&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; PyTorch 输出 &lt;code&gt;True&lt;/code&gt; 与正确的卡型号&lt;/li&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; 步骤 4 输出的 TFLOPS 达到该卡标称 FP32 算力的 50%–80%（对照上一章基线卡的型号查标称值；明显偏低通常是落到了 PCIe 供电限制或占用干扰，而不是“GPU 慢”）&lt;/li&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; 把实测 TFLOPS 记入基线卡——后续章节验证调度是否引入性能损失时，以它为参照&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="原理速览"&gt;原理速览&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;nvidia-container-runtime&lt;/code&gt; 在容器创建前通过钩子检查 &lt;code&gt;--gpus&lt;/code&gt; 声明，把对应设备节点（&lt;code&gt;/dev/nvidia0&lt;/code&gt; 等）与匹配宿主机驱动版本的用户态库挂进容器。容器内的 CUDA 应用因此“以为”自己装了完整驱动，实际复用的是宿主机那一份——版本一致性由注入机制保证，这正是规则 3 的实现基础。&lt;/p&gt;
&lt;h3 id="清理与止损"&gt;清理与止损&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker rmi nvidia/cuda:12.4.1-base-ubuntu22.04 pytorch/pytorch:2.5.1-cuda12.4-cudnn9-runtime&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;云实例若不再进入下一章实验，执行&lt;a href="../first-gpu-machine/"&gt;云上第一台 GPU 机器&lt;/a&gt;一章的 terminate 流程。&lt;/p&gt;
&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;本章跨过了 GPU 工程的第一道门槛：容器。你掌握了 &lt;code&gt;--gpus&lt;/code&gt; 的语义、镜像 tag 的选择逻辑，并把实测 TFLOPS 记入基线卡——从此“环境有问题”和“代码有问题”可以被区分开。更重要的伏笔是：容器里的 GPU 访问靠运行时注入，而 Kubernetes 的 GPU 方案（device plugin、GPU Operator）正是把这套注入搬进 kubelet 的调度体系。下一章&lt;a href="../single-node-k8s/"&gt;单机 Kubernetes 与 GPU Operator&lt;/a&gt;完成最后一步：让 GPU 变成 &lt;code&gt;nvidia.com/gpu: 1&lt;/code&gt; 这样一行可调度的资源请求。&lt;/p&gt;</content:encoded></item><item><title>单机 Kubernetes 与 GPU Operator：让 GPU 成为可调度资源</title><link>https://jimmysong.io/zh/book/ai-infra/getting-started/single-node-k8s/</link><pubDate>Mon, 14 Sep 2026 00:00:00 +0000</pubDate><author>Jimmy Song</author><guid>https://jimmysong.io/zh/book/ai-infra/getting-started/single-node-k8s/</guid><description>在单机 Kubernetes 上部署 GPU Operator，把上一章的 GPU 容器升级为可调度的 Pod：理解 device plugin 的上报链路，跑通第一个请求 nvidia.com/gpu 的 Pod，并诚实界定你此刻拥有与尚未拥有的平台能力。</description><content:encoded>
&lt;blockquote&gt;
&lt;p&gt;当一块 GPU 能用 &lt;code&gt;nvidia.com/gpu: 1&lt;/code&gt; 这样一行资源请求被调度时，它才从“机器的部件”变成“平台的资源”。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;你现在有了随时可开的云上 GPU（&lt;a href="../first-gpu-machine/"&gt;上一章&lt;/a&gt;）和能注入 GPU 的容器（&lt;a href="../docker-gpu/"&gt;再上一章&lt;/a&gt;）。但“哪块卡给谁”仍由人手决定：换节点要改参数、配额靠口头约定、用量无人记账。 Kubernetes 把这些变成 API 与控制循环，而 GPU Operator 把接入 GPU 所需的一整套组件（设备插件、容器运行时配置、监控导出器）变成一次 Helm 安装。本章在单机集群上完成这最后一步，并用一张“能力边界表”告诉你：跑通之后，你真正拥有了什么、还缺什么。&lt;/p&gt;
&lt;h2 id="为什么-kubernetes-是-gpu-的操作系统"&gt;为什么 Kubernetes 是 GPU 的操作系统&lt;/h2&gt;
&lt;p&gt;回忆上一章：GPU 容器的关键在运行时注入设备与驱动库。Kubernetes 面对的额外问题是&lt;strong&gt;多租与调度&lt;/strong&gt;——集群里的 GPU 分属不同节点，Pod 的创建由调度器而非人决定。K8s 的解法是设备插件（Device Plugin）机制：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;设备插件 DaemonSet 在每个节点上向 kubelet 上报“本节点有 N 块 GPU”（&lt;code&gt;nvidia.com/gpu&lt;/code&gt;）；&lt;/li&gt;
&lt;li&gt;kubelet 把它纳入节点可分配资源（Allocatable），调度器据此决定 Pod 落点；&lt;/li&gt;
&lt;li&gt;Pod 请求 &lt;code&gt;nvidia.com/gpu&lt;/code&gt; 并被绑定时，kubelet 调用与上一章同源的注入机制把卡交给容器。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这套“上报—调度—注入”的链路是理解后续一切的基础：HAMi 的显存切分、Kueue 的队列治理、MIG 的动态重配，全部挂在这条链路的延长线上。设备抽象的表达力边界（为什么 GPU 不是“另一种 CPU”）见&lt;a href="../../fundamentals/k8s-device-model/"&gt;设备资源抽象与演进&lt;/a&gt;，各厂商加速器接入 K8s 的统一模式见&lt;a href="../../fundamentals/accelerators-on-kubernetes/"&gt;Kubernetes 管理模型&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id="三种单机集群怎么选"&gt;三种单机集群怎么选&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;工具&lt;/th&gt;
&lt;th&gt;GPU 支持&lt;/th&gt;
&lt;th&gt;特点&lt;/th&gt;
&lt;th&gt;适用&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;minikube&lt;/td&gt;
&lt;td&gt;官方支持，&lt;code&gt;--gpus=all&lt;/code&gt; 一个参数&lt;/td&gt;
&lt;td&gt;部署最简单，节点是 Docker 容器&lt;/td&gt;
&lt;td&gt;本章实验、快速验证&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;kind&lt;/td&gt;
&lt;td&gt;需手动挂载设备节点&lt;/td&gt;
&lt;td&gt;多节点集群轻量，CI 常用&lt;/td&gt;
&lt;td&gt;无 GPU 或愿意手工配置者&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;k3s&lt;/td&gt;
&lt;td&gt;需配置 containerd 的 nvidia 运行时&lt;/td&gt;
&lt;td&gt;systemd 服务、最接近生产形态&lt;/td&gt;
&lt;td&gt;单机常驻、边缘节点&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;figcaption class="text-center mb-3"&gt;
表 1: minikube / kind / k3s 对比
&lt;/figcaption&gt;
&lt;p&gt;本章用 minikube（路径最短），k3s 作为课后练习：把同样的 GPU Operator 流程搬到 k3s 上，你会遇到真实的 containerd 运行时配置问题——那正是上一章 Toolkit 章节的延续。&lt;/p&gt;
&lt;h2 id="gpu-operator-解决了什么"&gt;GPU Operator 解决了什么&lt;/h2&gt;
&lt;p&gt;没有 Operator 时，接入一块 GPU 需要：装 nvidia-container-toolkit 并改运行时配置、部署 device plugin、部署 DCGM exporter、（必要时）装驱动——四件事四个版本矩阵，且要逐节点做。GPU Operator 把它们全部 DaemonSet 化：由 Operator 按节点状态自动安装、升级、对齐版本。&lt;/p&gt;
&lt;p&gt;本实验的场景是“宿主机已有驱动”（云上 GPU 优化镜像，或按&lt;a href="../gpu-environment/"&gt;准备 GPU 运行环境&lt;/a&gt;一章手动安装），因此安装时显式关闭 Operator 的驱动组件：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;--set driver.enabled&lt;span class="o"&gt;=&lt;/span&gt;false&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;其余组件（toolkit、device-plugin、DCGM exporter 等）仍由 Operator 管理。何时该让 Operator 连驱动一起管（裸金属、驱动即代码的场景），见&lt;a href="../../control-plane/gpu-operator/"&gt;GPU Operator&lt;/a&gt;的完整讨论。&lt;/p&gt;
&lt;h2 id="动手实验调度你的第一个-gpu-pod"&gt;动手实验：调度你的第一个 GPU Pod&lt;/h2&gt;
&lt;h3 id="实验目标"&gt;实验目标&lt;/h3&gt;
&lt;p&gt;在 minikube 单机集群上部署 GPU Operator，让 &lt;code&gt;nvidia.com/gpu&lt;/code&gt; 出现在节点可分配资源中，并成功调度一个请求 GPU 的 Pod 跑通 &lt;code&gt;nvidia-smi&lt;/code&gt;。&lt;/p&gt;
&lt;h3 id="前置条件与成本预估"&gt;前置条件与成本预估&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;上一章完成的机器：宿主机驱动正常、&lt;code&gt;nvidia-ctk runtime configure --runtime=docker&lt;/code&gt; 已执行且 Docker 已重启、&lt;code&gt;docker run --rm --gpus all ... nvidia-smi&lt;/code&gt; 通过。&lt;/li&gt;
&lt;li&gt;已装 kubectl、Helm、minikube；机器建议 ≥ 4 vCPU / 8 GiB 内存（Operator 全家桶与集群本体都要占资源）。&lt;/li&gt;
&lt;li&gt;成本：全程本地/实例内完成，无额外云资源；云实例时长约半小时。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="操作步骤"&gt;操作步骤&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 步骤 1：启动带 GPU 的单机集群（Docker 驱动，把 GPU 透传给 minikube 节点）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;minikube start --driver&lt;span class="o"&gt;=&lt;/span&gt;docker --gpus&lt;span class="o"&gt;=&lt;/span&gt;all
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl get nodes &lt;span class="c1"&gt;# 预期一个 Ready 节点&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 步骤 2：部署 GPU Operator（宿主机已有驱动，关闭 driver 组件）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;helm repo add nvidia https://helm.ngc.nvidia.com/nvidia --force-update
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;helm repo update
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;helm upgrade --install gpu-operator nvidia/gpu-operator &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --namespace gpu-operator --create-namespace &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --set driver.enabled&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 步骤 3：等待 Operator 组件就绪（toolkit、device-plugin、dcgm-exporter 等）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl -n gpu-operator get pods --watch
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 预期：所有 Pod Running，其中 nvidia-device-plugin-*. .. 1/1 Running&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 步骤 4：确认 GPU 已进入可分配资源&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl get nodes -o &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;{.items[0].status.allocatable.nvidia\.com/gpu}{&amp;#34;\n&amp;#34;}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 预期输出：1（单卡机器；多卡则是对应数量）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 步骤 5：调度第一个 GPU Pod&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cat &lt;span class="s"&gt;&amp;lt;&amp;lt;&amp;#39;EOF&amp;#39; | kubectl apply -f -
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;apiVersion: v1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;kind: Pod
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;metadata:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; name: gpu-smoke
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;spec:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; restartPolicy: Never
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; containers:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; - name: cuda
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; image: nvidia/cuda:12.4.1-base-ubuntu22.04
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; command: [&amp;#34;nvidia-smi&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; resources:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; limits:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; nvidia.com/gpu: 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;EOF&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl &lt;span class="nb"&gt;wait&lt;/span&gt; --for&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;condition&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;complete&lt;/span&gt; pod/gpu-smoke --timeout&lt;span class="o"&gt;=&lt;/span&gt;300s
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl logs gpu-smoke&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="验证清单"&gt;验证清单&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; 步骤 3 中 device-plugin Pod 稳定 Running（反复重启说明节点侧注入环境有问题，见下方排障表）&lt;/li&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; 步骤 4 输出与物理卡数一致&lt;/li&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; &lt;code&gt;kubectl logs gpu-smoke&lt;/code&gt; 输出的 &lt;code&gt;Driver Version&lt;/code&gt; 与宿主机基线卡一致&lt;/li&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; &lt;code&gt;kubectl describe pod gpu-smoke&lt;/code&gt; 的 Events 里能看到调度成功与镜像拉取记录&lt;/li&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; 实验数据（卡数、allocatable 值、Pod 日志）记入基线卡&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="故障排障速查"&gt;故障排障速查&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;现象&lt;/th&gt;
&lt;th&gt;常见原因&lt;/th&gt;
&lt;th&gt;第一反应&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;allocatable 输出 0&lt;/td&gt;
&lt;td&gt;minikube 容器没拿到 GPU / device-plugin 未就绪 / 宿主机驱动异常&lt;/td&gt;
&lt;td&gt;进节点验证：&lt;code&gt;minikube ssh nvidia-smi&lt;/code&gt;；再查 device-plugin 日志&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operator Pod 一直 Pending&lt;/td&gt;
&lt;td&gt;单机资源不足&lt;/td&gt;
&lt;td&gt;确认 ≥ 4C/8G；&lt;code&gt;kubectl describe pod&lt;/code&gt; 看被谁卡住&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gpu-smoke 一直 ContainerCreating&lt;/td&gt;
&lt;td&gt;toolkit 组件未就绪或运行时配置冲突&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kubectl -n gpu-operator logs -l app=nvidia-container-toolkit&lt;/code&gt;；等 toolkit DaemonSet 就绪后重试&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pod 起了但报 &lt;code&gt;insufficient&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;镜像 CUDA 版本高于宿主机驱动上限&lt;/td&gt;
&lt;td&gt;对照基线卡 CUDA 上限（见“准备 GPU 运行环境”的规则 3），换镜像 tag&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;minikube start&lt;/code&gt; 卡死或报 GPU 相关错误&lt;/td&gt;
&lt;td&gt;Docker 的 nvidia 运行时未配置或未重启&lt;/td&gt;
&lt;td&gt;重做上一章三步配置；先用 &lt;code&gt;docker run --rm --gpus all&lt;/code&gt; 自证&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;figcaption class="text-center mb-3"&gt;
表 2: 单机 GPU 集群常见故障与第一反应
&lt;/figcaption&gt;
&lt;h3 id="原理速览"&gt;原理速览&lt;/h3&gt;
&lt;p&gt;你在步骤 4–5 看到的就是本章开头那条链路：device plugin 上报 → kubelet 更新 Allocatable → 调度器把 &lt;code&gt;gpu-smoke&lt;/code&gt; 绑到唯一节点 → kubelet 用 toolkit（Operator 部署的那套）完成与 &lt;code&gt;docker run --gpus&lt;/code&gt; 同源的注入。&lt;strong&gt;上一章与本章是同一机制在两种编排下的两个投影&lt;/strong&gt;——理解了这一点，换到任何 K8s 发行版都不再神秘。&lt;/p&gt;
&lt;h3 id="清理与止损"&gt;清理与止损&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl delete pod gpu-smoke
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;helm uninstall gpu-operator -n gpu-operator &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; kubectl delete ns gpu-operator
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;minikube delete --all&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;云实例若不再使用，执行&lt;a href="../first-gpu-machine/"&gt;云上第一台 GPU 机器&lt;/a&gt;一章的 terminate 流程并确认零残留。&lt;/p&gt;
&lt;h2 id="你还没有得到什么"&gt;你还没有得到什么&lt;/h2&gt;
&lt;p&gt;诚实的能力边界——这也是全书剩余部分的目录：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;能力&lt;/th&gt;
&lt;th&gt;状态&lt;/th&gt;
&lt;th&gt;答案所在&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GPU 以 &lt;code&gt;nvidia.com/gpu&lt;/code&gt; 被调度&lt;/td&gt;
&lt;td&gt;✅ 已拥有&lt;/td&gt;
&lt;td&gt;本章&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;一块卡分给多个 Pod / 显存级配额&lt;/td&gt;
&lt;td&gt;❌ 整卡为最小单位&lt;/td&gt;
&lt;td&gt;&lt;a href="../../data-plane/"&gt;数据平面&lt;/a&gt;：MIG、HAMi、DRA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;队列、配额、优先级、公平共享&lt;/td&gt;
&lt;td&gt;❌ 尚无治理&lt;/td&gt;
&lt;td&gt;&lt;a href="../../control-plane/"&gt;控制面&lt;/a&gt;：Kueue、Volcano&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;拓扑感知放置（NVLink/NUMA）&lt;/td&gt;
&lt;td&gt;❌ 调度器无拓扑视野&lt;/td&gt;
&lt;td&gt;&lt;a href="../../control-plane/numa-scheduling/"&gt;NUMA 感知调度&lt;/a&gt;、&lt;a href="../../control-plane/placement-policy/"&gt;放置策略&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPU 利用率/显存/健康度的指标语义&lt;/td&gt;
&lt;td&gt;⚠️ exporter 已部署，语义未建&lt;/td&gt;
&lt;td&gt;&lt;a href="../../observability/"&gt;可观测与验收&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;异构加速器接入（NVIDIA 之外）&lt;/td&gt;
&lt;td&gt;❌ 仅 NVIDIA 路径&lt;/td&gt;
&lt;td&gt;&lt;a href="../../fundamentals/gpu-landscape/"&gt;加速器全景&lt;/a&gt;、&lt;a href="../../data-plane/ascend-vnpu/"&gt;昇腾案例&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;figcaption class="text-center mb-3"&gt;
表 3: 跑通本章后：已拥有与尚未拥有
&lt;/figcaption&gt;
&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;上手篇到此完成闭环：环境验证 → 云上开机 → GPU 容器 → 可调度的 GPU Pod。你现在已经拥有一台随时可开、可被 Kubernetes 调度的 GPU 机器，以及一张记录了驱动版本、CUDA 上限、实测 TFLOPS、allocatable 的环境基线卡。上面的边界表就是你的下一步菜单：如果“分卡”是你最迫切的痛点，去&lt;a href="../../data-plane/"&gt;数据平面&lt;/a&gt;；如果“治理”是，去&lt;a href="../../control-plane/"&gt;控制面&lt;/a&gt;；如果你想先弄懂每一步背后发生了什么，&lt;a href="../../fundamentals/"&gt;认识机器与问题&lt;/a&gt;会从硅片开始讲起。&lt;/p&gt;</content:encoded></item></channel></rss>