人工智能/深度学习

使用 DCGM 监控 Kubernetes 中的 GPU

对于基础设施或站点可靠性工程( SRE )团队来说,监控多个 GPU 对于管理大型 GPU 集群以实现 AI 或 HPC 工作负载至关重要。 GPU 指标允许团队了解工作负载行为,从而优化资源分配和利用率,诊断异常,并提高数据中心的整体效率。除了基础设施团队之外,无论您是从事 GPU – 加速 ML 工作流的研究人员,还是喜欢了解 GPU 利用率和容量规划饱和的数据中心设计师,您应该都对指标感兴趣。

这些趋势变得更为重要,因为 AI / ML 工作负载通过使用 Kubernetes 之类的容器管理平台进行容器化和扩展。在这篇文章中,我们将概述 NVIDIA 数据中心 GPU 经理( DCGM ),以及如何将其集成到诸如 Prometheus 和 Grafana 这样的开源工具中,从而为 Kubernetes 构建一个 GPU 监控解决方案。

NVIDIA DCGM 公司

NVIDIA DCGM 公司 是一组用于在基于 Linux 的大规模集群环境中管理和监视 NVIDIA GPUs 的工具。它是一个低开销的工具,可以执行各种功能,包括主动健康监视、诊断、系统验证、策略、电源和时钟管理、组配置和记帐。有关详细信息,请参阅 DCGM 用户指南

DCGM 包括用于收集 GPU 遥测的 API。特别感兴趣的是 GPU 利用率指标(用于监视张量核心、 FP64 单元等)、内存指标和互连流量指标。 DCGM 为各种语言(如 C 和 Python )提供了绑定,这些都包含在安装程序包中。为了与 Go 作为编程语言流行的容器生态系统集成,有基于 DCGM API 的 Go 绑定 。存储库包括示例和 restapi ,以演示如何使用 Go API 监视 GPUs 。去看看 NVIDIA/gpu-monitoring-tools repo

导出

监控堆栈通常由一个收集器、一个存储度量的时间序列数据库和一个可视化层组成。一个流行的开源堆栈是 Prometheus ,与 Grafana 一起作为可视化工具来创建丰富的仪表板。普罗米修斯还包括 Alertmanager 来创建和管理警报。 Prometheus 与 kube-state-metricsnode_exporter 一起部署,以公开 kubernetesapi 对象的集群级指标和节点级指标,如 CPU 利用率。图 1 显示了普罗米修斯的一个架构示例。

Image showing the various components of a Prometheus + Grafana architecture for gathering telemetry, including the server, Alertmanager, and UI components.
图 1 。参考普罗米修斯建筑。资料来源: 普罗米修斯文献 .

在前面描述的 Go API 的基础上,可以使用 DCGM 向 Prometheus 公开 GPU 度量。为此,我们建立了一个名为 dcgm-exporter 的项目。

dcgm-exporter 使用 Go 绑定 从 DCGM 收集 GPU 遥测数据,然后为 Prometheus 公开指标以使用 http 端点(/metrics)进行提取

dcgm-exporter 也是可配置的。您可以使用 .csv 格式的输入配置文件,自定义 DCGM 要收集的 GPU 指标。

Kubernetes 集群中的每个 pod GPU 指标

dcgm-exporter 收集节点上所有可用 GPUs 的指标。然而,在 Kubernetes 中,当一个 pod 请求 GPU 资源时,您不一定知道节点中的哪个 GPUs 将被分配给一个 pod 。从 v1 . 13 开始, kubelet 添加了一个设备监视功能,可以使用 pod 资源套接字找到分配给 pod 的 pod 名称、 pod 名称空间和设备 ID 的设备。

dcgm-exporter 中的 http 服务器连接到 kubelet pod resources 服务器( /var/lib/kubelet/pod-resources ),以标识在 pod 上运行的 GPU 设备,并将 GPU 设备 pod 信息附加到收集的度量中。

Image showing the architecture of dcgm-exporter for gathering telemetry with Prometheus with the node-exporter, dcgm-exporter components, and service monitor components.
图 2 。 GPU 在 Kubernetes 使用 dcgm exporter 进行遥测。

设置 GPU 监控解决方案

下面是一些设置 dcgm-exporter 的示例。如果您使用的是 NVIDIA GPU 操作员 ,那么 dcgm-exporter 是作为操作员的一部分部署的组件之一。

