Skip to main content

CockroachDB

Send metrics from CockroachDB to Oodle through an OpenTelemetry Collector.

Every CockroachDB node serves Prometheus-format metrics at /_status/vars on its HTTP port. The endpoint is on by default, so the cluster itself needs no configuration change. The collector scrapes each node and forwards the metrics to Oodle over OTLP.

Oodle ships four CockroachDB dashboards and twelve recommended monitors, both provisioned in one click from the integration tile.

Prerequisites

  • A running CockroachDB cluster, with each node's HTTP port reachable from the collector
  • OODLE_INSTANCE: Your Oodle instance ID. Go to the Settings icon -> API Keys page in your Oodle UI to find it. (Oodle UI links: ap1, us1)
  • OODLE_API_KEY: Your Oodle API key for authentication. Go to the Settings icon -> API Keys in your Oodle UI to choose an appropriate key. (Oodle UI links: ap1, us1)
  • An Oodle account (navigate to ap1, us1 to start setup)

OTel Collector configuration

Install the OpenTelemetry Collector Contrib distribution. The contrib build provides the prometheus receiver that scrapes the CockroachDB nodes. The core build does not include it.

Save the following as otel-collector-config.yaml. List one target per node, and set crdb_cluster to a name for the cluster.

receivers:
# Every CockroachDB node serves Prometheus-format metrics at
# /_status/vars on its HTTP port.
prometheus:
config:
scrape_configs:
- job_name: "cockroachdb"
scrape_interval: 15s
metrics_path: "/_status/vars"
static_configs:
- targets: ["roach1:8080"]
labels:
crdb_node: "roach1"
- targets: ["roach2:8080"]
labels:
crdb_node: "roach2"
- targets: ["roach3:8080"]
labels:
crdb_node: "roach3"
relabel_configs:
# The dashboards and monitors group by crdb_cluster, so several
# clusters can share one Oodle instance without their series
# colliding.
- target_label: "crdb_cluster"
replacement: "my-cluster"

processors:
memory_limiter:
check_interval: 1s
limit_mib: 512
spike_limit_mib: 128

# Every rewrite below is CockroachDB-specific, so each one is guarded on the
# scrape job. The Prometheus receiver maps job_name to the service.name
# resource attribute, which is also what Oodle renders as the job label.
# Without the guard, any other scrape job in this pipeline would have its
# metrics renamed, its job label overwritten, and its scope stripped.
transform/crdb:
metric_statements:
# CockroachDB uses bare metric names such as ranges, replicas, and
# capacity_used. Namespace them with crdb_ so they cannot collide with
# other sources in the same Oodle instance. up and scrape_* keep their
# standard names: the receiver reports those about the scrape, not
# CockroachDB, and the Node Down monitor reads up.
- context: metric
conditions:
- resource.attributes["service.name"] == "cockroachdb"
statements:
- set(name, Concat(["crdb_", name], ""))
where not IsMatch(name, "^crdb_")
and not IsMatch(name, "^(up|scrape_.*)$")

# Oodle turns every OTLP resource attribute into a metric label. The
# Prometheus receiver attaches scrape-target metadata as resource
# attributes, all of which restate "instance", so drop them rather than
# carry six extra labels on every series. service.name is left alone:
# the receiver already set it to the job name.
- context: resource
conditions:
- attributes["service.name"] == "cockroachdb"
statements:
- set(attributes["db.system"], "cockroachdb")
- delete_key(attributes, "net.host.name")
- delete_key(attributes, "net.host.port")
- delete_key(attributes, "server.address")
- delete_key(attributes, "server.port")
- delete_key(attributes, "http.scheme")
- delete_key(attributes, "url.scheme")

# Same idea for the instrumentation scope: the receiver's own Go package
# path is not useful as a label.
- context: scope
conditions:
- resource.attributes["service.name"] == "cockroachdb"
statements:
- set(name, "")
- set(version, "")

batch:
timeout: 5s
send_batch_size: 1024

exporters:
otlphttp/oodle:
endpoint: "https://${env:OODLE_INSTANCE}-otlp.collector.oodle.ai"
headers:
"X-OODLE-INSTANCE": "${env:OODLE_INSTANCE}"
"X-API-KEY": "${env:OODLE_API_KEY}"

service:
pipelines:
metrics:
receivers: [prometheus]
processors: [memory_limiter, transform/crdb, batch]
exporters: [otlphttp/oodle]
Metric names

The bundled dashboards and monitors query the crdb_ prefixed names, so the transform/crdb processor must stay in the pipeline. The raw exposition on /_status/vars and the CockroachDB DB Console keep the unprefixed names.

Sharing the pipeline with other scrape jobs

Each statement is conditioned on service.name == "cockroachdb", which the Prometheus receiver sets from job_name. Other scrape jobs in the same pipeline pass through untouched: they keep their own metric names, their own job label, and their own instrumentation scope.

Labels

Two labels identify a series, and both the dashboards and the monitors depend on them:

LabelSourcePurpose
crdb_clusterrelabel_configs in the scrape jobGroups the nodes of one cluster. Give each cluster its own value.
crdb_nodePer-target labels in the scrape jobIdentifies one node. Use the node's host name.
jobjob_name in the scrape jobMust stay cockroachdb. The dashboards and monitors filter on it.

