Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ Soperator consists of the following components:
- **soperator-activechecks**. This chart is optional. It is used to deploy Kubernetes resources of `kind: ActiveCheck`, which perform active jobs to monitor and validate the state of a Slurm cluster.
- **soperator-crds**. It deploys the schema of the `SlurmCluster` custom resource, it should always be applied when
you upgrade the version of Soperator in your cluster.
- **soperator-dcgm-exporter**. This chart is optional. It is a custom Helm chart with `hpc-jobs-dir` where Slurm stores information about jobs.
- **soperator-dcgm-exporter**. This chart is optional. It deploys the NVIDIA DCGM exporter with soperator's curated metric set and Slurm-aware relabelings (NVLink instance group and NodeSet labels).
- **soperator-fluxcd**. This chart is optional. It is an umbrella chart with various components designed for convenient use in a cluster with soperator. It includes `kind: HelmRelease` and `kind: HelmRepository`. This approach was chosen because customizing components through Kustomize is difficult, and Helm dependencies lack flexibility in configuration. Therefore, this umbrella chart was created to deploy HelmReleases with properly configured dependencies.
- **soperatorchecks**. This chart is optional. It is used to deploy additional controller that runs Slurm and Kubernetes jobs to actively check the state of the Slurm cluster.

Expand Down
1 change: 1 addition & 0 deletions docs/metrics-pipeline.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ The exporter applies metric relabeling to drop volatile Kubernetes labels (`pod`
- Metrics: GPU temperature, power, utilization, memory, errors
- Scrape Interval: 15s
- DaemonSet: Runs on nodes with `nvidia.com/gpu.deploy.dcgm-exporter=true`
- Job attribution: DCGM metrics carry no job labels; per-job GPU views join them with the Slurm exporter's `slurm_node_job` metric on `node_name`, which is added to DCGM series at scrape time from the worker Pod name (see [slurm-exporter.md](slurm-exporter.md))

Connection Example:
```bash
Expand Down
14 changes: 14 additions & 0 deletions docs/slurm-exporter.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,20 @@ max by (user_name, nvlink_instance_group, nodeset_name) (
)
```

### Joining DCGM GPU metrics with jobs

DCGM metrics carry no job labels, but they carry `node_name` — the Slurm node name, copied at scrape time from the worker Pod name. Attribute GPU metrics to jobs by joining it with `slurm_node_job`. The join is node-granular — when several jobs share one node, each job inherits the whole node's GPU signal.

```promql
# Average GPU utilization per running job
avg by (job_id) (
slurm_node_job
* on (node_name) group_left()
avg by (node_name) (DCGM_FI_DEV_GPU_UTIL)
)
and on (job_id) slurm_job_info{job_state="RUNNING"}
```

### Controller RPC Metrics

These metrics provide insights into SLURM controller performance, similar to the output of the `sdiag` command, and were implemented to address [issue #1027](https://github.com/nebius/soperator/issues/1027).
Expand Down
32 changes: 0 additions & 32 deletions helm/slurm-cluster/slurm_scripts/map_job_dcgm.sh

This file was deleted.

16 changes: 0 additions & 16 deletions helm/slurm-cluster/slurm_scripts/map_job_dcgm.sh.json

This file was deleted.

23 changes: 0 additions & 23 deletions helm/slurm-cluster/slurm_scripts/unmap_job_dcgm.sh

This file was deleted.

16 changes: 0 additions & 16 deletions helm/slurm-cluster/slurm_scripts/unmap_job_dcgm.sh.json

This file was deleted.

8 changes: 0 additions & 8 deletions helm/slurm-cluster/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -874,14 +874,6 @@ slurmScripts:
enabled: true
customContent: null
customConfig: null
map_job_dcgm.sh:
enabled: true
customContent: null
customConfig: null
unmap_job_dcgm.sh:
enabled: true
customContent: null
customConfig: null
extra: null
# extra:
# example.sh:
Expand Down
33 changes: 1 addition & 32 deletions helm/soperator-dcgm-exporter/templates/daemonset.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ spec:
value: "true"
- name: DCGM_EXPORTER_COLLECTORS
value: /etc/dcgm-exporter/dcgm-metrics.csv
- name: DCGM_HPC_JOB_MAPPING_DIR
value: {{ .Values.dcgmHpcJobMappingDir }}
- name: NODE_NAME
valueFrom:
fieldRef:
Expand Down Expand Up @@ -55,30 +53,9 @@ spec:
name: metrics-config
readOnly: true
subPath: dcgm-metrics.csv
- mountPath: {{ .Values.dcgmHpcJobMappingDir }}
mountPropagation: HostToContainer
name: hpc-jobs-dir
dnsPolicy: ClusterFirst
initContainers:
- name: create-hpc-jobs-dir
command:
- sh
- -c
- |
mkdir -p {{ .Values.dcgmHpcJobMappingDir }}
env:
- name: KUBERNETES_CLUSTER_DOMAIN
value: {{ quote .Values.kubernetesClusterDomain }}
image: {{ .Values.daemonSet.createHpcJobsDir.image.repository }}:{{ .Values.daemonSet.createHpcJobsDir.image.tag }}
imagePullPolicy: {{ .Values.daemonSet.createHpcJobsDir.image.pullPolicy | default "IfNotPresent" }}
resources: {}
securityContext:
privileged: true
volumeMounts:
- mountPath: /var
mountPropagation: HostToContainer
name: host-var
{{- if .Values.validateToolkit }}
initContainers:
- name: toolkit-validation
args:
- until [ -f /run/nvidia/validations/toolkit-ready ]; do echo waiting for nvidia container stack to be setup; sleep 5; done
Expand Down Expand Up @@ -126,14 +103,6 @@ spec:
type: ""
name: run-nvidia
{{- end }}
- hostPath:
path: {{ .Values.dcgmHpcJobMappingDir }}
type: ""
name: hpc-jobs-dir
- hostPath:
path: /var
type: ""
name: host-var
- configMap:
defaultMode: 420
items:
Expand Down
16 changes: 8 additions & 8 deletions helm/soperator-dcgm-exporter/values.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
kubernetesClusterDomain: cluster.local

dcgmHpcJobMappingDir: /var/run/nebius/slurm

validateToolkit: true

metricsPort: 9400
Expand All @@ -16,12 +14,6 @@ daemonSet:
tag: v24.6.2
pullPolicy: IfNotPresent

createHpcJobsDir:
image:
repository: cr.eu-north1.nebius.cloud/soperator/busybox
tag: latest
pullPolicy: IfNotPresent

nvidiaDcgmExporter:
image:
repository: cr.eu-north1.nebius.cloud/soperator/dcgm-exporter
Expand All @@ -43,6 +35,14 @@ serviceMonitor:
regex: feature_node_kubernetes_io_.*|kubernetes_io_.*|nvidia_com_gpu.*
- action: labeldrop
regex: UUID|DCGM_FI_PROCESS_NAME|DCGM_FI_DEV_SERIAL|DCGM_FI_DEV_NAME|job|pci_bus_id|prometheus
# The worker Pod name (exported_pod after the scrape resolves the pod-label
# conflict) equals the Slurm node name; expose it as node_name so DCGM series
# join with the Slurm exporter metrics without label rewriting.
- action: replace
regex: (.+)
sourceLabels:
- exported_pod
targetLabel: node_name
relabelings:
- action: replace
regex: nvidia-dcgm-exporter
Expand Down
1 change: 0 additions & 1 deletion helm/soperator-fluxcd/templates/dcgm-exporter.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ spec:
upgrade:
{{- toYaml .Values.observability.dcgmExporter.upgrade | nindent 4 }}
values:
dcgmHpcJobMappingDir: {{ .Values.observability.dcgmExporter.values.hpcJobMapDir }}
validateToolkit: {{ .Values.observability.dcgmExporter.values.validateToolkit }}
{{- if .Values.observability.dcgmExporter.values.resources }}
daemonSet:
Expand Down
1 change: 0 additions & 1 deletion helm/soperator-fluxcd/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -648,7 +648,6 @@ observability:
retries: 6
remediateLastFailure: true
values:
hpcJobMapDir: /var/run/nebius/slurm
validateToolkit: true
resources:
limits:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3909,7 +3909,7 @@
},
"editorMode": "code",
"exemplar": false,
"expr": "with (\n filters = {}\n)\n# GPU Util\navg(\n label_move(\n DCGM_FI_DEV_GPU_UTIL{cluster=~\"$cluster\", filters}\n , 'hpc_job','job_id')\n) by (job_id)\nand on (job_id)\nslurm_job_info{cluster=~\"$cluster\", \n filters,\n job_state=\"RUNNING\",\n user_name=~\"$user_name\", \n slurm_partition${partition_match_exp}, \n}",
"expr": "with (\n filters = {}\n)\n# GPU Util\navg(\n slurm_node_job{cluster=~\"$cluster\"}\n * on(cluster, node_name) group_left()\n avg(\n DCGM_FI_DEV_GPU_UTIL{cluster=~\"$cluster\", filters}\n ) by (cluster, node_name)\n) by (job_id)\nand on (job_id)\nslurm_job_info{cluster=~\"$cluster\",\n filters,\n job_state=\"RUNNING\",\n user_name=~\"$user_name\",\n slurm_partition${partition_match_exp},\n}",
"format": "table",
"hide": false,
"instant": true,
Expand All @@ -3924,7 +3924,7 @@
},
"editorMode": "code",
"exemplar": false,
"expr": "with (\n filters = {}\n)\n# SM Util\navg(\n label_move(\n DCGM_FI_PROF_SM_ACTIVE{cluster=~\"$cluster\", filters}\n , 'hpc_job','job_id')\n) by (job_id)\nand on (job_id)\nslurm_job_info{cluster=~\"$cluster\", \n filters,\n job_state=\"RUNNING\",\n user_name=~\"$user_name\", \n slurm_partition${partition_match_exp}, \n}",
"expr": "with (\n filters = {}\n)\n# SM Util\navg(\n slurm_node_job{cluster=~\"$cluster\"}\n * on(cluster, node_name) group_left()\n avg(\n DCGM_FI_PROF_SM_ACTIVE{cluster=~\"$cluster\", filters}\n ) by (cluster, node_name)\n) by (job_id)\nand on (job_id)\nslurm_job_info{cluster=~\"$cluster\",\n filters,\n job_state=\"RUNNING\",\n user_name=~\"$user_name\",\n slurm_partition${partition_match_exp},\n}",
"format": "table",
"hide": false,
"instant": true,
Expand Down Expand Up @@ -4734,13 +4734,13 @@
"disableTextWrap": false,
"editorMode": "code",
"exemplar": false,
"expr": "floor(\n avg_over_time(\n (\n (\n (\n round((count(DCGM_FI_DEV_FB_USED{cluster=~\"$cluster\", hpc_job!=\"\"} > 2) by(hpc_job) / 8) > 2)\n -\n round(\n (count(DCGM_FI_DEV_POWER_USAGE{cluster=~\"$cluster\", hpc_job!=\"\"} > 120) by(hpc_job) / 8)\n >\n 2\n )\n )\n /\n round((count(DCGM_FI_DEV_FB_USED{cluster=~\"$cluster\", hpc_job!=\"\", } > 2) by(hpc_job) / 8) > 2)\n )\n *\n 100\n )[1h:1m]\n) >= 70\n)\n and on(hpc_job)\nlabel_move(slurm_job_info{cluster=~\"$cluster\", job_state=\"RUNNING\"}, 'job_id', 'hpc_job')\nand on (hpc_job)\ncount_over_time( #filter out short durations\navg_over_time((\n (\n (\n (\n round((count(DCGM_FI_DEV_FB_USED{cluster=~\"$cluster\", hpc_job!=\"\"} > 2) by(hpc_job) / 8) > 2)\n -\n round(\n (count(DCGM_FI_DEV_POWER_USAGE{cluster=~\"$cluster\", hpc_job!=\"\"} > 120) by(hpc_job) / 8)\n >\n 2\n )\n )\n /\n round((count(DCGM_FI_DEV_FB_USED{cluster=~\"$cluster\", hpc_job!=\"\"} > 2) by(hpc_job) / 8) > 2)\n )\n *\n 100\n )[1h:1m]\n) > 70\n and on(hpc_job)\nlabel_move(slurm_job_info{cluster=~\"$cluster\", job_state=\"RUNNING\"}, 'job_id', 'hpc_job')\n)[1h:1m]\n) > 20",
"expr": "with (\n gpus_total = count(DCGM_FI_DEV_FB_USED{cluster=~\"$cluster\"}) by (cluster, node_name),\n mem_frac = count(DCGM_FI_DEV_FB_USED{cluster=~\"$cluster\"} > 2) by (cluster, node_name) / gpus_total,\n power_frac = count(DCGM_FI_DEV_POWER_USAGE{cluster=~\"$cluster\"} > 120) by (cluster, node_name) / gpus_total,\n workers_with_mem = round(sum(slurm_node_job{cluster=~\"$cluster\"} * on(cluster, node_name) group_left() mem_frac) by (job_id) > 2),\n workers_with_power = round(sum(slurm_node_job{cluster=~\"$cluster\"} * on(cluster, node_name) group_left() power_frac) by (job_id) > 2),\n underutil_pct = (workers_with_mem - workers_with_power) / workers_with_mem * 100,\n running_jobs = slurm_job_info{cluster=~\"$cluster\", job_state=\"RUNNING\"}\n)\nfloor(avg_over_time(underutil_pct[1h:1m]) >= 70)\nand on(job_id) running_jobs\nand on (job_id)\ncount_over_time( #filter out short durations\n (avg_over_time(underutil_pct[1h:1m]) > 70 and on(job_id) running_jobs)[1h:1m]\n) > 20",
"format": "time_series",
"fullMetaSearch": false,
"hide": false,
"includeNullMetadata": true,
"intervalFactor": 2,
"legendFormat": "{{hpc_job}}",
"legendFormat": "{{job_id}}",
"range": true,
"refId": "A",
"useBackend": false,
Expand Down Expand Up @@ -5948,7 +5948,7 @@
"uid": "${datasource}"
},
"editorMode": "code",
"expr": "with (\n filters = {}\n)\navg(\n label_move(DCGM_FI_DEV_GPU_UTIL{cluster=~\"$cluster\", filters, hpc_job!=\"\"}, 'hpc_job','job_id')\n * on (job_id) group_left(user_name)\n slurm_job_info{cluster=~\"$cluster\", \n filters,\n job_state=\"RUNNING\",\n user_name=~\"$user_name\", \n slurm_partition${partition_match_exp}\n }\n) by (user_name)",
"expr": "with (\n filters = {}\n)\navg(\n (\n slurm_node_job{cluster=~\"$cluster\"}\n * on(cluster, node_name) group_left()\n avg(\n DCGM_FI_DEV_GPU_UTIL{cluster=~\"$cluster\", filters}\n ) by (cluster, node_name)\n )\n * on (job_id) group_left(user_name)\n slurm_job_info{cluster=~\"$cluster\",\n filters,\n job_state=\"RUNNING\",\n user_name=~\"$user_name\",\n slurm_partition${partition_match_exp}\n }\n) by (user_name)",
"format": "time_series",
"intervalFactor": 1,
"legendFormat": "__auto",
Expand Down
Loading
Loading