基于DCGM和Prometheus的GPU监控方案

背景: 在早期的GPU监控中我们会使用一些NVML工具来对GPU卡的基本信息进行采集,并持久化到监控系统的数据存储层。因为我们知道,其实通过nvidia-smi这样的命令也是可以获取到GPU的基本信息的,但随着整个AI市场的发展和成熟,对于GPU的监控也越来越需要一套标准化的工具体系,也就是本篇文章讲的关于DCGM相关的监控解决方案。

DCGM(Data Center GPU Manager)即数据中心GPU管理器,是一套用于在集群环境中管理和监视Tesla™GPU的工具。

它包括主动健康监控,全面诊断,系统警报以及包括电源和时钟管理在内的治理策略。

它可以由系统管理员独立使用,并且可以轻松地集成到NVIDIA合作伙伴的集群管理,资源调度和监视产品中。

DCGM简化了数据中心中的GPU管理,提高了资源可靠性和正常运行时间,自动化了管理任务,并有助于提高整体基础架构效率。

注意: 虽然可以通过nvidia-smi命令将相关的信息采集,并定期汇报到数据存储进行数据分析计算和展现,但是涉及到一整套的监控体系的整合,仍然需要使用方进行一些列的改造。因此这里,我们采用NVIDIA官方提供的DCGM方案来进行GPU数据采集,并通过声称下一代监控系统的Prometheus进行整个监控和告警的集成。

DCGM工具部署

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ git clone https://github.com/NVIDIA/gpu-monitoring-tools.git

# 构建dcgm-exporter工具,其实就是nvidia官方对于nvidia-docker2.x推出的用于gpu数据监控的工具
# 最终会将gpu卡的metrics基本信息存储以metrics的数据格式存储到文件中
$ cd dcgm-exporter
# nvidia/dcgm-exporter:latest
$ make 

$ docker run -d --runtime=nvidia --rm --name=nvidia-dcgm-exporter nvidia/dcgm-exporter

# 查看dcgm-exporter收集到的gpu metrics数据
$ docker exec -it nvidia-dcgm-exporter tail -n 10  /run/prometheus/dcgm.prom
dcgm_ecc_dbe_aggregate_total{gpu="0",uuid="GPU-b91e30ac-fe77-e236-11ea-078bc2d1f226"} 0
# HELP dcgm_retired_pages_sbe Total number of retired pages due to single-bit errors.
# TYPE dcgm_retired_pages_sbe counter
dcgm_retired_pages_sbe{gpu="0",uuid="GPU-b91e30ac-fe77-e236-11ea-078bc2d1f226"} 0
# HELP dcgm_retired_pages_dbe Total number of retired pages due to double-bit errors.
# TYPE dcgm_retired_pages_dbe counter
dcgm_retired_pages_dbe{gpu="0",uuid="GPU-b91e30ac-fe77-e236-11ea-078bc2d1f226"} 0
# HELP dcgm_retired_pages_pending Total number of pages pending retirement.
# TYPE dcgm_retired_pages_pending counter
dcgm_retired_pages_pending{gpu="0",uuid="GPU-b91e30ac-fe77-e236-11ea-078bc2d1f226"} 0

dcgm-exporter采集指标项以及含义:

指标 含义
dcgm_fan_speed_percent GPU 风扇转速占比(%)
dcgm_sm_clock GPU sm 时钟(MHz)
dcgm_memory_clock GPU 内存时钟(MHz)
dcgm_gpu_temp GPU 运行的温度(℃)
dcgm_power_usage GPU 的功率(w)
dcgm_pcie_tx_throughput GPU PCIe TX传输的字节总数 (kb)
dcgm_pcie_rx_throughput GPU PCIe RX接收的字节总数 (kb)
dcgm_pcie_replay_counter GPU PCIe重试的总数
dcgm_gpu_utilization GPU 利用率(%)
dcgm_mem_copy_utilization GPU 内存利用率(%)
dcgm_enc_utilization GPU 编码器利用率 (%)
dcgm_dec_utilization GPU 解码器利用率 (%)
dcgm_xid_errors GPU 上一个xid错误的值
dcgm_power_violation GPU 功率限制导致的节流持续时间(us)
dcgm_thermal_violation GPU 热约束节流持续时间(us)
dcgm_sync_boost_violation GPU 同步增强限制,限制持续时间(us)
dcgm_fb_free GPU fb(帧缓存)的剩余(MiB)
dcgm_fb_used GPU fb (帧缓存)的使用 (MiB)

其实到这,dcgm已经完整的将我们需要的gpu的metrics数据采集出来了,并且是符合prometheus的数据格式和标准的,此时,我们可以根据实际的情况编写一个简单的api程序,将采集到的数据以api的形式暴露出去,就可以让整个prometheus server对各个gpu主机的metrics进行采集和监控。

不过官方提供了基于kubernetes集群中pod方式的api接口,采用golang语言开发,具体使用情况可参考如下文档。

prometheus gpu metrics exporter

gpu-monitoring-tools项目中,默认提供了一个pod-gpu-metrics-exporter模块,用于在kubernetes集群中的gpu-metrics的部署,官方的示例步骤如下:

  • nvidia-k8s-device-plugin
  • Deploy GPU Pods

注意: 在使kubernetes集群中部署的前提是你的GPU要托管在k8s集群内部,这也就意味着你得先成功将带GPU的主机成功托管到集群中,并且能够调度GPU资源

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 创建一个监控的命名空间
# Create the monitoring namespace
$ kubectl create namespace monitoring

# Add gpu metrics endpoint to prometheus
$ kubectl create -f prometheus/prometheus-configmap.yaml

# Deploy prometheus
$ kubectl create -f prometheus/prometheus-deployment.yaml

$ kubectl create -f pod-gpu-metrics-exporter-daemonset.yaml

# Open in browser: localhost:9090

# 具体的docker镜像构建和运行
# 依然是gpu-monitoring-tools项目
$ cd  pod-gpu-metrics-exporter
$ docker build -t pod-gpu-metrics-exporter .

# 运行dcgm-exporter
$ docker run -d --runtime=nvidia --rm --name=nvidia-dcgm-exporter nvidia/dcgm-exporter

# 运行gpu-metrics-exporter
$ docker run -d --privileged --rm -p 9400:9400 -v /var/lib/kubelet/pod-resources:/var/lib/kubelet/pod-resources --volumes-from nvidia-dcgm-exporter:ro nvidia/pod-gpu-metrics-exporter:v1.0.0-alpha

# 此时就将上述的那个dcgm-exporter中采集到数据成功暴露到对外的接口了
$ curl -s localhost:9400/gpu/metrics

需要注意的是,在gpu-metrics-exporter的程序中,是针对pod的方式来采集gpu的metrics的信息,并且附带了pod本身的基本信息,因此如果你的gpu主机还未在kubernetes集群中托管,官方提供的镜像可能并不能使用,需要对src/http.go文件中采集的路径进行改变,将默认的gpuPodMetrics改成gpuMetrics即可,两者会去读取不同的dcgm-exporter暴露出来的metrics文件,否则访问api接口时会发现无法找到metrics文件.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
func getGPUmetrics(resp http.ResponseWriter, req *http.Request) {
	//metrics, err := ioutil.ReadFile(gpuPodMetrics)
	metrics, err := ioutil.ReadFile(gpuMetrics)
	if err != nil {
		http.Error(resp, err.Error(), http.StatusInternalServerError)
		glog.Errorf("error responding to %v%v: %v", req.Host, req.URL, err.Error())
		return
	}
	resp.Write(metrics)
}

参考gpu-metrics-exporter

图省事的,可以直接下载如下两个镜像,在已经work 的GPU主机上直接运行.

  • dcgm-exporter: docker pull bgbiao/dcgm-exporter:latest
  • gpu-metrics-exporter: docker pull bgbiao/gpu-metrics-exporter:latest

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    # 确定dcgm-exporter是运行的
    $ docker run -d --runtime=nvidia --rm --name=nvidia-dcgm-exporter bgbiao/dcgm-exporter
    
    $ docker run -d --privileged --rm -p 9400:9400  --volumes-from nvidia-dcgm-exporter:ro bgbiao/gpu-metrics-exporter
    
    # 检查gpu暴露出来的基础信息
    $ curl -s localhost:9400/gpu/metrics
    dcgm_ecc_dbe_aggregate_total{gpu="0",uuid="GPU-b91e30ac-fe77-e236-11ea-078bc2d1f226"} 0
    ....
    ....

