DCGM Exporter を用いた Kubernetes における NVIDIA GPU 監視環境の構築
はじめに
以前こちらの 死活監視システムの構築 で Prometheus と Grafana を用いた GPU 監視環境の構築に関する記事をかきました. 1年も経つと様子がだいぶ変わってきているようなので再度調査しました. 使用している Kubernetes は 1.19.4 です.
画像出典: Integrating GPU Telemetry into Kubernetes — NVIDIA Cloud Native Technologies documentation
構成
今回は OSS として公開されているコンポーネントのうち以下の点の物を使用していきます.また,全てパッケージ管理ツールである Helm v3 を用いて管理していきます.
使用バージョン一覧
コンポーネント名 | APP バージョン | Chart バージョン |
---|---|---|
kube prometheus stack | 0.43.2 | 12.2.2 |
DCGM-Exporter | 2.1.0 | 2.1.0 |
ingress nginx | 0.41.2 | 3.11.0 |
kube prometheus stack
kube prometheus stack とは prometheus operator,grafana,node exporter などが含まれているコンポーネントです.kube prometheus stack をデプロイするだけでこれら全てのコンポーネントをデプロイすることができます.
DCGM-Exporter
NVIDIA が提供している prometheus 用の exporter です. GPU の温度や使用率,クロック周波数など様々なメトリクスを収集することができます.
ingress nginx
Kubernetes 用 Ingress Cotroller です.今回は,prometheus,alertmanager,grafana へのアクセスに ingress を用います.
構築
ingress nginx,kube prometheus stack,DCGM-Exporter の順番で構築していきます.特に kube prometheus stack と DCGM-Exporter の構築順序を変更しないでください.逆にして構築してしまうと DCGM-Exporter デプロイ時にno matches for kind "ServiceMonitor" in version "monitoring.coreos.com/v1
とエラーになってデプロイすることができません.
また,今回は monitoring
namespace に全てデプロイするので事前に作成しておいてください.
ingress nginx
helm を使用するのでお好みの設定の values.yaml を作成してデプロイするだけです.私の環境では以下のようにしました.設定可能項目はhelm show values ingress-nginx/ingress-nginx
で見ることができます.
controller: ingressClass: nginx-monitoring affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: hardware-type operator: In values: - NonGPU service: loadBalancerIP: xxx.xxx.xxx.xxx replicaCount: 2
ポイントは ingressClass
の部分です.複数の ingress controller を使用する場合はここを設定する必要があります.また,loadBalancerIP
の部分は固定したい場合のみ指定し,指定しない場合はランダムで割り振られます.
デプロイします.
$ helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx $ helm repo update $ helm install ingress-monitoring --namespace monitoring ingress-nginx/ingress-nginx --values values.yaml
kube prometheus stack
こちらは複数のコンポーネントがまとまっているためかなり設定項目があります.まずは,それぞれのコンポーネントの設定可能値を確認することをお勧めします.
$ helm repo add prometheus-community https://prometheus-community.github.io/helm-charts $ helm repo add grafana https://grafana.github.io/helm-charts $ helm repo update $ # kube prometheus の設定可能値 $ helm show values prometheus-community/kube-prometheus-stack $ # grafana の設定可能値 $ helm show values grafana/grafana
全体的な設定
全体的な設定として以下のようにしました.
namespaceOverride: "monitoring" defaultRules: rules: etcd: false kubeEtcd: enabled: false kubeControllerManager: service: port: 10257 targetPort: 10257 serviceMonitor: https: "true" insecureSkipVerify: "true" kubeScheduler: service: port: 10259 targetPort: 10259 serviceMonitor: https: "true" insecureSkipVerify: "true"
まず,こちらの Issue [prometheus-kube-stack] Target Kubelet 0/0 up and others are down にあるように,kubernetes では kubeScheduler やkubeApiserver や kubeControllermanager では http での通信が廃止されているにも関わらず,デフォルト設定では http でメトリクスを収集しにいく設定になっているため,https でメトリクスを収集しにいく設定にしなければなりません.また,etcd については現在うまくメトリクスを収集できないそうなので,収集しないように設定します.
さらに,kubeadm で Kubernetes クラスタを構築している場合は kubeadm conf で以下のように収集を受け付けるように設定してください.
apiVersion: kubeadm.k8s.io/v1beta2 kind: ClusterConfiguration kubernetesVersion: v1.19.4 scheduler: extraArgs: bind-address: 0.0.0.0 controllerManager: extraArgs: bind-address: 0.0.0.0
kubeProxy は kubectl edit cm/kube-proxy -n kube-system
で以下のように設定を変更する必要があります.
... kind: KubeProxyConfiguration metricsBindAddress: 0.0.0.0:10249 ...
alertmanager の設定
以下のようにしました.
alertmanager: ingress: enabled: true ingressClassName: nginx-monitoring hosts: - hogehoge.hoge.hoge paths: - /alertmanager alertmanagerSpec: replicas: 2 externalUrl: hogehoge.hoge.hoge routePrefix : alertmanager/ affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: hardware-type operator: In values: - NonGPU
ポイントとして,ingressClassName
に先ほど ingress nginx で設定したクラス名を指定する必要があります.
また,alertmanager.ingress.hosts
と alertmanager.alertmanagerSpec.externalUrl
,alertmanager.ingress.paths
とalertmanager.alertmanagerSpec.routePrefix
は揃える必要があります.
grafana の設定
以下のように設定しました.
grafana: adminPassword: hogehoge grafana.ini: server: domain: hogehoge.hoge.hoge root_url: "%(protocol)s://%(domain)s/grafana" serve_from_sub_path: true auth.anonymous: enabled: true org_role: Viewer ingress: enabled: true annotations: kubernetes.io/ingress.class: "nginx-monitoring" nginx.ingress.kubernetes.io/rewrite-target: /$1 nginx.ingress.kubernetes.io/use-regex: "true" hosts: - hogehoge.hoge.hoge path: /grafana/?(.*) sidecar: dashboards: enabled: true datasources: defaultDatasourceEnabled: false datasources: createPrometheusReplicasDatasources: true datasources.yaml: apiVersion: 1 datasources: - name: Prometheus type: prometheus url: http://kube-prometheus-kube-prome-prometheus:9090/prometheus access: proxy isDefault: true editable: false affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: hardware-type operator: In values: - NonGPU
grafana.ini
でアクセスに関する設定と anonymous ユーザに閲覧権限のみ付与する設定を行います.
grafana.ingress
では,/grafana でアクセスできるように設定しています.ただ単に grafana.ingress.path
に /grafana
を設定した場合 /garafa/ でないとアクセスすることができないので気をつけてください.
また,prometheus ,alertmanager ともに ingressClass に対応していますが, kube prometheus stack で使用している grafana は 5.8 系列のため grafana.ingress.annotations.kubernetes.io/ingress.class
で ingressClass を指定する必要がある点も注意が必要です.
次に,GPU 用のダッシュボードについてですが,NVIDIAが ここに 用意してくれています.Grafana Labs にもありますが,少し古いので注意が必要です.
Grafana ではドキュメント にあるように様々な形式でダッシュボードをインポートすることができます.しかしこちらの issue にある通り,ダッシュボード変数が認識されないため, NVIDIA が用意してくれているダッシュボードに微修正が必要です.そのため今回は sidecar を使った形式 で import を行います.
これは, grafana_dashboard: "1"
のラベルを付与した configmap を作成すると自動でインポートしてくれる優れものです.
ダッシュボードの変更箇所は以下の通りです.
... 略 "templating": { "list": [ + { + "hide": 0, + "label": "datasource", + "name": "DS_PROMETHEUS", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "", + "type": "datasource" + }, 以下略
私は configmap の生成には kustomize を使用したため 以下のような kutomization ファイル を作成しましたが,通常のマニフェストでも可能です.
apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization namespace: monitoring generatorOptions: disableNameSuffixHash: true labels: grafana_dashboard: "1" configMapGenerator: - name: nvidia-grafana-dashboard files: - configs/dcgm-exporter-dashboard.json
Prometheus の設定
以下のように設定しました.
prometheusOperator: admissionWebhooks: patch: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: hardware-type operator: In values: - NonGPU prometheus: ingress: enabled: true ingressClassName: nginx-monitoring hosts: - hogehoge.hoge.hoge paths: - /prometheus affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: hardware-type operator: In values: - NonGPU prometheusSpec: serviceMonitorSelectorNilUsesHelmValues: false podMonitorSelectorNilUsesHelmValues: false replicas: 2 affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: hardware-type operator: In values: - NonGPU externalUrl: http://hogehoge.hoge.hoge/prometheus routePrefix : prometheus/ additionalScrapeConfigs: - job_name: gpu-metrics scrape_interval: 1s metrics_path: /metrics scheme: http kubernetes_sd_configs: - role: endpoints namespaces: names: - gpu-operator-resources relabel_configs: - source_labels: [__meta_kubernetes_pod_node_name] action: replace target_label: kubernetes_node retention: 30d retentionSize: 290GB storageSpec: volumeClaimTemplate: spec: storageClassName: rook-ceph-block accessModes: ["ReadWriteOnce"] resources: requests: storage: 300Gi
DCGM-Exporter からメトリクスを収集するためには prometheus.prometheusSpec.serviceMonitorSelectorNilUsesHelmValues
は必ず false にする必要があります.また,NVIDIA のドキュメントにあるようにprometheus.prometheusSpec.additionalScrapeConfigs
にも設定が必要です.
さらに今回データは rook ceph から払い出した Block ストレージに吐き出すように設定し,永続化しています.
デプロイ
最終的に出来上がった values.yaml は以下のようになります.
namespaceOverride: "monitoring" defaultRules: rules: etcd: false # alertmanager alertmanager: ingress: enabled: true ingressClassName: nginx-monitoring hosts: - hogehoge.hoge.hoge paths: - /alertmanager alertmanagerSpec: replicas: 2 externalUrl: http://hogehoge.hoge.hoge/alertmanager routePrefix : alertmanager/ affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: hardware-type operator: In values: - NonGPU # grafana grafana: adminPassword: hogehoge grafana.ini: server: domain: hogehoge.hoge.hoge root_url: "%(protocol)s://%(domain)s/grafana" serve_from_sub_path: true auth.anonymous: enabled: true org_role: Viewer ingress: enabled: true annotations: kubernetes.io/ingress.class: "nginx-monitoring" nginx.ingress.kubernetes.io/rewrite-target: /$1 nginx.ingress.kubernetes.io/use-regex: "true" hosts: - hogehoge.hoge.hoge path: /grafana/?(.*) sidecar: dashboards: enabled: true datasources: defaultDatasourceEnabled: false datasources: createPrometheusReplicasDatasources: true datasources.yaml: apiVersion: 1 datasources: - name: Prometheus type: prometheus url: http://kube-prometheus-kube-prome-prometheus:9090/prometheus access: proxy isDefault: true editable: false affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: hardware-type operator: In values: - NonGPU prometheusOperator: admissionWebhooks: patch: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: hardware-type operator: In values: - NonGPU prometheus: ingress: enabled: true ingressClassName: nginx-monitoring hosts: - hogehoge.hoge.hoge paths: - /prometheus affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: hardware-type operator: In values: - NonGPU prometheusSpec: serviceMonitorSelectorNilUsesHelmValues: false podMonitorSelectorNilUsesHelmValues: false replicas: 2 affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: hardware-type operator: In values: - NonGPU externalUrl: http://hogehoge.hoge.hoge/prometheus routePrefix : prometheus/ additionalScrapeConfigs: - job_name: gpu-metrics scrape_interval: 1s metrics_path: /metrics scheme: http kubernetes_sd_configs: - role: endpoints namespaces: names: - gpu-operator-resources relabel_configs: - source_labels: [__meta_kubernetes_pod_node_name] action: replace target_label: kubernetes_node retention: 30d retentionSize: 290GB storageSpec: volumeClaimTemplate: spec: storageClassName: rook-ceph-block accessModes: ["ReadWriteOnce"] resources: requests: storage: 300Gi kubeEtcd: enabled: false kubeControllerManager: service: port: 10257 targetPort: 10257 serviceMonitor: https: "true" insecureSkipVerify: "true" kubeScheduler: service: port: 10259 targetPort: 10259 serviceMonitor: https: "true" insecureSkipVerify: "true"
デプロイしていきます.
# kube prometheus stack のデプロイ $ helm install kube-prometheus prometheus-community/kube-prometheus-stack -f values.yaml # dcgm 用ダッシュボード configmap のデプロイ $ kustomize build dashboards | kubectl apply -f -
DCGM-Exporter
以下のような values.yaml を使用して GPU が刺さっている Node のみにデプロイされるようにしました.
arguments: [] affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: hardware-type operator: In values: - NVIDIAGPU
ポイントとしては,こちらの issue で報告されているようにTesla V100 と Tesla T4 以外を使用している場合は必ず arguments: []
の指定が必要です.
デプロイしていきます.
$ helm repo add gpu-helm-charts https://nvidia.github.io/gpu-monitoring-tools/helm-charts $ helm repo update $ helm install dcgm-exporter --namespace monitoring gpu-helm-charts/dcgm-exporter -f values.yaml
ダッシュボード
prometheus には http://hogehoge.hoge.hoge/prometheus,alertmanager には http://hogehoge.hoge.hoge/alertmanager grafana には http://hogehoge.hoge.hoge/grafana でアクセスできます.アクセスできない場合やメトリクスが収集できていない場合は,必要なポートが空いているかなど確認してください.
最後に
今回は去年行った GPU 監視環境についてみなおしてみました.だいぶ理想的な形になったのではないかと思います.ではまたお会いしましょう.
参考
- NVIDIA の monitoring に関する公式ドキュメント
- NVIDIA Data Center コンポーネントの公式ドキュメント
- grafana root_url の公式ドキュメント
- grafana anonymous user の公式ドキュメント
- kubeScheduler や kubeControllerManagerの受付 IP範囲の設定
- kube-prometheus artifact hub
- kubeScheduler など監視できないIssue
- grafana helm chart の values.yaml
- ingressClass 対応された grafana 6.1.2
- grafana の ingressClass 対応に関する PR
- grafana Helm Chart ドキュメント
- prometheusダッシュボード 変数認識しない Isuue
- prometheus 用 ingress 書き方
- grafana ダッシュボード アドオン
- grafana datasource 書き方
- prometheus retension の変数