Run the collector

services:
otel-collector:
# The contrib distribution is required: the core image has no
# prometheus receiver.
image: otel/opentelemetry-collector-contrib:0.114.0
volumes:
- ./otel-collector-config.yaml:/etc/otelcol-contrib/config.yaml
environment:
- OODLE_INSTANCE=${OODLE_INSTANCE}
- OODLE_API_KEY=${OODLE_API_KEY}

Metric volume

CockroachDB publishes about 2,000 data points per node per scrape, and the integration ships all of them. Every family is therefore in Oodle when you go looking for it, and all four dashboards and all twelve monitors resolve without further configuration.

scrape_interval in the scrape job is the lever if that volume matters.

Verify

  1. Go to ap1, us1 and search for crdb_liveness_livenodes. Its value is the number of live nodes in the cluster.
  2. Confirm the labels are present: the series carries job="cockroachdb", a crdb_cluster value, and one crdb_node value per node.
  3. Open the CockroachDB tile (ap1, us1) and provision the dashboards and monitors.

Dashboards

Open the Dashboards tab in the CockroachDB tile and click View Dashboards to import all four. Provisioning overwrites by name, so it is safe to repeat.

DashboardCovers
CockroachDB RuntimeLive node count, CPU, memory, uptime, goroutines, clock offset
CockroachDB SQLQuery rates by statement type, connections, service and execution latency, transactions, full scans
CockroachDB StorageCapacity used and available, live bytes, read amplification, compactions, file descriptors
CockroachDB ReplicationRanges, replicas, leaseholders, under-replicated and unavailable ranges, snapshots, rebalancing

Each dashboard has a Cluster and a Node variable, so one dashboard serves every cluster that reports to the instance.

Monitors

Open the Monitors tab in the tile and click Provision Recommended Monitors. Monitors are matched by name, so your edits are kept and provisioning again restores only the ones you removed.

MonitorSeverityFires when
Node DownCriticalA node stops answering scrapes for 15 minutes
Unavailable RangesCriticalA range has no quorum
Store Disk LowCriticalA store has less than 15% of its capacity available
Cluster Disk LowCriticalThe cluster has less than 20% of its capacity available
Clock Offset Near MaxCriticalA node's clock drifts toward the point where it removes itself from the cluster
Liveness MismatchWarningFewer nodes report as live than answer scrapes
Underreplicated RangesWarningA range has fewer replicas than its zone configuration asks for
Instance FlappingWarningA node restarts more than once in ten minutes
Version MismatchWarningThe cluster runs more than one CockroachDB version for four hours
High Open FD CountWarningA node uses more than 80% of its file descriptor limit
Slow Raft RequestsWarningRaft proposals stay in flight longer than expected
SQL Latency HighWarningThe 99th percentile of SQL service latency passes one second

Metrics collected

Every metric CockroachDB publishes at /_status/vars reaches Oodle. These are the families the dashboards and monitors use.

MetricDescription
crdb_liveness_livenodesNodes the cluster considers live
crdb_sys_uptimeSeconds since the node process started
crdb_sys_cpu_combined_percent_normalizedNode CPU use, normalized across cores
crdb_sys_rssResident memory of the node process
crdb_clock_offset_meannanosMean clock offset from the other nodes
crdb_sql_connsOpen SQL connections
crdb_sql_query_countSQL statements run
crdb_sql_select_count, crdb_sql_insert_count, crdb_sql_update_count, crdb_sql_delete_countSQL statements by type
crdb_sql_failure_countSQL statements that returned an error
crdb_sql_full_scan_countStatements that ran a full table scan
crdb_sql_service_latency_bucket, crdb_sql_exec_latency_bucketSQL latency histograms
crdb_txn_commits, crdb_txn_abortsTransaction outcomes
crdb_capacity, crdb_capacity_used, crdb_capacity_availableStore capacity
crdb_livebytesBytes of live data
crdb_rocksdb_read_amplificationRead amplification of the storage engine
crdb_ranges, crdb_ranges_underreplicated, crdb_ranges_unavailableRange health
crdb_replicas, crdb_replicas_leaders, crdb_replicas_leaseholdersReplica and leaseholder counts
crdb_sys_fd_open, crdb_sys_fd_softlimitOpen file descriptors against the soft limit

Troubleshooting

IssueResolution
The collector logs unknown type "prometheus"Use the otel/opentelemetry-collector-contrib image. The core build has no prometheus receiver.
No metrics arriveConfirm the collector reaches each node's HTTP port (8080 by default) and that /_status/vars answers with curl.
Dashboard panels are emptyConfirm the scrape job is named cockroachdb and that crdb_cluster is set. The panels filter on both.
Series carry extra labelsConfirm the transform/crdb processor is in the metrics pipeline.
Another job's metrics are named crdb_* or labelled job="cockroachdb"Confirm every statement group in transform/crdb keeps its service.name == "cockroachdb" condition.
A monitor never firesConfirm the metric it reads is present in ap1, us1, and that the collector is not dropping it.

Demo

A runnable three-node cluster with the collector and a workload generator is in oodle-onboarding.


Support

If you need assistance or have any questions, please reach out to us through: