一、背景说明

在 Kubernetes 中使用 NVIDIA GPU,不能只依赖 Kubernetes 自身能力。Kubernetes 通过 Device Plugin 机制来识别和调度 GPU 这类扩展资源。节点上安装对应厂商的 GPU 驱动和 Device Plugin 后,集群里会暴露出类似 nvidia.com/gpu 这样的可调度资源,Pod 通过 resources.limits.nvidia.com/gpu: 1 来申请 GPU。Kubernetes 官方文档也说明,GPU 这类设备资源需要写在 limits 中;如果只写 limits 不写 requests,Kubernetes 会默认把 limit 当成 request;如果同时写 requestslimits,两者必须相等。

NVIDIA GPU Operator 的作用,就是把 Kubernetes 使用 NVIDIA GPU 需要的组件统一交给 Operator 管理,包括 NVIDIA Driver、NVIDIA Container Toolkit、NVIDIA Device Plugin、GPU Feature Discovery、DCGM Exporter 等。NVIDIA GPU Operator 项目说明中也提到,它用于自动管理 Kubernetes 中运行 NVIDIA GPU 所需的软件组件。

本文主要记录四部分内容:

  1. GPU Operator 的部署

  2. GPU Operator 部署后的简单测试

  3. Prometheus 通过 ServiceMonitor 接入 DCGM Exporter 指标

  4. Grafana 导入 NVIDIA DCGM Dashboard 展示 GPU 监控数据


二、环境说明

本文环境大致如下:

Kubernetes: v1.35.5
节点名称: master-01
GPU: NVIDIA RTX 3070 Ti
容器运行时: containerd
GPU Operator: v25.3.4
Prometheus: 通过 Prometheus Operator 部署
Grafana: 已部署

先在宿主机上确认 NVIDIA Driver 是否正常:

nvidia-smi

如果宿主机上已经可以正常执行 nvidia-smi,说明 NVIDIA Driver 已经安装好。本文的部署方式就是基于“宿主机已经提前安装好 NVIDIA Driver”的场景,所以后面部署 GPU Operator 时会设置:

driver:
  enabled: false

这个参数表示不让 GPU Operator 再去安装 NVIDIA Driver。NVIDIA 官方文档也说明,在节点已经预装 NVIDIA GPU Driver 的场景下,可以通过 driver.enabled=false 阻止 Operator 安装驱动。


三、部署 GPU Operator

1. 添加 NVIDIA Helm 仓库

helm repo add nvidia https://helm.ngc.nvidia.com/nvidia
helm repo update

GPU Operator 官方安装方式就是通过 Helm 部署 nvidia/gpu-operator 这个 chart。

2. 准备 values 文件

创建 gpu-operator-values.yaml

cat > gpu-operator-values.yaml <<'EOF'
driver:
  enabled: false

dcgmExporter:
  enabled: true
  config:
    create: false
    name: ""
EOF

这里几个参数说明一下。

driver:
  enabled: false

表示不由 GPU Operator 安装 NVIDIA Driver。这个适合宿主机已经安装好驱动的场景。如果宿主机没有安装驱动,就不能直接照抄这个参数,需要根据自己的系统版本、驱动版本、GPU Operator 版本来确认是否让 Operator 管理驱动。

dcgmExporter:
  enabled: true

表示启用 DCGM Exporter。DCGM Exporter 会采集 GPU 使用率、显存、温度、功耗等指标,并通过 HTTP /metrics 暴露给 Prometheus 抓取。NVIDIA DCGM Exporter 官方文档说明,dcgm-exporter 基于 NVIDIA DCGM,能够在 /metrics HTTP endpoint 暴露 GPU 指标,供 Prometheus 等监控系统采集。

dcgmExporter:
  config:
    create: false
    name: ""

这个配置主要是为了避免某些版本中 dcgmExporter.config 字段为空时 Helm 模板渲染异常。不同 GPU Operator 版本的 values 字段可能会有变化,部署前建议先看一下当前版本默认 values:

helm show values nvidia/gpu-operator --version v25.3.4 | less

3. 安装 GPU Operator

helm install gpu-operator nvidia/gpu-operator \
  -n gpu-operator \
  --create-namespace \
  --version v25.3.4 \
  -f gpu-operator-values.yaml \
  --wait \
  --timeout 10m

