在2022年2月,亚马逊网络服务(Amazon Web Services)在亚马逊云监控(Amazon CloudWatch)中增加了对NVIDIA GPU指标的支持,使得可以将指标从亚马逊云监控代理(Amazon CloudWatch Agent)推送到亚马逊云监控,并监视代码以实现最佳GPU利用率。此功能已集成到我们的许多托管Amazon机器映像(AMI)中,例如深度学习AMI和AWS ParallelCluster AMI。要获取GPU利用率的实例级指标,您可以使用Packer或亚马逊镜像构建器(Amazon ImageBuilder)来引导自己的自定义AMI,并在AWS Batch、亚马逊弹性容器服务(Amazon ECS)或亚马逊弹性Kubernetes服务(Amazon EKS)等各种托管服务提供中使用。然而,对于许多基于容器的服务提供和工作负载来说,最理想的是在容器、Pod或命名空间级别捕获利用率指标。
本文详细介绍了如何设置基于容器的GPU指标,并提供了从EKS Pod收集这些指标的示例。
解决方案概述
为了演示基于容器的GPU指标,我们创建了一个带有g5.2xlarge
实例的EKS集群;但是,这也适用于任何受支持的NVIDIA加速实例系列。
我们部署了NVIDIA GPU操作器以启用GPU资源的使用,并部署了NVIDIA DCGM Exporter以启用GPU指标的收集。然后我们探索了两种架构。第一种架构将NVIDIA DCGM Exporter的指标通过云监控代理连接到云监控,如下图所示。
第二种架构(见下图)将DCGM Exporter的指标连接到Prometheus,然后使用Grafana仪表板可视化这些指标。
先决条件
为了简化从本文中复制整个堆栈,我们使用一个已经安装了所有所需工具(aws cli、eksctl、helm等)的容器。为了从GitHub克隆容器项目,您需要git。为了构建和运行容器,您需要Docker。为了部署架构,您需要AWS凭证。为了使用端口转发访问Kubernetes服务,您还需要kubectl。
这些先决条件可以安装在您的本地机器、带有NICE DCV的EC2实例或AWS Cloud9上。在本文中,我们将使用一个带有c5.2xlarge
Cloud9实例和一个40GB
本地存储卷。使用Cloud9时,请通过访问Cloud9->Preferences->AWS Settings
来禁用AWS托管临时凭证,如下面的屏幕截图所示。
构建和运行aws-do-eks容器
在您首选的环境中打开终端shell,并运行以下命令:
git clone https://github.com/aws-samples/aws-do-eks
cd aws-do-eks
./build.sh
./run.sh
./exec.sh
结果如下:
root@e5ecb162812f:/eks#
现在您已经在一个具有完成以下任务所需的所有工具的容器环境中获得了一个shell。我们将称之为“aws-do-eks shell”。除非有特定的指示,否则您将在本文中的以下各节中运行命令。
创建一个带有节点组的EKS集群
该节点组包括您选择的GPU实例系列;在此示例中,我们使用g5.2xlarge
实例类型。
aws-do-eks项目附带了一系列的集群配置。您可以通过单个配置更改来设置所需的集群配置。
- 在容器shell中运行
./env-config.sh
,然后设置CONF=conf/eksctl/yaml/eks-gpu-g5.yaml
- 要验证集群配置,请运行
./eks-config.sh
您应该看到以下集群清单:
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
name: do-eks-yaml-g5
version: "1.25"
region: us-east-1
availabilityZones:
- us-east-1a
- us-east-1b
- us-east-1c
- us-east-1d
managedNodeGroups:
- name: sys
instanceType: m5.xlarge
desiredCapacity: 1
iam:
withAddonPolicies:
autoScaler: true
cloudWatch: true
- name: g5
instanceType: g5.2xlarge
instancePrefix: g5-2xl
privateNetworking: true
efaEnabled: false
minSize: 0
desiredCapacity: 1
maxSize: 10
volumeSize: 80
iam:
withAddonPolicies:
cloudWatch: true
iam:
withOIDC: true
- 要创建集群,请在容器中运行以下命令
./eks-create.sh
输出结果如下:
root@e5ecb162812f:/eks# ./eks-create.sh
/eks/impl/eksctl/yaml /eks
./eks-create.sh
Mon May 22 20:50:59 UTC 2023
使用/eks/conf/eksctl/yaml/eks-gpu-g5.yaml创建集群...
eksctl create cluster -f /eks/conf/eksctl/yaml/eks-gpu-g5.yaml
2023-05-22 20:50:59 [ℹ] eksctl版本0.133.0
2023-05-22 20:50:59 [ℹ] 使用区域us-east-1
2023-05-22 20:50:59 [ℹ] us-east-1a子网 - 公共:192.168.0.0/19 私有:192.168.128.0/19
2023-05-22 20:50:59 [ℹ] us-east-1b子网 - 公共:192.168.32.0/19 私有:192.168.160.0/19
2023-05-22 20:50:59 [ℹ] us-east-1c子网 - 公共:192.168.64.0/19 私有:192.168.192.0/19
2023-05-22 20:50:59 [ℹ] us-east-1d子网 - 公共:192.168.96.0/19 私有:192.168.224.0/19
2023-05-22 20:50:59 [ℹ] 节点组"sys"将使用""[AmazonLinux2/1.25]
2023-05-22 20:50:59 [ℹ] 节点组"g5"将使用""[AmazonLinux2/1.25]
2023-05-22 20:50:59 [ℹ] 使用Kubernetes版本1.25
2023-05-22 20:50:59 [ℹ] 在"us-east-1"区域使用托管节点创建EKS集群"do-eks-yaml-g5- 要验证您的集群是否成功创建,请运行以下命令
kubectl get nodes -L node.kubernetes.io/instance-type
输出类似于以下内容:
名称 状态 角色 年龄 版本 实例类型
ip-192-168-18-137.ec2.internal Ready <none> 47m v1.25.9-eks-0a21954 m5.xlarge
ip-192-168-214-241.ec2.internal Ready <none> 46m v1.25.9-eks-0a21954 g5.2xlarge
在此示例中,我们的集群中有一个 m5.xlarge 实例和一个 g5.2xlarge 实例;因此,我们在上述输出中看到两个节点。
在集群创建过程中,将安装NVIDIA设备插件。在集群创建后,您将需要将其删除,因为我们将使用NVIDIA GPU Operator。
- 使用以下命令删除插件
kubectl -n kube-system delete daemonset nvidia-device-plugin-daemonset
我们得到下面的输出:
已删除 daemonset.apps "nvidia-device-plugin-daemonset"
安装NVIDIA Helm仓库
使用以下命令安装NVIDIA Helm仓库:
helm repo add nvidia https://helm.ngc.nvidia.com/nvidia && helm repo update
使用NVIDIA GPU Operator部署DCGM exporter
要部署DCGM exporter,请完成以下步骤:
- 准备DCGM exporter GPU指标配置
curl https://raw.githubusercontent.com/NVIDIA/dcgm-exporter/main/etc/dcp-metrics-included.csv > dcgm-metrics.csv
您可以选择编辑dcgm-metrics.csv
文件。根据需要添加或删除任何指标。
- 创建gpu-operator命名空间和DCGM exporter ConfigMap
kubectl create namespace gpu-operator && /
kubectl create configmap metrics-config -n gpu-operator --from-file=dcgm-metrics.csv
输出如下:
命名空间/gpu-operator已创建
configmap/metrics-config已创建
- 将GPU operator应用于EKS集群
helm install --wait --generate-name -n gpu-operator --create-namespace nvidia/gpu-operator \
--set dcgmExporter.config.name=metrics-config \
--set dcgmExporter.env[0].name=DCGM_EXPORTER_COLLECTORS \
--set dcgmExporter.env[0].value=/etc/dcgm-exporter/dcgm-metrics.csv \
--set toolkit.enabled=false
输出如下:
名称: gpu-operator-1684795140
最后部署时间: Day Month Date HH:mm:ss YYYY
命名空间: gpu-operator
状态: 部署完成
修订版本: 1
测试套件: 无
- 确认DCGM exporter pod正在运行
kubectl -n gpu-operator get pods | grep dcgm
输出如下:
nvidia-dcgm-exporter-lkmfr 1/1 运行 0 1m
如果您检查日志,应该会看到“Starting webserver”
的消息:
kubectl -n gpu-operator logs -f $(kubectl -n gpu-operator get pods | grep dcgm | cut -d ' ' -f 1)
输出如下:
默认容器"nvidia-dcgm-exporter"中的: nvidia-dcgm-exporter, toolkit-validation (init)
time="2023-05-22T22:40:08Z" level=info msg="Starting dcgm-exporter"
time="2023-05-22T22:40:08Z" level=info msg="DCGM successfully initialized!"
time="2023-05-22T22:40:08Z" level=info msg="Collecting DCP Metrics"
time="2023-05-22T22:40:08Z" level=info msg="No configmap data specified, falling back to metric file /etc/dcgm-exporter/dcgm-metrics.csv"
time="2023-05-22T22:40:08Z" level=info msg="Initializing system entities of type: GPU"
time="2023-05-22T22:40:09Z" level=info msg="Initializing system entities of type: NvSwitch"
time="2023-05-22T22:40:09Z" level=info msg="Not collecting switch metrics: no switches to monitor"
time="2023-05-22T22:40:09Z" level=info msg="Initializing system entities of type: NvLink"
time="2023-05-22T22:40:09Z" level=info msg="Not collecting link metrics: no switches to monitor"
time="2023-05-22T22:40:09Z" level=info msg="Kubernetes metrics collection enabled!"
time="2023-05-22T22:40:09Z" level=info msg="Pipeline starting"
time="2023-05-22T22:40:09Z" level=info msg="Starting webserver"
NVIDIA DCGM Exporter公开了一个Prometheus指标端点,可以由CloudWatch代理接收。要查看端点,请使用以下命令:
kubectl -n gpu-operator get services | grep dcgm
我们得到以下输出:
nvidia-dcgm-exporter ClusterIP 10.100.183.207 <none> 9400/TCP 10m
- 为了生成一些GPU利用率,我们部署一个运行gpu-burn二进制文件的pod
kubectl apply -f https://raw.githubusercontent.com/aws-samples/aws-do-eks/main/Container-Root/eks/deployment/gpu-metrics/gpu-burn-deployment.yaml
输出如下:
deployment.apps/gpu-burn created
该部署使用单个GPU,连续运行100%利用率20秒,然后运行0%利用率20秒的模式。
- 为了确保端点正常工作,您可以运行一个临时容器,使用curl读取
http://nvidia-dcgm-exporter:9400/metrics
的内容
kubectl -n gpu-operator run -it --rm curl --restart='配置和部署 CloudWatch 代理
要配置和部署 CloudWatch 代理,请按照以下步骤完成:
- 下载 YAML 文件并编辑
curl -O https://raw.githubusercontent.com/aws-samples/amazon-cloudwatch-container-insights/k8s/1.3.15/k8s-deployment-manifest-templates/deployment-mode/service/cwagent-prometheus/prometheus-eks.yaml
该文件包含一个 cwagent configmap
和一个 prometheus configmap
。在本文中,我们都对它们进行编辑。
- 编辑
prometheus-eks.yaml
文件
在您喜欢的编辑器中打开 prometheus-eks.yaml
文件,并将 cwagentconfig.json
部分替换为以下内容:
apiVersion: v1
data:
# cwagent json config
cwagentconfig.json: |
{
"logs": {
"metrics_collected": {
"prometheus": {
"prometheus_config_path": "/etc/prometheusconfig/prometheus.yaml",
"emf_processor": {
"metric_declaration": [
{
"source_labels": ["Service"],
"label_matcher": ".*dcgm.*",
"dimensions": [["Service","Namespace","ClusterName","job","pod"]],
"metric_selectors": [
"^DCGM_FI_DEV_GPU_UTIL$",
"^DCGM_FI_DEV_DEC_UTIL$",
"^DCGM_FI_DEV_ENC_UTIL$",
"^DCGM_FI_DEV_MEM_CLOCK$",
"^DCGM_FI_DEV_MEM_COPY_UTIL$",
"^DCGM_FI_DEV_POWER_USAGE$",
"^DCGM_FI_DEV_ROW_REMAP_FAILURE$",
"^DCGM_FI_DEV_SM_CLOCK$",
"^DCGM_FI_DEV_XID_ERRORS$",
"^DCGM_FI_PROF_DRAM_ACTIVE$",
"^DCGM_FI_PROF_GR_ENGINE_ACTIVE$",
"^DCGM_FI_PROF_PCIE_RX_BYTES$",
"^DCGM_FI_PROF_PCIE_TX_BYTES$",
"^DCGM_FI_PROF_PIPE_TENSOR_ACTIVE$"
]
}
]
}
}
},
"force_flush_interval": 5
}
}
- 在
prometheus
配置部分中,追加以下 DCGM 导出器的作业定义
- job_name: 'kubernetes-pod-dcgm-exporter'
sample_limit: 10000
metrics_path: /api/v1/metrics/prometheus
kubernetes_sd_configs:
- role: pod
relabel_configs:
- source_labels: [__meta_kubernetes_pod_container_name]
action: keep
regex: '^DCGM.*$'
- source_labels: [__address__]
action: replace
regex: ([^:]+)(?::\d+)?
replacement: ${1}:9400
target_label: __address__
- action: labelmap
regex: __meta_kubernetes_pod_label_(.+)
- action: replace
source_labels:
- __meta_kubernetes_namespace
target_label: Namespace
- source_labels: [__meta_kubernetes_pod]
action: replace
target_label: pod
- action: replace
source_labels:
- __meta_kubernetes_pod_container_name
target_label: container_name
- action: replace
source_labels:
- __meta_kubernetes_pod_controller_name
target_label: pod_controller_name
- action: replace
source_labels:
- __meta_kubernetes_pod_controller_kind
target_label: pod_controller_kind
- action: replace
source_labels:
- __meta_kubernetes_pod_phase
target_label: pod_phase
- action: replace
source_labels:
- __meta_kubernetes_pod_node_name
target_label: NodeName
- 保存文件并将
cwagent-dcgm
配置应用于集群
kubectl apply -f ./prometheus-eks.yaml
我们得到以下输出:
namespace/amazon-cloudwatch created
configmap/prometheus-cwagentconfig created
configmap/prometheus-config created
serviceaccount/cwagent-prometheus created
clusterrole.rbac.authorization.k8s.io/cwagent-prometheus-role created
clusterrolebinding.rbac.authorization.k8s.io/cwagent-prometheus-role-binding created
deployment.apps/cwagent-prometheus created
- 确认CloudWatch代理Pod正在运行
kubectl -n amazon-cloudwatch get pods
我们得到以下输出:
NAME READY STATUS RESTARTS AGE
cwagent-prometheus-7dfd69cc46-s4cx7 1/1 Running 0 15m
在CloudWatch控制台上可视化指标
要在CloudWatch中可视化指标,请完成以下步骤:
- 在导航窗格中的CloudWatch控制台下,选择所有指标
- 在自定义命名空间部分,选择新的ContainerInsights/Prometheus条目
有关ContainerInsights/Prometheus命名空间的更多信息,请参阅获取其他Prometheus源的信息并导入这些指标。
- 深入到指标名称并选择
DCGM_FI_DEV_GPU_UTIL
- 在图形指标选项卡上,将周期设置为5秒
- 将刷新间隔设置为10秒
您将看到从DCGM导出器收集的指标,可视化了每20秒打开和关闭的gpu-burn
模式。
在浏览选项卡上,您可以看到数据,包括每个指标的Pod名称。
EKS API元数据已与DCGM指标数据合并,生成了基于Pod的提供的GPU指标。
这结束了通过CloudWatch代理将DCGM指标导出到CloudWatch的第一种方法。
在下一节中,我们将配置第二种架构,将DCGM指标导出到Prometheus,并使用Grafana对其进行可视化。
使用Prometheus和Grafana可视化DCGM的GPU指标
完成以下步骤:
- 添加Prometheus社区Helm图表
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
该图表部署了Prometheus和Grafana。在运行安装命令之前,我们需要对图表进行一些编辑。
- 将图表配置值保存到
/tmp
文件中
helm inspect values prometheus-community/kube-prometheus-stack > /tmp/kube-prometheus-stack.values
- 编辑图表配置文件
编辑保存的文件(/tmp/kube-prometheus-stack.values
),并通过查找设置名称并设置值来设置以下选项:
prometheus.prometheusSpec.serviceMonitorSelectorNilUsesHelmValues=false
- 将以下ConfigMap添加到
additionalScrapeConfigs
部分
additionalScrapeConfigs:
- job_name:gpu-metrics
scrape_interval:1s
metrics_path:/metrics
scheme:http
kubernetes_sd_configs:
- role:endpoints
namespaces:
names:
- gpu-operator
relabel_configs:
- source_labels:[__meta_kubernetes_pod_node_name]
action:replace
target_label:kubernetes_node
- 使用更新后的值部署Prometheus堆栈
helm install prometheus-community/kube-prometheus-stack \
--create-namespace --namespace prometheus \
--generate-name \
--values /tmp/kube-prometheus-stack.values
我们得到以下输出:
NAME:kube-prometheus-stack-1684965548
LAST DEPLOYED:Wed May 24 21:59:14 2023
NAMESPACE:prometheus
STATUS:deployed
REVISION:1
NOTES:
kube-prometheus-stack已安装。通过运行以下命令检查其状态:
kubectl --namespace prometheus get pods -l "release=kube-prometheus-stack-1684965548"
访问https://github.com/prometheus-operator/kube-prometheus
以获取使用运算符创建和配置Alertmanager和Prometheus实例的说明。
- 确认Prometheus pods正在运行
kubectl get pods -n prometheus
我们得到以下输出:
NAME READY STATUS RESTARTS AGE
alertmanager-kube-prometheus-stack-1684-alertmanager-0 2/2 Running 0 6m55s
kube-prometheus-stack-1684-operator-6c87649878-j7v55 1/1 Running 0 6m58s
kube-prometheus-stack-1684965548-grafana-dcd7b4c96-bzm8p 3/3 Running 0 6m58s
kube-prometheus-stack-1684965548-kube-state-metrics-7d856dptlj5 1/1 Running 0 6m58s
kube-prometheus-stack-1684965548-prometheus-node-exporter-2fbl5 1/1 Running 0 6m58s
kube-prometheus-stack-1684965548-prometheus-node-exporter-m7zmv 1/1 Running 0 6m58s
prometheus-kube-prometheus-stack-1684-prometheus-0 2/2 Running 0 6m55s
Prometheus和Grafana pods处于Running
状态。
接下来,我们验证DCGM指标是否流入Prometheus。
- 端口转发Prometheus UI
有多种方式可以将在EKS中运行的Prometheus UI暴露给源自集群外部的请求。我们将使用kubectl端口转发
。到目前为止,我们一直在aws-do-eks
容器内执行命令。为了访问在集群中运行的Prometheus服务,我们将从主机创建一个隧道。在这里,aws-do-eks
容器正在通过在容器之外的主机上的新终端窗口中执行以下命令来运行。我们将将其称为“主机shell”。
kubectl -n prometheus port-forward svc/$(kubectl -n prometheus get svc | grep prometheus | grep -v alertmanager | grep -v operator | grep -v grafana | grep -v metrics | grep -v exporter | grep -v operated | cut -d ' ' -f 1) 8080:9090 &
在端口转发过程运行时,我们可以从主机上访问Prometheus UI,如下所述。
- 打开Prometheus UI
- 如果您使用的是Cloud9,请导航到
Preview->Preview Running Application
,在Cloud9 IDE中的一个选项卡中打开Prometheus UI,然后单击选项卡右上角的图标,以在新窗口中弹出。
- 如果您在本地主机上或通过远程桌面连接到EC2实例,请打开浏览器并访问URL
http://localhost:8080
。
- 输入
DCGM
以查看流入Prometheus的DCGM指标
- 选择
DCGM_FI_DEV_GPU_UTIL
,选择执行,然后导航到图表选项卡以查看预期的GPU利用率模式
- 停止Prometheus的端口转发进程
在主机Shell中运行以下命令行:
kill -9 $(ps -aef | grep port-forward | grep -v grep | grep prometheus | awk '{print $2}')
现在我们可以通过Grafana仪表板可视化DCGM指标。
- 检索登录Grafana UI的密码
kubectl -n prometheus get secret $(kubectl -n prometheus get secrets | grep grafana | cut -d ' ' -f 1) -o jsonpath="{.data.admin-password}" | base64 --decode ; echo
- 端口转发Grafana服务
在主机Shell中运行以下命令行:
kubectl port-forward -n prometheus svc/$(kubectl -n prometheus get svc | grep grafana | cut -d ' ' -f 1) 8080:80 &
- 登录Grafana UI
以与之前访问Prometheus UI相同的方式访问Grafana UI登录界面。如果使用Cloud9,请选择Preview->Preview Running Application
,然后在新窗口中弹出。如果使用本地主机或具有远程桌面的EC2实例,请访问URLhttp://localhost:8080
。使用用户名admin和之前检索到的密码登录。
- 在导航窗格中选择仪表板
- 选择新建和导入
我们将导入NVIDIA DCGM Exporter Dashboard中描述的默认DCGM Grafana仪表板。
- 在
通过grafana.com导入
字段中输入12239
,然后选择加载
- 选择数据源为Prometheus
- 选择导入
您将会看到一个类似于以下截图的仪表盘。
为了证明这些指标是基于 Pod 的,我们将修改此仪表盘中的GPU 利用率窗格。
- 选择窗格和选项菜单(三个点)
- 展开选项部分并编辑图例字段
- 将值替换为
Pod {{pod}}
,然后选择保存
现在图例显示与显示的 GPU 利用率相关联的gpu-burn
Pod 名称。
- 停止端口转发 Grafana UI 服务
在主机 shell 中运行以下命令:
kill -9 $(ps -aef | grep port-forward | grep -v grep | grep prometheus | awk '{print $2}')
在本文中,我们演示了在 EKS 集群中使用开源的 Prometheus 和 Grafana。如果需要,可以将此部署替换为 Amazon Managed Service for Prometheus 和 Amazon Managed Grafana。
清理
要清理您创建的资源,请从aws-do-eks
容器 shell 运行以下脚本:
./eks-delete.sh
结论
在本文中,我们利用 NVIDIA DCGM Exporter 收集 GPU 指标,并使用 CloudWatch 或 Prometheus 和 Grafana 进行可视化。我们邀请您使用这里演示的架构,在您自己的 AWS 环境中启用 NVIDIA DCGM 的 GPU 利用率监控。
其他资源
- Amazon EC2 GPU 实例
- NVIDIA DCGM:在集群环境中管理和监控 GPU
- kube-prometheus-stack GitHub 仓库