Aller au contenu principal

ClickHouse monitoring and alerting

ClickHouse itself and its backup sidecar each expose Prometheus metrics, off by default. The backup sidecar deserves the closer attention below: its watch loop retries a failed backup on the next scheduled tick instead of stopping, so a persistent failure never crashes the container or shows up in pod status (see Backup freshness).

Requires Prometheus Operator's CRDs

Both ClickHouse's own ServiceMonitor and the backup sidecar's render a ServiceMonitor resource. If Prometheus Operator's CRDs aren't installed in the cluster, enabling either one makes the release fail to install: leave both false until they are.

ClickHouse server metrics

ClickHouse's own metrics (query performance, resource usage) are exposed on the standard http-metrics port. Two settings gate this, both false by default:

clickhouse-server:
metrics:
enabled: true
serviceMonitor:
enabled: true

metrics.enabled turns on the metrics endpoint itself; metrics.serviceMonitor.enabled additionally ships a matching ServiceMonitor for it: both are required to get a scraped target.

Key metrics

ClickHouse exposes multiple metrics; the ones below are the ones that map directly to a guardrail this chart configures by default (see Scheduling and reliability and Other guardrails), so a breach here means a specific, known threshold was hit, not a symptom to interpret from scratch.

MetricTypeMeaning
ClickHouseErrorMetric_TOO_MANY_SIMULTANEOUS_QUERIESCounterA query was rejected outright for exceeding MAX_CONCURRENT_QUERIES (32 by default). ClickHouseMetrics_Query (gauge) tracks the current concurrent count if you want to watch the trend before it gets there.
ClickHouseErrorMetric_TIMEOUT_EXCEEDEDCounterA query was killed for running past MAX_EXECUTION_TIME (55 seconds by default).
ClickHouseMetrics_MemoryTracking / ClickHouseErrorMetric_MEMORY_LIMIT_EXCEEDEDGauge (bytes) / CounterServer-wide memory currently tracked, and how many times a query was killed for exceeding MAX_SERVER_MEMORY_USAGE_TO_RAM_RATIO (0.9 of the pod's memory limit by default).
ClickHouseAsyncMetrics_MaxPartCountForPartitionGaugeThe active-part count of the single worst-case partition across every table. PARTS_TO_DELAY_INSERT/PARTS_TO_THROW_INSERT (1000/3000 by default) check it before accepting an insert into that partition. ClickHouse's own guidance: values above 300 already indicate overload.
ClickHouseProfileEvents_DelayedInserts / ClickHouseProfileEvents_RejectedInsertsCounterEarly warning (an insert was artificially slowed) vs. hard failure (an insert was rejected with Too many parts) for the same parts-per-partition guardrails above.
ClickHouseProfileEvents_ExternalAggregationCompressedBytes / ExternalSortCompressedBytesCounter (bytes)Cumulative data spilled to disk because a query's GROUP BY/ORDER BY crossed MAX_BYTES_RATIO_BEFORE_EXTERNAL_GROUP_BY/_SORT (0.4 by default). A rising rate means queries are increasingly falling back to the slower disk-spill path.
ClickHouseAsyncMetrics_DiskAvailable_defaultGauge (bytes)Free space on the metadata volume. Unlike the filesystem cache, this volume doesn't degrade gracefully if it fills (see What happens if this volume fills up).
Filesystem cache hit ratio has its own metrics

Covered separately in Filesystem cache: ClickHouseProfileEvents_CachedReadBufferReadFromCacheHits/...Misses (the same counters behind that page's SQL query) and ClickHouseMetrics_FilesystemCacheSize/...SizeLimit for how full the cache currently is.

A rising rate on either of these is a hardware-sizing signal, not just an alert

Sustained ExternalAggregationCompressedBytes/...SortCompressedBytes growth or MEMORY_LIMIT_EXCEEDED increases both point to RAM, not CPU, being the actual bottleneck for your aggregation workload. See Hardware recommendations for when to move from ClickHouse's general-purpose RAM/core ratio to their data-warehouse one.

Key thresholds

These assume the chart's default guardrail values above; adjust the literal numbers if you've customized clickhouse.serverConfig. Critical means a query or insert has already failed because of it; warning means you're trending toward one.

ConditionBased onSuggested thresholdSeverity
A query was rejected for too many concurrent queriesClickHouseErrorMetric_TOO_MANY_SIMULTANEOUS_QUERIESAny increaseCritical
A query was killed for running too longClickHouseErrorMetric_TIMEOUT_EXCEEDEDAny increaseCritical
A query was killed for exceeding its memory limitClickHouseErrorMetric_MEMORY_LIMIT_EXCEEDEDAny increaseCritical
An insert was rejected outright (Too many parts)ClickHouseProfileEvents_RejectedInsertsAny increaseCritical
An insert is being throttled, but not yet rejectedClickHouseProfileEvents_DelayedInsertsAny increaseWarning
A partition is approaching the insert-delay thresholdClickHouseAsyncMetrics_MaxPartCountForPartitionAbove 80% of PARTS_TO_DELAY_INSERT (800 by default) for 15 minutesWarning
Queries are increasingly spilling to diskRate of ExternalAggregationCompressedBytes + ExternalSortCompressedBytesSustained non-zero rate for 30 minutesWarning
The metadata volume is projected to fill upClickHouseAsyncMetrics_DiskAvailable_defaultpredict_linear(...[1d]) < 0 (projected to hit zero within 24h)Critical

An example implementing the thresholds above as a Prometheus Operator PrometheusRule:

apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: clickhouse-server
namespace: <namespace>
labels:
release: <your-prometheus-operator-release-label>
spec:
groups:
- name: clickhouse-server
rules:
- alert: ClickHouseTooManyConcurrentQueries
expr: increase(ClickHouseErrorMetric_TOO_MANY_SIMULTANEOUS_QUERIES[5m]) > 0
for: 1m
labels:
severity: critical
annotations:
summary: 'A query was rejected for exceeding the concurrent query limit'
description: 'MAX_CONCURRENT_QUERIES (32 by default) was hit. ClickHouseMetrics_Query shows the current concurrent count if you want to confirm the trend.'

- alert: ClickHouseQueryTimeout
expr: increase(ClickHouseErrorMetric_TIMEOUT_EXCEEDED[5m]) > 0
for: 1m
labels:
severity: critical
annotations:
summary: 'A query was killed for exceeding MAX_EXECUTION_TIME'
description: 'MAX_EXECUTION_TIME is 55 seconds by default. Check slow query logs for what ran long.'

- alert: ClickHouseQueryMemoryLimitExceeded
expr: increase(ClickHouseErrorMetric_MEMORY_LIMIT_EXCEEDED[5m]) > 0
for: 1m
labels:
severity: critical
annotations:
summary: 'A query was killed for exceeding the server memory limit'
description: 'MAX_SERVER_MEMORY_USAGE_TO_RAM_RATIO is 0.9 by default. ClickHouseMetrics_MemoryTracking shows the server-wide trend leading up to this.'

- alert: ClickHouseInsertRejected
expr: increase(ClickHouseProfileEvents_RejectedInserts[5m]) > 0
for: 1m
labels:
severity: critical
annotations:
summary: "An insert was rejected with 'Too many parts'"
description: 'PARTS_TO_THROW_INSERT (3000 by default) was hit for at least one partition. ClickHouseAsyncMetrics_MaxPartCountForPartition shows the worst-case partition if you want to confirm which one.'

- alert: ClickHouseInsertDelayed
expr: increase(ClickHouseProfileEvents_DelayedInserts[5m]) > 0
for: 5m
labels:
severity: warning
annotations:
summary: 'Inserts are being throttled due to a high part count'
description: 'PARTS_TO_DELAY_INSERT (1000 by default) was hit for at least one partition. Not yet a rejection, but merges are falling behind inserts. Left unaddressed, this trends toward ClickHouseInsertRejected.'

- alert: ClickHouseMaxPartCountApproachingLimit
expr: ClickHouseAsyncMetrics_MaxPartCountForPartition > 800
for: 15m
labels:
severity: warning
annotations:
summary: 'A partition is approaching the insert-delay threshold'
description: 'Above 80% of PARTS_TO_DELAY_INSERT (1000 by default). Sustained high insert rate outpacing background merges. Consider batching inserts at the source.'

- alert: ClickHouseExternalSpillIncreasing
expr: rate(ClickHouseProfileEvents_ExternalAggregationCompressedBytes[30m]) > 0 or rate(ClickHouseProfileEvents_ExternalSortCompressedBytes[30m]) > 0
for: 30m
labels:
severity: warning
annotations:
summary: 'Queries are spilling GROUP BY/ORDER BY to disk'
description: 'MAX_BYTES_RATIO_BEFORE_EXTERNAL_GROUP_BY/_SORT (0.4 by default) is being crossed repeatedly, a sign these queries are memory-constrained relative to their data volume.'

- alert: ClickHouseMetadataDiskWillFillUp
expr: predict_linear(ClickHouseAsyncMetrics_DiskAvailable_default[1d], 86400) < 0
for: 10m
labels:
severity: critical
annotations:
summary: 'The metadata volume is projected to fill up within 24 hours'
description: 'Unlike the filesystem cache, this volume does not degrade gracefully when full: inserts and merges fail outright. See Metadata volume in the sizing guide.'

ClickHouseInsertRejected and its threshold choices mirror Altinity's own clickhouse-operator reference alerting rules (ClickHouseRejectedInsert/ClickHouseDelayedInsertThrottling/ClickHouseMaxPartCountForPartition), and ClickHouseMetadataDiskWillFillUp's predict_linear pattern mirrors their ClickHouseDiskUsage rule. Both are scoped down here to the settings this chart actually configures. Most of Altinity's other rules (replica lag, ZooKeeper/Keeper sessions, Kafka, Distributed tables) don't apply: this chart runs ClickHouse single-node, without replication, Kafka, or sharding.

Backup sidecar metrics

The backup sidecar fails silently: do not rely on the pod's Kubernetes status

A failed backup does not crash the container, does not restart the pod, and does not turn any Kubernetes-level health indicator red. The clickhouse pod can show Running/Ready with 0 restarts while backups have been failing for days. The only reliable way to know is to build alerting on the Prometheus metrics below.

Metrics endpoint

The backup sidecar exposes Prometheus-format metrics on its own REST API, on the backup-rest port (7171), at /metrics. Like every other route on that API, it requires the same HTTP Basic Auth credentials as the rest of the backup API (API_USERNAME/API_PASSWORD, see Prerequisites): an unauthenticated scrape gets a 401.

To check it manually before wiring up Prometheus: API_USERNAME/API_PASSWORD are already injected into the clickhouse-backup container itself (it needs them to enforce this same auth on incoming requests), so there's no need to pull them from the secret separately:

kubectl exec -n <namespace> <clickhouse-pod> -c clickhouse-backup -- \
sh -c 'curl -s -u "$API_USERNAME:$API_PASSWORD" http://localhost:7171/metrics'

Scraping with Prometheus Operator

Set clickhouse.backup.metrics.enabled: true to have the chart ship a matching ServiceMonitor for the backup-rest port automatically: it sources both the username and password from the same clickhouse-credentials secret already used elsewhere (see Prerequisites):

clickhouse:
backup:
metrics:
enabled: true

Key metrics

Every scheduled tick (full or incremental) runs as a create_remote operation, whether or not that day's full backup also triggers a rebase (see Understanding the backup schedule):

MetricTypeMeaning
clickhouse_backup_last_create_remote_statusGauge0=failed, 1=success, 2=unknown. The outcome of the most recent scheduled backup attempt, full or incremental.
clickhouse_backup_last_create_remote_finishGauge (unix timestamp)Timestamp of the most recent attempt, success or failure. Going stale means the watch loop itself has stopped ticking, not just that a backup failed.
clickhouse_backup_successful_create_remotes / clickhouse_backup_failed_create_remotesCounterRunning counters, useful for a failure-rate alert over a rolling window.
clickhouse_backup_last_rebase_status / clickhouse_backup_last_rebase_finishGaugeSame shape, for the weekly rebase step that runs after a full backup once a previous chain already exists. Stays unset for the first ~2 weeks after a fresh install, since full_type: rebase only applies from the second full cycle onward.
clickhouse_backup_in_progress_commandsGaugeHow many backup operations are currently running. Stuck above 0 for far longer than a backup normally takes usually means a hung operation.
clickhouse_backup_number_backups_remote / clickhouse_backup_last_backup_size_remoteGaugeChain count and size currently on remote storage, a basic sanity check that the chain count/size hasn't unexpectedly collapsed.

Key thresholds

These thresholds are metric-agnostic: they apply however the metrics above are collected, whether that's Prometheus, Datadog, Grafana Cloud, or any other monitoring solution. They assume the chart's default schedule (daily create_remote, weekly rebase); adjust them if you've customized WATCH_SCHEDULES.

ConditionBased onSuggested thresholdSeverity
No backup attempt (full or incremental) recentlyTime since clickhouse_backup_last_create_remote_finishMore than 36 hours (1.5x the default daily cadence)Critical
The last backup attempt failedclickhouse_backup_last_create_remote_statusEqual to 0 (failed) for 15 minutes straightCritical
No weekly full-backup rebase recentlyTime since clickhouse_backup_last_rebase_finishMore than 10 days (only meaningful once a second full cycle has run, expect this to be stale for the first two weeks after a fresh install)Warning
A backup operation has been running too longclickhouse_backup_in_progress_commandsGreater than 0 for 2 hours straightWarning
The most recent remote backup reports a size of zeroclickhouse_backup_last_backup_size_remote and clickhouse_backup_number_backups_remoteSize equal to 0 while at least one backup already exists remotelyCritical

An example implementing the thresholds above as a Prometheus Operator PrometheusRule:

apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: clickhouse-backup
namespace: <namespace>
labels:
release: <your-prometheus-operator-release-label>
spec:
groups:
- name: clickhouse-backup
rules:
- alert: ClickHouseBackupStalled
expr: time() - clickhouse_backup_last_create_remote_finish > 36 * 3600
for: 10m
labels:
severity: critical
annotations:
summary: 'No backup attempt (full or incremental) in over 36 hours'
description: 'The clickhouse-backup sidecar has not attempted a backup in more than 36 hours (1.5x the default daily cadence). The watch loop may have stopped, or its config may be invalid. Pod status will not show this: check the sidecar logs directly.'

- alert: ClickHouseBackupFailing
expr: clickhouse_backup_last_create_remote_status == 0
for: 15m
labels:
severity: critical
annotations:
summary: 'The last backup attempt failed'
description: 'clickhouse_backup_last_create_remote_status is 0 (failed). Check the sidecar logs for the error; clickhouse-backup retries on the next scheduled tick, it does not stop or crash on its own.'

- alert: ClickHouseFullBackupStalled
expr: time() - clickhouse_backup_last_rebase_finish > 10 * 24 * 3600
for: 10m
labels:
severity: warning
annotations:
summary: 'No weekly full-backup rebase in over 10 days'
description: 'Only meaningful once a second full cycle has run. Expect this to be stale for the first two weeks after a fresh install, since full_type: rebase only applies from the second full backup onward.'

- alert: ClickHouseBackupStuck
expr: clickhouse_backup_in_progress_commands > 0
for: 2h
labels:
severity: warning
annotations:
summary: 'A backup operation has been running for over 2 hours'
description: 'clickhouse_backup_in_progress_commands has stayed above 0 for 2 hours straight, longer than a scheduled full or incremental backup normally takes. Check the sidecar logs for a hung operation. Raise the threshold if your data volume makes 2 hours a normal backup duration.'

- alert: ClickHouseBackupSizeZero
expr: clickhouse_backup_last_backup_size_remote == 0 and clickhouse_backup_number_backups_remote > 0
for: 10m
labels:
severity: critical
annotations:
summary: 'The most recent remote backup reports a size of zero'
description: 'At least one backup exists on remote storage, but the most recent one reports 0 bytes, likely a broken or empty backup rather than a real one. Check the sidecar logs and verify the backup with `clickhouse-backup list remote`.'

ClickHouseBackupStalled's 36-hour window is deliberately generous (1.5x the 24-hour default cadence) to avoid flapping on a single delayed tick, matching the convention clickhouse-backup's own maintainers use in their reference alerting rules for the ClickHouse Operator. ClickHouseBackupSizeZero mirrors the same reference rule set's ClickHouseRemoteBackupSizeZero.