Monitoring
Each Spinnaker microservice is instrumented with numerous metrics exposed via a built in endpoint. The recommended approach for monitoring Spinnaker is to attach the OpenTelemetry (OTEL) Java agent to each microservice via a Kubernetes init container, send telemetry to an OpenTelemetry Collector , and configure that collector to forward data to your preferred monitoring backend.
Note on the Spinnaker Observability Plugin: The Armory Observability Plugin (
armory-plugins/armory-observability-plugin) was previously recommended but is no longer actively developed. New deployments should use the OTEL Java agent approach described on this page.
This approach provides:
- Standard JVM metrics (heap, GC, thread pools) auto-instrumented with no code changes
- Spinnaker application metrics via the agent’s built-in Micrometer extraction — no scraping required
- Distributed tracing across Spinnaker microservices
- Vendor-neutral pipeline: route the same telemetry to Prometheus, Datadog, New Relic, Grafana Cloud, or any OTLP-compatible backend by changing only the collector config
Architecture Overview
Spinnaker microservices
(OTEL Java agent injected via init container)
│
│ OTLP (gRPC or HTTP)
▼
OpenTelemetry Collector
│
├──▶ Prometheus (remote write or scrape)
├──▶ Datadog
├──▶ New Relic
└──▶ Any OTLP-compatible backend
The OTEL Java agent includes built-in support for extracting
Micrometer
metrics from the JVM. Enabling this
instrumentation causes the agent to collect Spinnaker’s internal application
metrics and emit them via OTLP alongside standard JVM and HTTP metrics — no
separate Prometheus scrape of /spectator/metrics is required.
Step 1: Add the OTEL Agent as a Kustomize Component
The recommended way to inject the OTEL agent is as a Kustomize component that patches each service deployment to add an init container, a shared volume, and the required environment variables. This requires no changes to the base service images.
Create components/otel-agent/kustomization.yml in your spinnaker-kustomize
directory:
apiVersion: kustomize.config.k8s.io/v1alpha1
kind: Component
patches:
- patch: |-
- op: add
path: /spec/template/spec/initContainers
value:
- name: otel-agent-init
image: otel/autoinstrumentation-java:2.27.0
command: ["cp", "/javaagent.jar", "/otel/opentelemetry-javaagent.jar"]
volumeMounts:
- name: otel-agent
mountPath: /otel
- op: add
path: /spec/template/spec/volumes/-
value:
name: otel-agent
emptyDir: {}
- op: add
path: /spec/template/spec/containers/0/volumeMounts/-
value:
name: otel-agent
mountPath: /otel
- op: add
path: /spec/template/spec/containers/0/env/-
value:
name: JAVA_TOOL_OPTIONS
value: "-javaagent:/otel/opentelemetry-javaagent.jar"
- op: add
path: /spec/template/spec/containers/0/env/-
value:
name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "http://otel-collector:4317"
- op: add
path: /spec/template/spec/containers/0/env/-
value:
name: OTEL_EXPORTER_OTLP_PROTOCOL
value: grpc
- op: add
path: /spec/template/spec/containers/0/env/-
value:
name: OTEL_METRICS_EXPORTER
value: otlp
- op: add
path: /spec/template/spec/containers/0/env/-
value:
name: OTEL_TRACES_EXPORTER
value: otlp
- op: add
path: /spec/template/spec/containers/0/env/-
value:
name: OTEL_LOGS_EXPORTER
value: none
- op: add
path: /spec/template/spec/containers/0/env/-
value:
name: OTEL_INSTRUMENTATION_MICROMETER_ENABLED
value: "true"
target:
group: apps
version: v1
kind: Deployment
labelSelector: "app.kubernetes.io/part-of=spinnaker"
The OTEL_INSTRUMENTATION_MICROMETER_ENABLED=true flag activates the agent’s
built-in Micrometer instrumentation, which reads Spinnaker’s internal metrics
at the JVM level and emits them via OTLP. No additional Spring or application
config is needed.
Service names: By default the agent uses the Deployment name as
service.name. To set it explicitly per service, add individual per-deployment patches that setOTEL_SERVICE_NAME(e.g.clouddriver,orca,gate).
Enable the component in your root kustomization.yml:
components:
- components/otel-agent
# ... other components
Then apply as usual:
kubectl kustomize -o spinnaker.yml
kubectl apply -f spinnaker.yml
Step 2: Deploy the OpenTelemetry Collector
The OTEL Collector is the central hub that receives telemetry from the agents and routes it to your backend(s). Deploy it as a standalone Deployment or DaemonSet depending on your scale.
Kubernetes Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: otel-collector
namespace: spinnaker
spec:
replicas: 1
selector:
matchLabels:
app: otel-collector
template:
metadata:
labels:
app: otel-collector
spec:
containers:
- name: otel-collector
image: otel/opentelemetry-collector-contrib:latest
args: ["--config=/conf/otel-collector-config.yaml"]
ports:
- containerPort: 4317 # OTLP gRPC
- containerPort: 4318 # OTLP HTTP
- containerPort: 8888 # Collector self-metrics
volumeMounts:
- name: otel-collector-config
mountPath: /conf
volumes:
- name: otel-collector-config
configMap:
name: otel-collector-config
---
apiVersion: v1
kind: Service
metadata:
name: otel-collector
namespace: spinnaker
spec:
selector:
app: otel-collector
ports:
- name: otlp-grpc
port: 4317
targetPort: 4317
- name: otlp-http
port: 4318
targetPort: 4318
Collector Configuration
apiVersion: v1
kind: ConfigMap
metadata:
name: otel-collector-config
namespace: spinnaker
data:
otel-collector-config.yaml: |
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
batch:
timeout: 10s
memory_limiter:
check_interval: 1s
limit_mib: 512
exporters:
# Replace with your chosen backend — see Step 4
prometheusremotewrite:
endpoint: "http://prometheus:9090/api/v1/write"
service:
pipelines:
metrics:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [prometheusremotewrite]
traces:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [prometheusremotewrite]
Step 3: Configure a Backend
Replace the exporters section of the collector config with one of the
following. You can fan out to multiple backends simultaneously by listing
them all and referencing each in the pipeline.
Prometheus
Run Prometheus with the remote write receiver enabled
(--web.enable-remote-write-receiver), then use:
exporters:
prometheusremotewrite:
endpoint: "http://prometheus:9090/api/v1/write"
Or expose a scrape endpoint from the collector itself:
exporters:
prometheus:
endpoint: "0.0.0.0:8889"
Datadog
exporters:
datadog:
api:
key: "${DD_API_KEY}"
site: datadoghq.com # or datadoghq.eu
Requires the otel/opentelemetry-collector-contrib image.
New Relic
exporters:
otlp:
endpoint: "https://otlp.nr-data.net:4317"
headers:
api-key: "${NEW_RELIC_LICENSE_KEY}"
Grafana Cloud (OTLP)
exporters:
otlp:
endpoint: "https://otlp-gateway-<region>.grafana.net/otlp"
headers:
authorization: "Basic ${GRAFANA_OTLP_TOKEN}"
Consuming Metrics
Spinnaker Application Metrics (Micrometer)
Spinnaker publishes internal metrics using a multi-dimensional data model
based on “tags”. Each metric has a name and type; each data point is a
numeric value time-stamped at the time of reporting and tagged with a set
of one or more label=value pairs.
With OTEL_INSTRUMENTATION_MICROMETER_ENABLED=true, the OTEL agent reads
these metrics directly from the JVM’s Micrometer registry and emits them via
OTLP. See the
Monitoring Reference
for a full
description of available metrics, tags, and the data model.
Types of Metrics
-
Counters are monotonically increasing values over the lifetime of the process. Use counter differences over a time window to compute rates. Spinnaker also uses a special Timer counter type (always in nanoseconds) that emits complementary
__countand__totalTimeseries. DividetotalTimebycountto get average latency. -
Gauges are instantaneous value readings. Useful for queue sizes, active connections, and similar current-state measurements.
Standard JVM Metrics (from OTEL agent)
The OTEL Java agent automatically emits:
jvm.memory.used/jvm.memory.committed— heap and non-heap usagejvm.gc.duration— GC pause timesjvm.thread.count— live threadshttp.server.request.duration— latency histogram for all HTTP endpointshttp.client.request.duration— latency for outbound HTTP calls
These use OpenTelemetry semantic conventions and are available in any backend without additional configuration.
Dashboards
The spinnaker-mixin project provides pre-built Grafana dashboards that work with Prometheus-backed Spinnaker metrics. You can import these as a starting point and extend them with the standard JVM metrics produced by the OTEL agent.
The dashboards include:
- Per-microservice dashboards — API success/error counts, latency by controller and method, service-specific operational metrics
- Cloud provider dashboards — Spinnaker’s interaction with each configured cloud provider (success rates, latency, resource operations)
- Minimal Spinnaker — a concise overview for quick health assessment