文档包括 建立 Kubernetes 集群 的步骤。为了简洁起见,请快速前进到使用 NVIDIA 软件组件运行 Kubernetes 集群的步骤,例如,驱动程序、容器运行时和 Kubernetes 设备插件。您可以使用 Prometheus Operator 来部署 Prometheus ,它还可以方便地部署 Grafana 仪表板。在本文中,为了简单起见,您将使用单节点 Kubernetes 集群。

设置 普罗米修斯运营商目前提供的社区头盔图 时,必须遵循 将 GPU 遥测技术集成到 Kubernetes 中 中的步骤。必须为外部访问公开 Grafana ,并且必须将 prometheusSpec.serviceMonitorSelectorNilUsesHelmValues 设置为 false 。

简而言之,设置监视包括运行以下命令:

$ helm repo add prometheus-community \
https://prometheus-community.github.io/helm-charts

$ helm repo update
$ helm inspect values prometheus-community/kube-prometheus-stack > /tmp/kube-prometheus-stack.values
# Edit /tmp/kube-prometheus-stack.values in your favorite editor
# according to the documentation
# This exposes the service via NodePort so that Prometheus/Grafana
# are accessible outside the cluster with a browser
$ helm install prometheus-community/kube-prometheus-stack \
--create-namespace --namespace prometheus \
--generate-name \
--set prometheus.service.type=NodePort \
--set prometheus.prometheusSpec.serviceMonitorSelectorNilUsesHelmValues=false

此时,您的集群应该看起来像下面这样,其中所有的 Prometheus pods 和服务都在运行:

$ kubectl get pods -A
NAMESPACE     NAME                                                              READY   STATUS    RESTARTS   AGE
kube-system   calico-kube-controllers-8f59968d4-zrsdt                           1/1     Running   0          18m
kube-system   calico-node-c257f                                                 1/1     Running   0          18m
kube-system   coredns-f9fd979d6-c52hz                                           1/1     Running   0          19m
kube-system   coredns-f9fd979d6-ncbdp                                           1/1     Running   0          19m
kube-system   etcd-ip-172-31-27-93                                              1/1     Running   1          19m
kube-system   kube-apiserver-ip-172-31-27-93                                    1/1     Running   1          19m
kube-system   kube-controller-manager-ip-172-31-27-93                           1/1     Running   1          19m
kube-system   kube-proxy-b9szp                                                  1/1     Running   1          19m
kube-system   kube-scheduler-ip-172-31-27-93                                    1/1     Running   1          19m
kube-system   nvidia-device-plugin-1602308324-jg842                             1/1     Running   0          17m
prometheus    alertmanager-kube-prometheus-stack-1602-alertmanager-0            2/2     Running   0          92s
prometheus    kube-prometheus-stack-1602-operator-c4bc5c4d5-f5vzc               2/2     Running   0          98s
prometheus    kube-prometheus-stack-1602309230-grafana-6b4fc97f8f-66kdv         2/2     Running   0          98s
prometheus    kube-prometheus-stack-1602309230-kube-state-metrics-76887bqzv2b   1/1     Running   0          98s
prometheus    kube-prometheus-stack-1602309230-prometheus-node-exporter-rrk9l   1/1     Running   0          98s
prometheus    prometheus-kube-prometheus-stack-1602-prometheus-0                3/3     Running   1          92s
 
 
$ kubectl get svc -A
NAMESPACE     NAME                                                        TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                        AGE
default       kubernetes                                                  ClusterIP   10.96.0.1        <none>        443/TCP                        20m
kube-system   kube-dns                                                    ClusterIP   10.96.0.10       <none>        53/UDP,53/TCP,9153/TCP         20m
kube-system   kube-prometheus-stack-1602-coredns                          ClusterIP   None             <none>        9153/TCP                       2m18s
kube-system   kube-prometheus-stack-1602-kube-controller-manager          ClusterIP   None             <none>        10252/TCP                      2m18s
kube-system   kube-prometheus-stack-1602-kube-etcd                        ClusterIP   None             <none>        2379/TCP                       2m18s
kube-system   kube-prometheus-stack-1602-kube-proxy                       ClusterIP   None             <none>        10249/TCP                      2m18s
kube-system   kube-prometheus-stack-1602-kube-scheduler                   ClusterIP   None             <none>        10251/TCP                      2m18s
kube-system   kube-prometheus-stack-1602-kubelet                          ClusterIP   None             <none>        10250/TCP,10255/TCP,4194/TCP   2m12s
prometheus    alertmanager-operated                                       ClusterIP   None             <none>        9093/TCP,9094/TCP,9094/UDP     2m12s
prometheus    kube-prometheus-stack-1602-alertmanager                     ClusterIP   10.104.106.174   <none>        9093/TCP                       2m18s
prometheus    kube-prometheus-stack-1602-operator                         ClusterIP   10.98.165.148    <none>        8080/TCP,443/TCP               2m18s
prometheus    kube-prometheus-stack-1602-prometheus                       NodePort    10.105.3.19      <none>        9090:30090/TCP                 2m18s
prometheus    kube-prometheus-stack-1602309230-grafana                    ClusterIP   10.100.178.41    <none>        80/TCP                         2m18s
prometheus    kube-prometheus-stack-1602309230-kube-state-metrics         ClusterIP   10.100.119.13    <none>        8080/TCP                       2m18s
prometheus    kube-prometheus-stack-1602309230-prometheus-node-exporter   ClusterIP   10.100.56.74     <none>        9100/TCP                       2m18s
prometheus    prometheus-operated                                         ClusterIP   None             <none>        9090/TCP                       2m12s

