- An OTel Collector receives OTLP from the engine pods.
- Metrics are exposed for Prometheus to scrape (
ServiceMonitor) and/or pushed to an external Prometheus-compatible endpoint (remoteWrite). - Traces are sent to an in-cluster Tempo, an external backend (e.g. Grafana Cloud), or both.
observability.enabled=true.
The chart deploys the Collector and (optionally) Tempo. It does not ship Grafana or
any dashboards — bring your own Grafana and point it at Prometheus (metrics) and Tempo
(traces).
Prerequisites
The chart creates custom resources but does not install the operators that reconcile them. Install these first:| Operator | Provides | Needed for |
|---|---|---|
| OpenTelemetry Operator | OpenTelemetryCollector CRD | the Collector (always, when observability is enabled) |
| Prometheus Operator | ServiceMonitor CRD | in-cluster metric scraping (prometheus.serviceMonitor.enabled) |
| Tempo Operator | TempoMonolithic CRD | an in-cluster trace backend (traces.tempoCR.enabled) |
Quickstart — metrics only
The smallest useful config: a Collector + aServiceMonitor, no trace backend. Put it in
a values file:
observability-values.yaml
OpenTelemetryCollector CR (the OTel Operator reconciles it into a
Collector Deployment) and a ServiceMonitor. The engine is auto-wired to send OTLP to
the Collector — confirm:
Collector ports
| Port | Service | Purpose |
|---|---|---|
4317 / 4318 | …-collector | OTLP gRPC / HTTP receiver — the engine sends here |
8889 | …-collector | Prometheus exporter — LMCache application metrics, for scraping (empty until the engine sends data) |
8888 | …-collector-monitoring | the Collector’s own internal metrics (otelcol_*) |
Exporting metrics
Two independent options — use either or both: Scrape in-cluster (ServiceMonitor) — your Prometheus discovers and scrapes the Collector:remoteWrite) — e.g. Grafana Cloud. Store the auth
header in a Secret and reference it from the Collector’s environment (see
Authenticating to external backends):
Exporting traces
The engine emits traces once observability is enabled; they need a backend to land in. In-cluster Tempo (traces.tempoCR.enabled) — the chart creates a TempoMonolithic CR
(reconciled by the Tempo Operator) and wires the Collector to export to it:
tempoCR and externalEndpoint together and the Collector fans traces
out to both backends simultaneously. Without any trace backend, traces are only visible in
the Collector’s debug log (below).
Authenticating to external backends
Never put tokens invalues.yaml or --set. Store them in a Secret as full auth headers,
mount it into the Collector via otelCollector.envFrom, and reference the env vars with
${env:VAR} in the endpoint headers:
envFrom), and
${env:GC_TRACES_AUTH} / ${env:GC_METRICS_AUTH} in the values above resolve at runtime —
so the token never appears in the chart values or the rendered CR.
Debug exporter
For development,observability.otelCollector.debug=true adds a debug exporter that logs
every received metric and trace to the Collector’s stdout (verbosity detailed). Useful
for confirming data shape; turn it off in production (it’s noisy).
Verify telemetry is flowing
Telemetry is only produced when the engine does work — send inference through vLLM (see the end-to-end example) first. Metrics export on an interval (~60 s), so allow a minute after traffic. The source of truth is the Collector’s own counters, not log volume:otelcol_receiver_accepted_metric_points / _spans confirms the engine’s
OTLP reached the Collector.
You can also see the engine’s application metrics on the Prometheus exporter (8889 on
the main …-collector service):
lmcache_mp_* series = the engine is exporting through the Collector. Empty
means no traffic has hit the engine yet. Finally, run the bundled assertion:
Disabling and toggling
observability.enabled=false (the default) produces zero observability resources, and
CRD validation is skipped — so a default install needs none of the operators above. Toggling
is clean: helm upgrade with enabled=true creates the Collector CR; setting it back to
false removes the CR and its Collector pods.
Troubleshooting
otelcol_receiver_accepted_* stays at 0 after traffic
The engine isn’t reaching the Collector. Check, in order:
- The engine CR has the tracing
extraArgs(above). If not, confirmobservability.enabled=truewas applied:helm get values tensormesh-operator -n tensormesh-operator. - The OTLP endpoint resolves — the Collector’s
…-collectorservice on port4317. - The engine pod restarted after the flags were added (its age should post-date enabling observability).
${env:…} references match.
Collector pod never becomes Ready / install fails on a missing CRD
The OpenTelemetry Operator (or Tempo Operator, if tempoCR.enabled) isn’t installed — re-check the prerequisite CRDs.
Next steps
End-to-end example
Full install → inference → benchmark, including generating the traffic that produces telemetry.
Install with Helm
All chart values, including the
observability.* keys.