prometheus采集

注意: 有了上述的gpu-metrics-exporter之后,我们的gpu相关的运行数据就可以以premetheus兼容的方式获取了,此时在prometheus-server上配置,去定期pull 数据即可,我们的prometheus-server目前部署在kubernetes集群内部,因此这里分享将gpu的数据采集到kubernetes集群内部的prometheus中。

创建endpoint以及对应的service

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# gpu-metrics的endpoint和service配置

$ cat endpoint-gpus.yaml
apiVersion: v1
kind: Endpoints
metadata:
  name: gpu-metrics
  namespace: monitoring
  labels:
    app: gpu-metrics
subsets:
- addresses:
  - ip: 172.16.65.234
  ports:
  - port: 9400
    name: http-metrics
    protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
  namespace: monitoring
  name: gpu-metrics
  labels:
    app: gpu-metrics
spec:
  ports:
  - name: http-metrics
    port: 19400
    targetPort: 9400
    protocol: TCP

$ kubectl  apply -f endpoint-gpus.yaml

# 查看创建的相关资源
$ kubectl  get ep,svc -n monitoring  -l app=gpu-metrics
NAME                    ENDPOINTS            AGE
endpoints/gpu-metrics   172.16.65.234:9400   5m24s

NAME                  TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)     AGE
service/gpu-metrics   ClusterIP   10.253.138.97   <none>        19400/TCP   5m24s

# 测试service暴露的端点
# 确保集群内部可以访问service暴露出来的endpoint即可

$ curl 10.253.138.97:19400/gpu/metrics
# HELP dcgm_sm_clock SM clock frequency (in MHz).
# TYPE dcgm_sm_clock gauge
dcgm_sm_clock{gpu="0",uuid="GPU-b91e30ac-fe77-e236-11ea-078bc2d1f226"} 1328
# HELP dcgm_memory_clock Memory clock frequency (in MHz).
# TYPE dcgm_memory_clock gauge
dcgm_memory_clock{gpu="0",uuid="GPU-b91e30ac-fe77-e236-11ea-078bc2d1f226"} 715

创建prometheus抓取数据的规则

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
$ cat prometheus-gpus.yml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  labels:
    app: gpu-metrics
  name: gpu-metrics
  namespace: monitoring
spec:
  # 对应的端点是上面创建的svc的ports
  endpoints:
    # 定义endpoint采集的时间和采集的URI
  - interval: 30s
    port: http-metrics
    path: /gpu/metrics
  jobLabel: app
  # 匹配monitoring命名空间的app=gpu-metrics的svc
  namespaceSelector:
    matchNames:
    - monitoring
  selector:
    matchLabels:
      app: gpu-metrics

$ kubectl  apply -f prometheus-gpus.yml
servicemonitor.monitoring.coreos.com/gpu-metrics created

$ kubectl  get servicemonitor  -n monitoring gpu-metrics
NAME          AGE
gpu-metrics   69s

当上述资源创建完成后,在集群内部的prometheus-server中就可以找到对应的target,确认状态为up即表示prometheus已正常采集集群外gpu的metrics数据了,接下来数据就会以30s为间隔,源源不断的将数据采集到prometheus存储中.

prometheus-gpu-targets

gpu监控信息的展示

到这里,我们已经到万里长征的最后一步了,就是把prometheus中gpu的监控数据用grafana展示出来,以实时去分析一些gpu的基本数据。

在grafana官网中,已经有大佬制作了gpu监控的相关模板,比如[GPU-Nodes-Metrics](https://grafana.com/grafana/dashboards/12027),因此,对于我们使用者来说,在grafana的面板中,将该模板导入即可使用。

选择导入方式创建

指定模板(dashboard id或json)

注意:确认prometheus库正确后即可导入

最终的GPU监控图

参考项目

gpu-monitor-tools

gpu-metrics-grafana

知识星球

公众号