安装 DCGM-Exporter

下面是如何开始安装 dcgm-exporter 来监视 GPU 的性能和利用率。您可以使用头盔图表来设置 dcgm-exporter 。首先,添加 Helm 回购:

$ helm repo add gpu-helm-charts \
https://nvidia.github.io/gpu-monitoring-tools/helm-charts
$ helm repo update

然后,使用 Helm 安装图表:

$ helm install \
   --generate-name \
   gpu-helm-charts/dcgm-exporter

可以使用以下命令观察展开:

$ helm ls
NAME                            NAMESPACE       REVISION        APP VERSION
dcgm-exporter-1-1601677302      default         1               dcgm-exporter-1.1.0             2.0.10
nvidia-device-plugin-1601662841 default         1          nvidia-device-plugin-0.7.0      0.7.0

普罗米修斯和格拉法纳的服务应公开如下:

$ kubectl get svc -A
NAMESPACE     NAME                                                      TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                        AGE
default       dcgm-exporter                                             ClusterIP   10.99.34.128     <none>        9400/TCP                       43d
default       kubernetes                                                  ClusterIP   10.96.0.1        <none>        443/TCP                        20m
kube-system   kube-dns                                                    ClusterIP   10.96.0.10       <none>        53/UDP,53/TCP,9153/TCP         20m
kube-system   kube-prometheus-stack-1602-coredns                          ClusterIP   None             <none>        9153/TCP                       2m18s
kube-system   kube-prometheus-stack-1602-kube-controller-manager          ClusterIP   None             <none>        10252/TCP                      2m18s
kube-system   kube-prometheus-stack-1602-kube-etcd                        ClusterIP   None             <none>        2379/TCP                       2m18s
kube-system   kube-prometheus-stack-1602-kube-proxy                       ClusterIP   None             <none>        10249/TCP                      2m18s
kube-system   kube-prometheus-stack-1602-kube-scheduler                   ClusterIP   None             <none>        10251/TCP                      2m18s
kube-system   kube-prometheus-stack-1602-kubelet                          ClusterIP   None             <none>        10250/TCP,10255/TCP,4194/TCP   2m12s
prometheus    alertmanager-operated                                       ClusterIP   None             <none>        9093/TCP,9094/TCP,9094/UDP     2m12s
prometheus    kube-prometheus-stack-1602-alertmanager                     ClusterIP   10.104.106.174   <none>        9093/TCP                       2m18s
prometheus    kube-prometheus-stack-1602-operator                         ClusterIP   10.98.165.148    <none>        8080/TCP,443/TCP               2m18s
prometheus    kube-prometheus-stack-1602-prometheus                       NodePort    10.105.3.19      <none>        9090:30090/TCP                 2m18s
prometheus    kube-prometheus-stack-1602309230-grafana                    ClusterIP   10.100.178.41    <none>        80:32032/TCP                   2m18s
prometheus    kube-prometheus-stack-1602309230-kube-state-metrics         ClusterIP   10.100.119.13    <none>        8080/TCP                       2m18s
prometheus    kube-prometheus-stack-1602309230-prometheus-node-exporter   ClusterIP   10.100.56.74     <none>        9100/TCP                       2m18s
prometheus    prometheus-operated                                         ClusterIP   None             <none>        9090/TCP                       2m12s

