Data & Pipelines
The telemetry plumbing that makes a GPU fleet observable, the metrics worth scraping, traces across inference calls, the cost pipeline that turns metrics into dollars, and the weight/data flows that train and feed models.
The telemetry pipeline
The basic shape, drawn left to right:
GPU nodes ──┐
│ DCGM exporter (DaemonSet) Prometheus / Mimir / Cortex
├──────────────────────────────────▶ scrape /metrics ──▶ TSDB
Serving pods│ /metrics from vLLM, Triton ─▶ Grafana
└──────────────────────────────────▶ ─▶ Alertmanager
─▶ Cost ETL ──▶ data warehouse
Logs: pods ──▶ fluent-bit ──▶ Loki / ELK / Datadog
Traces: pods (OTel SDK) ──▶ OTel collector ──▶ Tempo / Jaeger / Honeycomb
You don't need to use these specific tools; you need to know what each layer does and what failure modes each has.
DCGM exporter — what you actually scrape
NVIDIA's DCGM (Data Center GPU Manager) reads telemetry directly from the GPU. The dcgm-exporter exposes it as Prometheus metrics. Default fields aren't enough; you'll add profiling fields.
| Metric | Field ID | What it tells you |
|---|---|---|
DCGM_FI_DEV_SM_CLOCK | 100 | SM clock speed — throttling shows here |
DCGM_FI_DEV_MEM_CLOCK | 101 | Memory clock speed |
DCGM_FI_DEV_GPU_TEMP | 150 | GPU temperature |
DCGM_FI_DEV_POWER_USAGE | 155 | Power draw (W) |
DCGM_FI_DEV_GPU_UTIL | 203 | The naive number from nvidia-smi; mostly useless alone |
DCGM_FI_DEV_MEM_COPY_UTIL | 204 | Memory copy engine util |
DCGM_FI_DEV_FB_USED | 252 | HBM used |
DCGM_FI_DEV_FB_FREE | 253 | HBM free |
DCGM_FI_PROF_GR_ENGINE_ACTIVE | 1001 | Graphics engine active fraction |
DCGM_FI_PROF_SM_ACTIVE | 1002 | SM-active — better utilization signal |
DCGM_FI_PROF_SM_OCCUPANCY | 1003 | SM occupancy — best signal |
DCGM_FI_PROF_PIPE_TENSOR_ACTIVE | 1004 | Tensor core active fraction |
DCGM_FI_PROF_DRAM_ACTIVE | 1005 | Memory bandwidth util — critical for decode |
DCGM_FI_PROF_NVLINK_TX_BYTES | 1011 | NVLink TX bytes |
DCGM_FI_DEV_XID_ERRORS | 230 | Xid error count — hardware/driver health |
DCGM_FI_DEV_ECC_* | various | ECC error counters (single-bit, double-bit) |
The DCGM_FI_PROF_* fields use the GPU's profiling counters. They have a small overhead and may conflict with user-space profilers (Nsight). Production typically samples at 1-5s intervals and accepts the small cost.
Prometheus shape & scaling
Prometheus is the de facto metrics backbone. For a fleet:
- Cardinality is the enemy. Labels with high cardinality (request_id, prompt_hash) will balloon the index. Stay at: node, gpu_index, model, namespace, deployment, instance.
- Federation or remote-write to a long-term store (Mimir, Cortex, Thanos, VictoriaMetrics) for cross-cluster queries and retention.
- Recording rules precompute heavy expressions (p95 latency over 5m windowed) so dashboards are snappy.
- Histograms over averages for any latency metric —
histogram_quantileon a_bucketseries.
# A representative ServiceMonitor (Prometheus Operator)
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: dcgm-exporter
labels: { release: prometheus }
spec:
selector:
matchLabels: { app: dcgm-exporter }
endpoints:
- port: metrics
interval: 5s
scrapeTimeout: 4s
relabelings:
- sourceLabels: [__meta_kubernetes_pod_node_name]
targetLabel: node
Logs from inference servers
Don't log the prompt body by default. Do log:
- Request ID (correlatable across services).
- Tenant / API key hash.
- Model and version.
- Prompt token count, response token count.
- TTFT, total latency.
- Cache hit indicator (prefix-cache reused or not).
- Outcome: success / error / aborted by client / timeout.
- Error class if applicable (OOM, NCCL, downstream).
Structured JSON. Fluent-bit (lightweight) or Vector to a backend (Loki, Elastic, Datadog). Retention sized to the regulatory window for any audit-relevant logs.
Tracing across model invocations
For complex pipelines (RAG, agents, multi-stage) traces beat logs. OpenTelemetry is the default standard.
- Gateway opens the root span on a request.
- Each downstream — retriever, embedding service, reranker, LLM — opens a child span.
- Spans carry attributes: model, tokens, cache hit, GPU node.
- Sampling is essential. Tail-based sampling (keep all traces with errors or high latency) gives the best signal-to-cost.
from opentelemetry import trace
from opentelemetry.trace import Status, StatusCode
tracer = trace.get_tracer("inference-gateway")
async def handle(request):
with tracer.start_as_current_span("infer") as span:
span.set_attribute("model", request.model)
span.set_attribute("prompt.tokens", request.prompt_tokens)
try:
with tracer.start_as_current_span("backend.vllm") as backend_span:
resp = await call_backend(request)
backend_span.set_attribute("response.tokens", resp.tokens)
return resp
except Exception as e:
span.set_status(Status(StatusCode.ERROR, str(e)))
raise
The metrics catalog — what every service should expose
If you build a serving service, expose this minimum metric set. vLLM and Triton ship most of these by default.
{service}_requests_total{status, model}— counter.{service}_request_duration_seconds_bucket{model, le}— histogram for percentiles.{service}_time_to_first_token_seconds_bucket{model}— histogram.{service}_tokens_generated_total{model}— counter.{service}_tokens_prompt_total{model}— counter.{service}_num_requests_running{model}— gauge.{service}_num_requests_waiting{model}— gauge.{service}_kv_cache_usage_perc{model}— gauge.{service}_prefix_cache_hit_ratio{model}— gauge.
"With num_requests_waiting, request_duration_p95, tokens_generated, and kv_cache_usage, you can diagnose 80% of serving incidents."
The cost pipeline
Cost is a derived metric. The pipeline:
- Source: Prometheus GPU-hour-equivalent usage per pod (sum of allocated GPUs × time).
- Source: cloud billing API (or on-prem amortization table) giving $/GPU-hour by instance type, including the team's reservation/spot mix.
- ETL: nightly job joins usage × unit cost, attributes via labels (team, namespace, model).
- Sink: data warehouse (BigQuery, Snowflake, Redshift) with daily granularity.
- Surface: Grafana dashboards, Slack weekly digest, internal cost portal.
Tools to know: Kubecost / OpenCost (Kubernetes-native), AWS Cost & Usage Reports, GCP Billing Export to BigQuery, Cloudability. None replaces a custom join when you need cross-cloud or on-prem attribution.
Training-side data flows
This role probably won't own training data quality, but you'll own its plumbing.
- Dataset storage — Parquet on S3 with manifest files. Petabyte-class for serious training.
- Data loaders — feeding the GPUs is its own performance problem. PyTorch
DataLoaderworkers, prefetch, pin_memory, IterableDataset for streaming. - Throughput target — keep the GPU saturated. The "GPU starved on I/O" pathology is real; profile data-loader idle time as part of training health.
- Filesystem — for HPC-style training, Lustre/Weka/FSx-for-Lustre over IB. For lighter training, S3 with a local cache (e.g.
s3fs,mountpoint-s3, or pre-staged copies). - Sharding strategy — pre-shard datasets by rank so workers don't all read the same files.
Model weight pipeline
Weights flow from training to serving. The pipeline you build:
- Source of truth — Hugging Face Hub mirror or internal model registry (e.g. MLflow, custom artifact store backed by S3 with signed manifests).
- Versioning — every checkpoint has an immutable ID. Tags like
llama3-70b-finetune-2026-05-08resolve to a hash. - Quantization step — produce FP8/INT4 derivatives, store them as siblings of the source FP16.
- Conversion to serving format — TensorRT-LLM engine builds happen on the target GPU type; safetensors is the source format for vLLM.
- Distribution — pre-stage to local NVMe on every relevant node; cache for re-use across pod restarts.
- Garbage collection — old checkpoints are big; lifecycle rules age them out of hot storage to glacier.
Pods pulling weights from Hugging Face on every restart is slow, expensive, and a supply-chain risk. Mirror to your internal store, pre-stage to nodes, and pin by digest.