四、GPU Operator 部署验证与 GPU Pod 测试

1. 查看 GPU Operator 相关 Pod

kubectl -n gpu-operator get pod

正常情况下可以看到类似组件:

gpu-operator-xxxx
gpu-operator-node-feature-discovery-master-xxxx
gpu-operator-node-feature-discovery-worker-xxxx
gpu-feature-discovery-xxxx
nvidia-container-toolkit-daemonset-xxxx
nvidia-device-plugin-daemonset-xxxx
nvidia-dcgm-exporter-xxxx
nvidia-cuda-validator-xxxx

其中几个比较关键的组件如下:

组件 作用
gpu-operator GPU Operator 控制器本身
node-feature-discovery 发现节点硬件特征,并给节点打标签
nvidia-container-toolkit-daemonset 配置容器运行时,使容器可以使用 NVIDIA GPU
nvidia-device-plugin-daemonset 向 kubelet 注册 nvidia.com/gpu 扩展资源
gpu-feature-discovery 发现 GPU 型号、驱动、CUDA 等 GPU 信息
nvidia-dcgm-exporter 暴露 GPU 监控指标给 Prometheus
nvidia-cuda-validator 验证 CUDA/GPU 是否可用

2. 查看节点是否识别到 GPU 资源

kubectl describe node master-01 | grep -A10 -E "Capacity|Allocatable"

重点看有没有下面这种资源:

nvidia.com/gpu:  1

也可以直接过滤:

kubectl describe node master-01 | grep nvidia.com/gpu

如果能看到 nvidia.com/gpu,说明 NVIDIA Device Plugin 已经把 GPU 资源注册给 Kubernetes 了。后续 Pod 就可以通过下面这种方式申请 GPU:

resources:
  limits:
    nvidia.com/gpu: 1

3. 创建 GPU 测试 Pod

创建一个简单的测试 Pod,启动后直接执行 nvidia-smi

apiVersion: v1
kind: Pod
metadata:
  name: gpu-test
spec:
  restartPolicy: Never
  containers:
  - name: cuda-test
    image: docker.io/nvidia/cuda:12.0.0-base-ubuntu22.04
    command: ["nvidia-smi"]
    resources:
      limits:
        nvidia.com/gpu: 1

查看日志:

kubectl logs gpu-test

如果日志中能看到类似下面的输出,说明 Pod 内已经可以正常使用 GPU:

+---------------------------------------------------------------------------------------+
| NVIDIA-SMI ...                                                                        |
| Driver Version: ...                                                                   |
| CUDA Version: ...                                                                     |
+---------------------------------------------------------------------------------------+

测试完成后可以删除:

kubectl delete pod gpu-test

如果节点上已经识别到了 nvidia.com/gpu,但是 Pod 里执行 nvidia-smi 失败,需要重点检查 NVIDIA Container Toolkit、containerd runtime 配置、RuntimeClass/CDI 配置是否正常。


五、接入 Prometheus 并验证 DCGM Exporter 指标抓取

GPU Operator 部署完成后,会在 gpu-operator 命名空间中创建 DCGM Exporter 对应的 Service。Prometheus 后面就是通过这个 Service 去抓取 GPU 指标。

1. 查看 DCGM Exporter Service

kubectl -n gpu-operator get svc nvidia-dcgm-exporter

查看 Service 详细配置:

kubectl -n gpu-operator get svc nvidia-dcgm-exporter -o yaml

重点看 ports.name,一般是:

ports:
- name: gpu-metrics
  port: 9400
  targetPort: 9400

后面写 ServiceMonitor 的时候,endpoints.port 要写 Service 端口名称,也就是:

port: gpu-metrics

Prometheus Operator 的 ServiceMonitor 用来描述 Prometheus 应该如何抓取一组 Service。ServiceMonitor 会通过 label selector 选择 Service,再根据 endpoint 配置生成 Prometheus 抓取任务。Prometheus Operator 官方 API 文档中说明,ServiceMonitor CRD 用于定义 Prometheus 或 PrometheusAgent 如何从一组 Service 抓取指标,同时 Prometheus 会通过 label selector 和 namespace selector 选择 ServiceMonitor。

2. 先确认 DCGM Exporter 本身有指标

可以进入 nvidia-dcgm-exporter 容器内部测试:

kubectl -n gpu-operator exec -it <nvidia-dcgm-exporter-pod-name> -- bash

然后在容器里执行:

curl http://127.0.0.1:9400/metrics

如果能看到大量 DCGM_FI_DEV_ 开头的指标,说明 DCGM Exporter 本身正常,例如:

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_FI_DEV_SM_CLOCK
DCGM_FI_DEV_MEM_CLOCK

3. 创建 ServiceMonitor

我的 Prometheus 是通过 Prometheus Operator 部署的,所以这里不直接修改 prometheus.yml,而是创建 ServiceMonitor

创建 nvidia-dcgm-servicemonitor.yaml

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: nvidia-dcgm-exporter
  namespace: monitoring
  labels:
    app: nvidia-dcgm
spec:
  endpoints:
  - interval: 15s
    path: /metrics
    port: gpu-metrics
  namespaceSelector:
    matchNames:
    - gpu-operator
  selector:
    matchLabels:
      app: nvidia-dcgm-exporter

这里解释几个关键字段。

metadata.namespace: monitoring
metadata:
  name: nvidia-dcgm-exporter
  namespace: monitoring

这个表示 ServiceMonitor 对象创建在 monitoring 命名空间。

我的 Prometheus 也在 monitoring 命名空间,所以这样比较方便。如果你的 Prometheus 只发现当前命名空间下的 ServiceMonitor,那么 ServiceMonitor 就应该放在 Prometheus 能发现的命名空间。

可以通过下面命令查看 Prometheus 当前选择规则:

kubectl -n monitoring get prometheus -o yaml | grep -A20 -E "serviceMonitorSelector|serviceMonitorNamespaceSelector"

如果你的 Prometheus 配置了类似下面的 selector:

serviceMonitorSelector:
  matchLabels:
    release: kube-prometheus-stack

那 ServiceMonitor 也必须加上对应 label,例如:

metadata:
  labels:
    release: kube-prometheus-stack

否则 Prometheus 不会发现这个 ServiceMonitor。

namespaceSelector.matchNames: gpu-operator
spec:
  namespaceSelector:
    matchNames:
    - gpu-operator

这个表示让这个 ServiceMonitor 去 gpu-operator 命名空间找 Service。

也就是说:

metadata:
  namespace: monitoring

表示 ServiceMonitor 自己创建在哪里;

spec:
  namespaceSelector:
    matchNames:
    - gpu-operator

表示 ServiceMonitor 要去哪个命名空间发现被监控的 Service。

这两个不是一个概念。

selector.matchLabels
selector:
  matchLabels:
    app: nvidia-dcgm-exporter

这个 selector 必须能匹配到 gpu-operator 命名空间里的 nvidia-dcgm-exporter Service。

可以用下面命令确认 Service 的 label:

kubectl -n gpu-operator get svc nvidia-dcgm-exporter --show-labels

如果看到类似:

app=nvidia-dcgm-exporter

那么上面的 selector.matchLabels 就是对的。

endpoints.port: gpu-metrics
endpoints:
- interval: 15s
  path: /metrics
  port: gpu-metrics

这里的 port 必须对应 Service 里的端口名称。可以确认一下:

kubectl -n gpu-operator get svc nvidia-dcgm-exporter -o jsonpath='{.spec.ports[*].name}'

如果输出:

gpu-metrics

那 ServiceMonitor 里就写:

port: gpu-metrics

4. 在 Prometheus 页面验证

ServiceMonitor 创建后,稍等一会儿,打开 Prometheus 页面:

Status -> Targets

找到类似下面的 target:

serviceMonitor/monitoring/nvidia-dcgm-exporter/0


六、常见问题排查

1. Prometheus Targets 页面看不到 nvidia-dcgm-exporter

先确认 ServiceMonitor 是否创建成功:

kubectl -n monitoring get servicemonitor nvidia-dcgm-exporter

再确认 Prometheus 是否能选择这个 ServiceMonitor:

kubectl -n monitoring get prometheus -o yaml | grep -A20 serviceMonitorSelector

如果 Prometheus 的 serviceMonitorSelector 配置了 label 过滤,那么 ServiceMonitor 的 metadata.labels 必须匹配。

例如 Prometheus 只选择:

serviceMonitorSelector:
  matchLabels:
    release: kube-prometheus-stack

那 ServiceMonitor 也必须加:

metadata:
  labels:
    release: kube-prometheus-stack

否则 Prometheus 不会加载这个 ServiceMonitor。

2. Prometheus 日志中出现 Forbidden

如果 Prometheus 日志里有类似:

failed to list *v1.Pod: pods is forbidden
failed to list *v1.Endpoints: endpoints is forbidden

说明 Prometheus 的 ServiceAccount 没有权限读取 gpu-operator 命名空间的 Service、Endpoints、Pod 等资源。

先查看 Prometheus 使用的 ServiceAccount:

kubectl -n monitoring get prometheus -o yaml | grep serviceAccountName

假设 ServiceAccount 是:

prometheus-k8s

可以创建如下 RBAC:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: prometheus-read-gpu-operator
  namespace: gpu-operator
rules:
- apiGroups: [""]
  resources:
  - services
  - endpoints
  - pods
  verbs:
  - get
  - list
  - watch
- apiGroups: ["discovery.k8s.io"]
  resources:
  - endpointslices
  verbs:
  - get
  - list
  - watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: prometheus-read-gpu-operator
  namespace: gpu-operator
subjects:
- kind: ServiceAccount
  name: prometheus-k8s
  namespace: monitoring
roleRef:
  kind: Role
  name: prometheus-read-gpu-operator
  apiGroup: rbac.authorization.k8s.io

如果你的 Prometheus ServiceAccount 不是 prometheus-k8s,需要把上面 RoleBinding 中的 subjects.name 改成实际名称。


七、Grafana Dashboard 展示

Prometheus 已经能抓到 DCGM Exporter 指标后,就可以在 Grafana 里展示 GPU 监控面板。

Grafana 官方有现成的 NVIDIA DCGM Dashboard,例如 12219 这个 Dashboard,用于展示 Kubernetes 集群中由 DCGM Exporter 采集到的 GPU 指标。

导入步骤如下:

  1. 登录 Grafana

  2. 确认 Prometheus 数据源已经配置好

  3. 进入 Dashboards

  4. 点击 New

  5. 点击 Import

  6. 输入 Dashboard ID,例如:

12219

或者使用其他 NVIDIA DCGM 相关 Dashboard。

  1. 点击 Load

  2. 选择 Prometheus 数据源

  3. 点击 Import

导入完成后,可以看到 GPU 相关监控面板,例如:

我这里导入的是 NVIDIA DCGM Exporter Dashboard,Grafana 页面中已经能看到 GPU 温度、功耗等面板,说明:

DCGM Exporter -> Prometheus -> Grafana

这条链路已经打通。

需要注意一点,不同 Dashboard 的 PromQL 写法、单位换算和聚合方式可能不完全适合所有显卡。如果某些功耗类面板出现明显不符合实际的数值,建议先回到 Prometheus 里直接查原始指标,例如:

DCGM_FI_DEV_POWER_USAGE

再结合宿主机上的 nvidia-smi 输出判断。如果 Prometheus 原始指标正常,只是 Grafana 面板显示异常,一般就是 Dashboard 的查询语句、单位或聚合方式需要调整。


八、整体链路总结

这套 GPU 监控链路可以理解为:

GPU
  ↓
NVIDIA Driver / DCGM
  ↓
DCGM Exporter
  ↓
Service: nvidia-dcgm-exporter
  ↓
ServiceMonitor
  ↓
Prometheus
  ↓
Grafana Dashboard

其中 GPU Operator 负责把 NVIDIA GPU 运行和监控所需的组件统一部署出来。部署完成后,Kubernetes 节点上会出现 nvidia.com/gpu 资源,业务 Pod 可以通过 resources.limits.nvidia.com/gpu: 1 申请 GPU。

DCGM Exporter 会暴露 GPU 监控指标,Prometheus 通过 ServiceMonitor 自动发现并抓取,最后 Grafana 导入现成的 Dashboard 做可视化展示。

本文最终验证结果如下:

Prometheus Targets:
serviceMonitor/monitoring/nvidia-dcgm-exporter/0  1/1 up

并且 Grafana 中已经可以看到 NVIDIA DCGM Exporter Dashboard 的 GPU 监控数据。到这里,GPU Operator 部署、GPU Pod 测试、Prometheus 指标接入、Grafana Dashboard 展示就都完成了。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