使用在端口 32032 公开的 Grafana 服务,访问 Grafana 主页。使用普罗米修斯图表中可用的凭据登录到仪表板: prometheus.values 中的 adminPassword 字段。

现在要为 GPU 度量启动 Grafana 仪表板,请从 数一数仪表板。 导入引用的 NVIDIA 仪表板。

使用 DCGM 仪表板

Image showing the GPU metrics dashboard in Grafana and various GPU metrics charts and gauges.
图 3 。参考 NVIDIA Grafana dashboard 。

现在运行一些 GPU 工作负载。为此, DCGM 包括一个名为 dcgmproftester 的 CUDA 负载生成器。它可以用来生成确定性的 CUDA 工作负载,用于读取和验证 GPU 度量。我们有一个集装箱化的 dcgmproftester ,可以在 Docker 命令行上运行。这个例子生成一个半精度( FP16 )矩阵乘法( GEMM ),并使用 GPU 上的张量核。

产生负载

要生成负载,必须首先下载 DCGM 并将其容器化。下面的脚本创建一个可用于运行 dcgmproftester 的容器。此容器可在 NVIDIA DockerHub 存储库中找到。

#!/usr/bin/env bash
set -exo pipefail
 
mkdir -p /tmp/dcgm-docker
pushd /tmp/dcgm-docker
 
cat > Dockerfile <<EOF
ARG BASE_DIST
ARG CUDA_VER
FROM nvidia/cuda:\${CUDA_VER}-base-\${BASE_DIST}
LABEL io.k8s.display-name="NVIDIA dcgmproftester"
 
ARG DCGM_VERSION
 
