GPU

本地 GPU 学习实验

·32 分钟阅读·12654 字

本地环境中 GPU 驱动、PyTorch、NVML、CUDA、Docker 与 DCGM 的学习实验路线

📋 目录

本地 GPU 学习实验

假设电脑是 Windows 11 + NVIDIA 独立显卡,推荐采用:

Windows NVIDIA 驱动
        ↓
WSL2 + Ubuntu
        ↓
CUDA / PyTorch
        ↓
Docker GPU 容器
        ↓
NVML GPU 监控
        ↓
Prometheus + Grafana
        ↓
单节点 Kubernetes GPU 实验

WSL2 可以让 Linux CUDA 程序使用 Windows 主机的 NVIDIA GPU,因此非常适合学习 Linux、CUDA、容器和云原生 GPU 运维。(NVIDIA Docs)


一、先检查硬件和驱动

在 Windows PowerShell 中运行:

nvidia-smi

正常情况下会看到:

NVIDIA-SMI  xxx.xx
Driver Version: xxx.xx
CUDA Version: xx.x

GPU Name
GPU Utilization
Memory Usage
Temperature
Processes

这里需要理解一个重点:

nvidia-smi 显示的 CUDA Version

代表当前驱动最高支持的 CUDA 版本,不等于你已经安装了 CUDA Toolkit。

再检查显卡型号:

nvidia-smi --query-gpu=name,memory.total,driver_version --format=csv

例如可能输出:

name, memory.total [MiB], driver_version
NVIDIA GeForce RTX 3050 Laptop GPU, 4096 MiB, 580.xx

只要 nvidia-smi 可以正常工作,就可以继续。


二、安装 WSL2 和 Ubuntu

使用管理员 PowerShell:

wsl --install -d Ubuntu

安装完成后重启电脑。

查看 WSL 状态:

wsl --status

查看发行版:

wsl -l -v

应该看到:

NAME       STATE      VERSION
Ubuntu     Running    2

如果 Ubuntu 不是 WSL2:

wsl --set-version Ubuntu 2

更新 WSL:

wsl --update

进入 Ubuntu:

wsl

然后在 Ubuntu 中检查 GPU:

nvidia-smi

如果 Windows 和 WSL 中都能看到显卡,说明这条调用链已经打通:

Ubuntu 程序
    ↓
WSL2 GPU 接口
    ↓
Windows NVIDIA 驱动
    ↓
物理 GPU

不要在 WSL2 里安装 Linux NVIDIA 显卡驱动。 WSL 使用的是 Windows 主机驱动提供的 GPU 能力;在 WSL 内重复安装 Linux 驱动容易破坏环境。NVIDIA 官方流程也是先安装支持 WSL 的 Windows 驱动,再安装 WSL2 和 Linux 开发环境。(NVIDIA Docs)


三、准备 Python 环境

在 Ubuntu 中执行:

sudo apt update
sudo apt install -y python3 python3-pip python3-venv git build-essential

建立独立虚拟环境:

python3 -m venv ~/venvs/nvidia-lab
source ~/venvs/nvidia-lab/bin/activate

升级基础工具:

python -m pip install --upgrade pip setuptools wheel

以后进入实验环境都执行:

source ~/venvs/nvidia-lab/bin/activate

退出环境:

deactivate

四、先使用 PyTorch 体验 GPU

对于初学者,建议先装 PyTorch,而不是立即安装完整 CUDA Toolkit。

原因是:

直接学习 CUDA C++
难度较高

PyTorch 调用 CUDA
几分钟就能看到效果

请使用 PyTorch 官方安装选择器生成与你当前系统匹配的命令。通常需要选择:

OS: Linux
Package: Pip
Language: Python
Compute Platform: CUDA

安装后运行:

python

输入:

import torch

print("PyTorch:", torch.__version__)
print("CUDA available:", torch.cuda.is_available())
print("CUDA runtime:", torch.version.cuda)

if torch.cuda.is_available():
    print("GPU:", torch.cuda.get_device_name(0))
    print("GPU count:", torch.cuda.device_count())

期望看到:

CUDA available: True
GPU: NVIDIA GeForce RTX ...

做一次矩阵计算

创建文件:

nano gpu_test.py

写入:

import time
import torch