WORKDIR /dcgm
RUN apt-get update && apt-get install -y --no-install-recommends \
    libgomp1 \
    wget && \
    rm -rf /var/lib/apt/lists/* && \
    wget --no-check-certificate https://developer.download.nvidia.com/compute/redist/dcgm/\${DCGM_VERSION}/DEBS/datacenter-gpu-manager_\${DCGM_VERSION}_amd64.deb && \
    dpkg -i datacenter-gpu-manager_*.deb && \
    rm -f datacenter-gpu-manager_*.deb
 
ENTRYPOINT ["/usr/bin/dcgmproftester11"]
EOF
 
DIR=.
DCGM_REL_VERSION=2.0.10
BASE_DIST=ubuntu18.04
CUDA_VER=11.0
IMAGE_NAME=nvidia/samples:dcgmproftester-$DCGM_REL_VERSION-cuda$CUDA_VER-$BASE_DIST
 
 
docker build --pull \
        -t "$IMAGE_NAME" \
        --build-arg DCGM_VERSION=$DCGM_REL_VERSION \
        --build-arg BASE_DIST=$BASE_DIST \
        --build-arg CUDA_VER=$CUDA_VER \
        --file Dockerfile \
        "$DIR"
 
popd

在将容器部署到 Kubernetes 集群之前,请尝试在 Docker 中运行它。在本例中,通过指定 -t 1004 ,使用张量核心触发 FP16 矩阵乘法,并运行测试 -d 45 ( 45 秒)。您可以通过修改 -t 参数来尝试运行其他工作负载。

$ docker run --rm --gpus all --cap-add=SYS_ADMIN nvidia/samples:dcgmproftester-2.0.10-cuda11.0-ubuntu18.04 --no-dcgm-validation -t 1004 -d 45
 
Skipping CreateDcgmGroups() since DCGM validation is disabled
CU_DEVICE_ATTRIBUTE_MAX_THREADS_PER_MULTIPROCESSOR: 1024
CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT: 40
CU_DEVICE_ATTRIBUTE_MAX_SHARED_MEMORY_PER_MULTIPROCESSOR: 65536
CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR: 7
CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR: 5
CU_DEVICE_ATTRIBUTE_GLOBAL_MEMORY_BUS_WIDTH: 256
CU_DEVICE_ATTRIBUTE_MEMORY_CLOCK_RATE: 5001000
Max Memory bandwidth: 320064000000 bytes (320.06 GiB)
CudaInit completed successfully.
 
Skipping WatchFields() since DCGM validation is disabled
TensorEngineActive: generated ???, dcgm 0.000 (27605.2 gflops)
TensorEngineActive: generated ???, dcgm 0.000 (28697.6 gflops)
TensorEngineActive: generated ???, dcgm 0.000 (28432.8 gflops)
TensorEngineActive: generated ???, dcgm 0.000 (28585.4 gflops)
TensorEngineActive: generated ???, dcgm 0.000 (28362.9 gflops)
TensorEngineActive: generated ???, dcgm 0.000 (28361.6 gflops)
TensorEngineActive: generated ???, dcgm 0.000 (28448.9 gflops)
TensorEngineActive: generated ???, dcgm 0.000 (28311.0 gflops)
TensorEngineActive: generated ???, dcgm 0.000 (28210.8 gflops)
TensorEngineActive: generated ???, dcgm 0.000 (28304.8 gflops)

将此计划到您的 Kubernetes 集群上,并在 Grafana 仪表板中查看适当的度量。下面的代码示例使用容器的适当参数构造此 podspec :

cat << EOF | kubectl create -f -
 apiVersion: v1
 kind: Pod
 metadata:
   name: dcgmproftester
 spec:
   restartPolicy: OnFailure
   containers:
   - name: dcgmproftester11
     image: nvidia/samples:dcgmproftester-2.0.10-cuda11.0-ubuntu18.04
     args: ["--no-dcgm-validation", "-t 1004", "-d 120"]
     resources:
       limits:
          nvidia.com/gpu: 1
     securityContext:
       capabilities:
          add: ["SYS_ADMIN"]
 
EOF

您可以看到 dcgmproftester pod 正在运行,然后在 Grafana 仪表板上显示度量。 GPU 利用率( GrActive )已达到 98% 的峰值。您还可能会发现其他指标很有趣,比如 power 或 GPU 内存。

$ kubectl get pods -A
NAMESPACE     NAME                                                              READY   STATUS    RESTARTS   AGE
...
default       dcgmproftester                                                    1/1     Running   0          6s
...
Image showing graphs of GPU utilization plotted on the Grafana dashboard across time when running CUDA workloads.
图 4 。 GPU 运行 dcgmproftest 时在 Grafana 中的利用率。

验证 GPU 指标

DCGM 最近增加了一些设备级指标。其中包括细粒度的 GPU 利用率指标,可以监视 SM 占用率和 Tensor 核心利用率。有关详细信息,请参阅 DCGM 用户指南 中的 分析指标 。为了方便起见,当您使用 Helm 图表部署 dcgm-exporter 时,它被配置为默认收集这些度量。

图 5 显示了在 Prometheus 仪表板中验证 dcgm-exporter 提供的评测指标。

Image showing the profiling metrics served by dcgm-exporter in the Prometheus dashboard.
图 5 。 GPU 在普罗米修斯仪表板中分析 DCGM 的指标。

您可以定制 Grafana 仪表板以包含 DCGM 的其他指标。在本例中,通过编辑 repo 上可用的 JSON 伯爵夫人 文件,将 Tensor 核心利用率添加到仪表板中。你也可以使用网页界面。请随意修改仪表板。

此仪表板包括 Tensor 核心利用率。您可以进一步自定义它。重新启动 dcgmproftester 容器后,您可以看到 T4 上的张量核心已达到约 87% 的利用率:

Image showing the utilization chart of Tensor Cores plotted across time in the Grafana dashboard.
图 6 。 Grafana 的张量核心利用率(百分比)。

请随意修改 JSON 仪表板,以包含 DCGM 支持的其他 GPU 指标。支持的 GPU 指标在 DCGM DCGM API Documentation 中可用。通过使用 GPU 指标作为定制指标和普罗米修斯适配器,您可以使用 卧式吊舱自动标度机 根据 GPU 利用率或其他指标来缩放吊舱的数量。

概要

要从 dcgm-exporter 开始,并将监视解决方案放在 Kubernetes 上,无论是在前提还是在云中,请参阅 将 GPU 遥测技术集成到 Kubernetes 中 ,或将其部署为 NVIDIA GPU 操作员 的一部分。官方的 GitHub 代码库是 NVIDIA/gpu-monitoring-tools ,我们很乐意听到您的反馈!可以在 NVIDIA/gpu-monitoring-tools/issues 处自由地文件问题或新功能请求。

 

标签