def benchmark(device: str, size: int = 5000) -> float:
    a = torch.randn(size, size, device=device)
    b = torch.randn(size, size, device=device)

    if device == "cuda":
        torch.cuda.synchronize()

    start = time.perf_counter()
    _ = a @ b

    if device == "cuda":
        torch.cuda.synchronize()

    return time.perf_counter() - start

def main() -> None:
    print(f"PyTorch version: {torch.__version__}")
    print(f"CUDA available: {torch.cuda.is_available()}")

    cpu_time = benchmark("cpu", size=2000)
    print(f"CPU time: {cpu_time:.4f} seconds")

    if torch.cuda.is_available():
        print(f"GPU: {torch.cuda.get_device_name(0)}")
        gpu_time = benchmark("cuda", size=2000)
        print(f"GPU time: {gpu_time:.4f} seconds")
        print(f"Approximate speedup: {cpu_time / gpu_time:.2f}x")

if __name__ == "__main__":
    main()

运行:

python gpu_test.py

第一次 GPU 运算可能较慢,因为会发生 CUDA 初始化。更准确的测试应该预热后运行多次,不过这个实验足以验证 PyTorch 是否真正使用了 GPU。

运行程序的同时,另开一个终端执行:

watch -n 1 nvidia-smi

你会看到:

  • GPU 利用率上升

  • 显存被占用

  • Python 进程出现

  • 功耗和温度变化

这就是最简单的 GPU 可观测性实验。


五、使用 NVML 编写监控程序

安装 Python NVML 库:

pip install nvidia-ml-py

创建:

nano gpu_monitor.py

写入:

import time

from pynvml import (
    NVMLError,
    nvmlDeviceGetCount,
    nvmlDeviceGetHandleByIndex,
    nvmlDeviceGetMemoryInfo,
    nvmlDeviceGetName,
    nvmlDeviceGetPowerUsage,
    nvmlDeviceGetTemperature,
    nvmlDeviceGetUtilizationRates,
    nvmlInit,
    nvmlShutdown,
    NVML_TEMPERATURE_GPU,
)

def mib(value: int) -> float:
    return value / 1024 / 1024

def main() -> None:
    try:
        nvmlInit()
        count = nvmlDeviceGetCount()

        while True:
            print("-" * 70)

            for index in range(count):
                handle = nvmlDeviceGetHandleByIndex(index)
                name = nvmlDeviceGetName(handle)
                utilization = nvmlDeviceGetUtilizationRates(handle)
                memory = nvmlDeviceGetMemoryInfo(handle)
                temperature = nvmlDeviceGetTemperature(
                    handle,
                    NVML_TEMPERATURE_GPU,
                )

                try:
                    power_watts = nvmlDeviceGetPowerUsage(handle) / 1000
                    power_text = f"{power_watts:.1f} W"
                except NVMLError:
                    power_text = "unsupported"

                print(f"GPU {index}: {name}")
                print(f"  GPU utilization: {utilization.gpu}%")
                print(f"  Memory utilization: {utilization.memory}%")
                print(
                    f"  VRAM: {mib(memory.used):.0f} / "
                    f"{mib(memory.total):.0f} MiB"
                )
                print(f"  Temperature: {temperature}°C")
                print(f"  Power: {power_text}")

            time.sleep(2)

    except KeyboardInterrupt:
        print("\nMonitor stopped.")
    except NVMLError as exc:
        print(f"NVML error: {exc}")
    finally:
        try:
            nvmlShutdown()
        except NVMLError:
            pass

if __name__ == "__main__":
    main()

运行:

python gpu_monitor.py

同时运行前面的矩阵计算程序:

python gpu_test.py

你就能观察到:

Python 计算任务
    ↓
PyTorch
    ↓
CUDA
    ↓
NVIDIA Driver
    ↓
GPU

与此同时:

GPU
    ↓
Driver
    ↓
NVML
    ↓
gpu_monitor.py

这个实验非常适合以后扩展成 AIOps 项目,例如:

  • GPU 温度超过阈值时报警

  • 显存使用超过 90% 时通知

  • 检测 GPU 空闲资源

  • 收集 GPU 使用趋势

  • 关联 GPU 进程与用户


六、尝试 CUDA C++ 编程

只有在需要使用以下工具时,才需要安装 CUDA Toolkit:

nvcc
CUDA headers
CUDA samples
Nsight 工具
自行编译 CUDA 程序

CUDA Toolkit 包含编译器、运行时、加速库、调试和性能分析工具。(NVIDIA Developer)

安装完成后检查:

nvcc --version

创建:

nano vector_add.cu

写入:

#include <cuda_runtime.h>

#include <cstdlib>
#include <iostream>
#include <vector>

#define CUDA_CHECK(call)                                              \
    do {                                                              \
        cudaError_t error = (call);                                   \
        if (error != cudaSuccess) {                                   \
            std::cerr << "CUDA error: "                               \
                      << cudaGetErrorString(error)                     \
                      << " at line " << __LINE__ << std::endl;         \
            std::exit(EXIT_FAILURE);                                  \
        }                                                             \
    } while (0)

__global__ void vector_add(
    const float* a,
    const float* b,
    float* result,
    int size
) {
    int index = blockIdx.x * blockDim.x + threadIdx.x;

    if (index < size) {
        result[index] = a[index] + b[index];
    }
}

int main() {
    constexpr int size = 1'000'000;
    const std::size_t bytes = size * sizeof(float);

    std::vector<float> host_a(size, 1.0F);
    std::vector<float> host_b(size, 2.0F);
    std::vector<float> host_result(size);

    float* device_a = nullptr;
    float* device_b = nullptr;
    float* device_result = nullptr;

    CUDA_CHECK(cudaMalloc(&device_a, bytes));
    CUDA_CHECK(cudaMalloc(&device_b, bytes));
    CUDA_CHECK(cudaMalloc(&device_result, bytes));

    CUDA_CHECK(cudaMemcpy(
        device_a,
        host_a.data(),
        bytes,
        cudaMemcpyHostToDevice
    ));

    CUDA_CHECK(cudaMemcpy(
        device_b,
        host_b.data(),
        bytes,
        cudaMemcpyHostToDevice
    ));

    constexpr int threads = 256;
    const int blocks = (size + threads - 1) / threads;

    vector_add<<<blocks, threads>>>(
        device_a,
        device_b,
        device_result,
        size
    );

    CUDA_CHECK(cudaGetLastError());
    CUDA_CHECK(cudaDeviceSynchronize());

    CUDA_CHECK(cudaMemcpy(
        host_result.data(),
        device_result,
        bytes,
        cudaMemcpyDeviceToHost
    ));

    std::cout << "Result[0]: " << host_result[0] << '\n';
    std::cout << "Result[last]: " << host_result.back() << '\n';

    CUDA_CHECK(cudaFree(device_a));
    CUDA_CHECK(cudaFree(device_b));
    CUDA_CHECK(cudaFree(device_result));

    return 0;
}

编译:

nvcc vector_add.cu -o vector_add

运行:

./vector_add

结果应为:

Result[0]: 3
Result[last]: 3

这时你亲自走完了:

CUDA C++ 源代码
    ↓
nvcc 编译
    ↓
CPU Host Code + GPU Kernel
    ↓
CUDA Runtime
    ↓
NVIDIA Driver
    ↓
GPU SM 执行线程

七、尝试 GPU Docker 容器

NVIDIA Container Toolkit 负责让 Docker 或其他容器运行时把 GPU 设备及相关库暴露给容器。(NVIDIA Docs)

在 Windows + WSL2 环境下,比较简单的方式是:

  1. 安装 Docker Desktop。

  2. 启用 WSL2 后端。

  3. 打开 Ubuntu 的 WSL Integration。

  4. 在 Ubuntu 中执行 Docker 命令。

先检查:

docker version

然后运行一个 CUDA 容器:

docker run --rm --gpus all \
  nvidia/cuda:12.9.1-base-ubuntu24.04 \
  nvidia-smi

容器标签会随时间变化;若这个标签不存在,请换成 NVIDIA CUDA 镜像仓库当前存在的 base-ubuntu 标签。

正常时,容器里也能看到显卡:

Docker Container
    ↓
NVIDIA Container Runtime
    ↓
WSL GPU 接口
    ↓
Windows NVIDIA Driver
    ↓
GPU

进一步运行 PyTorch 容器:

docker run --rm -it --gpus all \
  pytorch/pytorch:latest \
  python -c "import torch; print(torch.cuda.is_available()); print(torch.cuda.get_device_name(0))"

学习容器时重点观察:

docker run --rm --gpus all ...

其中:

--gpus all

表示将可用 GPU 暴露给这个容器。


八、尝试 DCGM Exporter

DCGM Exporter 通过 HTTP /metrics 暴露 GPU 指标,Prometheus 可以直接抓取这些指标。(NVIDIA Docs)

可以先尝试容器:

docker run --rm --gpus all \
  --cap-add SYS_ADMIN \
  -p 9400:9400 \
  nvcr.io/nvidia/k8s/dcgm-exporter:latest

另开一个终端:

curl http://localhost:9400/metrics

可能看到:

DCGM_FI_DEV_GPU_UTIL
DCGM_FI_DEV_FB_USED
DCGM_FI_DEV_FB_FREE
DCGM_FI_DEV_GPU_TEMP
DCGM_FI_DEV_POWER_USAGE

不过需要注意:

  • DCGM 的核心用途是数据中心 GPU 管理。

  • GeForce 支持的指标和诊断能力可能少于数据中心 GPU。

  • WSL2 也存在功能限制。

  • 某些容器权限和性能指标可能不可用。

DCGM 官方支持部分非数据中心 NVIDIA GPU,但不同产品可用功能并不完全相同。(NVIDIA Docs)

因此,笔记本上 DCGM Exporter 跑不通时,不要把它理解成显卡损坏。先用以下组合学习监控即可:

nvidia-smi
NVML Python
Prometheus 自定义 Exporter
Grafana

九、自己写一个 Prometheus GPU Exporter

这比直接部署 DCGM Exporter更适合作为第一个 AIOps 项目。

安装:

pip install prometheus-client nvidia-ml-py

创建:

nano nvml_exporter.py

写入:

import time

from prometheus_client import Gauge, start_http_server
from pynvml import (
    NVMLError,
    NVML_TEMPERATURE_GPU,
    nvmlDeviceGetCount,
    nvmlDeviceGetHandleByIndex,
    nvmlDeviceGetMemoryInfo,
    nvmlDeviceGetName,
    nvmlDeviceGetTemperature,
    nvmlDeviceGetUtilizationRates,
    nvmlInit,
    nvmlShutdown,
)

GPU_UTILIZATION = Gauge(
    "nvidia_gpu_utilization_percent",
    "GPU utilization percentage",
    ["gpu", "name"],
)

GPU_MEMORY_USED = Gauge(
    "nvidia_gpu_memory_used_bytes",
    "GPU framebuffer memory currently used",
    ["gpu", "name"],
)

GPU_MEMORY_TOTAL = Gauge(
    "nvidia_gpu_memory_total_bytes",
    "Total GPU framebuffer memory",
    ["gpu", "name"],
)

GPU_TEMPERATURE = Gauge(
    "nvidia_gpu_temperature_celsius",
    "GPU temperature in Celsius",
    ["gpu", "name"],
)

def collect_metrics() -> None:
    count = nvmlDeviceGetCount()

    for index in range(count):
        handle = nvmlDeviceGetHandleByIndex(index)
        name = nvmlDeviceGetName(handle)
        labels = {
            "gpu": str(index),
            "name": name,
        }

        utilization = nvmlDeviceGetUtilizationRates(handle)
        memory = nvmlDeviceGetMemoryInfo(handle)
        temperature = nvmlDeviceGetTemperature(
            handle,
            NVML_TEMPERATURE_GPU,
        )

        GPU_UTILIZATION.labels(**labels).set(utilization.gpu)
        GPU_MEMORY_USED.labels(**labels).set(memory.used)
        GPU_MEMORY_TOTAL.labels(**labels).set(memory.total)
        GPU_TEMPERATURE.labels(**labels).set(temperature)

def main() -> None:
    try:
        nvmlInit()
        start_http_server(9401)
        print("NVML exporter listening on http://localhost:9401/metrics")

        while True:
            collect_metrics()
            time.sleep(2)

    except KeyboardInterrupt:
        print("\nExporter stopped.")
    except NVMLError as exc:
        print(f"NVML error: {exc}")
    finally:
        try:
            nvmlShutdown()
        except NVMLError:
            pass

if __name__ == "__main__":
    main()

运行:

python nvml_exporter.py

查看:

curl http://localhost:9401/metrics

这样你就已经实现了一个迷你版:

NVML
  ↓
Python Exporter
  ↓
HTTP /metrics
  ↓
Prometheus
  ↓
Grafana

这个项目对学习 Python、GPU 运维和 Prometheus 很有价值。


十、尝试 Kubernetes GPU

笔记本上建议把 Kubernetes 放在最后,因为它是最容易被环境问题绊住的一层。

需要理解 Kubernetes 本身不会直接操作 GPU,而是通过厂商提供的 Device Plugin 将 GPU 注册为可调度资源。(Kubernetes)

理想链路是:

Kubernetes
    ↓
NVIDIA Device Plugin
    ↓
NVIDIA Container Toolkit
    ↓
NVIDIA Driver
    ↓
GPU

Pod 通常这样申请 GPU:

apiVersion: v1
kind: Pod
metadata:
  name: pytorch-gpu-test
spec:
  restartPolicy: Never
  containers:
    - name: pytorch
      image: pytorch/pytorch:latest
      command:
        - python
        - -c
        - |
          import torch
          print("CUDA available:", torch.cuda.is_available())
          if torch.cuda.is_available():
              print("GPU:", torch.cuda.get_device_name(0))
      resources:
        limits:
          nvidia.com/gpu: 1

保存为:

gpu-pod.yaml

部署:

kubectl apply -f gpu-pod.yaml
kubectl logs pytorch-gpu-test

查看节点是否注册 GPU:

kubectl describe node

查找:

Capacity:
  nvidia.com/gpu: 1

Allocatable:
  nvidia.com/gpu: 1

不过在 Windows + WSL2 + Docker Desktop Kubernetes 中,GPU Device Plugin 的兼容性和配置通常不如原生 Linux稳定。因此更推荐两种方式:

第一种:笔记本安装原生 Ubuntu,使用 k3s 或 kubeadm
第二种:先在 WSL2 学 Docker GPU,Kubernetes GPU 放到云服务器实验

NVIDIA GPU Operator 可以自动部署驱动、Container Toolkit、Device Plugin、DCGM Exporter 和 MIG Manager,但它更适合标准 Linux GPU 节点,而不是作为笔记本上的第一个实验。(NVIDIA Docs)


哪些功能可以在消费级笔记本上学习

技术笔记本可行性建议
nvidia-smi很高马上尝试
PyTorch CUDA很高第一阶段重点
NVML Python很高运维方向重点
CUDA C++高第二阶段学习
GPU Docker高容器方向重点
Prometheus + Grafana很高AIOps 项目重点
DCGM Exporter中等能跑就学,失败不必死磕
TensorRT中高推理阶段学习
Triton Server中高可以部署小模型
vLLM取决于显存小显存限制明显
Kubernetes Device Plugin中低原生 Linux 更合适
NCCL 多卡通信低单 GPU 无法完整实验
MIG不可行通常需要支持 MIG 的数据中心 GPU
NVLink/NVSwitch不可行需要服务器硬件
Fabric Manager不可行需要 NVSwitch 平台

推荐的学习顺序

不要一次安装所有东西。按下面六个实验推进:

实验 1:Windows 和 WSL 中运行 nvidia-smi
实验 2:PyTorch 完成 GPU 矩阵计算
实验 3:用 NVML 编写 GPU 监控脚本
实验 4:运行 CUDA 或 PyTorch GPU 容器
实验 5:自制 Exporter + Prometheus + Grafana
实验 6:在原生 Linux 或云服务器实验 Kubernetes GPU

当前最应该先完成前三项。完成后,你已经能够清楚理解:

应用如何使用 GPU
CUDA 与驱动是什么关系
监控程序如何读取 GPU 指标
显存、利用率、功耗和进程如何变化

而 MIG、NVLink、NVSwitch、Fabric Manager 和多机 NCCL 不适合在普通笔记本上强行模拟,后面租用一台数据中心 GPU 云服务器集中体验,会比折腾本地虚拟化有效得多。

Yanche Blog

记录云原生、Linux、数据库等技术领域的学习心得,以及日常生活的思考与感悟。

© 2026 Yanche Blog. All rights reserved.

Powered by Astro