Monitoring with Prometheus
The scheduler and the artifact cache both serve Prometheus metrics at
/metrics on their HTTP port. You can use them to see how busy your workers
are, how many tasks are being run and how well the cache is doing. Worker
counts come from the scheduler, so there is no need to scrape the workers
themselves.
Scraping the services
Metrics are served on the HTTP listener, not the gRPC port. The scheduler
listens on port 8080 by default. The cache serves metrics on whichever HTTP
or HTTPS listener it is configured with. See
Scheduler and Cache
for listener and TLS settings.
Listener addresses are written as tcp:// URLs, for example
listen_http: "tcp://:8080". They can also be set with JOLT_LISTEN_HTTP
for the scheduler and JOLT_CACHE_LISTEN_HTTP for the cache. The cache uses
TLS unless JOLT_CACHE_INSECURE=true (or insecure: true in its
configuration file).
Here is a Prometheus configuration that scrapes a scheduler and a cache every
15 seconds, both on plain HTTP port 8080:
scrape_configs:
- job_name: jolt-scheduler
scrape_interval: 15s
metrics_path: /metrics
static_configs:
- targets: ["scheduler.example.com:8080"]
labels:
cluster: jolt-production
- job_name: jolt-cache
scrape_interval: 15s
metrics_path: /metrics
static_configs:
- targets: ["cache.example.com:8080"]
labels:
cluster: jolt-production
If the cache uses HTTPS, add scheme: https and a tls_config with your
CA certificate. Keep the metrics endpoints on an internal network rather than
exposing them publicly.
In Kubernetes, scrape each scheduler and cache pod directly, for example with a
PodMonitor or ServiceMonitor if you use Prometheus Operator. Don’t
scrape a load-balanced service address that fronts several replicas. Each scrape
could then hit a different replica, and the counters would jump around.
The cluster label in the example is optional, but it helps if you run more
than one Jolt deployment. The queries below assume a single cluster. Add
{cluster="jolt-production"} to them if you have several.
You can check that an endpoint works with curl:
curl --fail http://scheduler.example.com:8080/metrics
curl --fail http://cache.example.com:8080/metrics
Prometheus also records an up metric for every target, so
up{job="jolt-scheduler"} == 0 tells you when the scheduler can’t be
scraped. It only tells you that the endpoint answered, not that builds are
working.
Gauges and counters
Gauges show a current value, such as the number of connected workers or the
cache size. Counters only go up and start over from zero when the service
restarts. Use rate() or increase() on counters rather than looking at
the raw value. When you have several instances, apply rate() first and
sum() second, so that a restart of one instance doesn’t skew the result.
Gauges are sampled at scrape time, so anything shorter than the scrape interval may be missed. The cache gauges are rebuilt from storage when the cache restarts, but the hit and miss counters start over.
Scheduler metrics
Metric |
Type |
Description |
|---|---|---|
|
Gauge |
The total number of builds currently running. |
|
Counter |
The total number of completed builds. |
|
Gauge |
The total number of tasks currently queued. |
|
Gauge |
The total number of tasks currently running. |
|
Counter |
The total number of failed tasks. |
|
Counter |
The total number of successful tasks. |
|
Counter |
The total number of completed tasks. |
|
Gauge |
The total number of workers currently connected. |
|
Gauge |
The number of connected workers by OS, arch, and platform label. |
|
Gauge |
The number of connected workers unavailable for scheduling by OS, arch, and platform label, including workers awaiting acknowledgement. |
The task counters go up once per status update, not once per task, so a task can be counted more than once. Use them for rates and trends rather than exact task counts.
To see how many tasks finish per second:
sum(rate(jolt_scheduler_tasks_total[5m]))
Worker metrics
The two per-platform worker gauges have these labels:
Label |
Worker property |
Value |
|---|---|---|
|
|
Operating system, such as |
|
|
CPU architecture, such as |
|
|
The worker’s Jolt labels. |
The values come from the worker’s own platform (JOLT_PLATFORM), not from
JOLT_TASK_PLATFORM. For example, workers started with
JOLT_PLATFORM="label=tiny" are reported as platform_label="tiny" and
are counted separately from label=signing workers on the same OS and
architecture. Hostnames and other properties are left out, so identical workers
are counted together. Every distinct combination of labels creates new series,
so keep the number of label combinations small.
A worker with more than one label is reported with all of its labels, sorted
and separated by commas. JOLT_PLATFORM="label=tiny label=signing" becomes
platform_label="signing,tiny". The worker is counted once under that
combination, not once per label, so platform_label="tiny" won’t include it.
The same applies to a worker with more than one OS or architecture, which shows
up as, for example, node_os="linux,windows". A comma or backslash inside a
label is escaped with a backslash, so label=signing,tiny becomes
platform_label="signing\\,tiny".
If a worker doesn’t have a property, the label is empty, for example
platform_label="".
A worker is active from the moment it is assigned a build until it is free to take another one, even if it isn’t running a task at that moment. The active count therefore shows how much of the pool is assigned work, not how much CPU is being used.
Each OS, architecture and label combination gets a series as soon as a matching
worker connects, and the series goes away when the last such worker
disconnects. When no workers are connected, jolt_scheduler_workers is
0 and the per-platform gauges have no series at all.
There is no metric for idle workers, but you can subtract one gauge from the other:
jolt_scheduler_workers_by_platform - jolt_scheduler_workers_active_by_platform
The fraction of workers in use on each platform:
jolt_scheduler_workers_active_by_platform / jolt_scheduler_workers_by_platform
The same fraction per label, across all operating systems, architectures and schedulers:
sum by (platform_label) (jolt_scheduler_workers_active_by_platform)
/
sum by (platform_label) (jolt_scheduler_workers_by_platform)
Cache metrics
Metric |
Type |
Description |
|---|---|---|
|
Gauge |
Artifacts in the cache. |
|
Gauge |
Total size of the artifacts in the cache. This is not the free disk space. |
|
Counter |
Artifacts removed to stay under the size limit. |
|
Counter |
Lookups that found the artifact. |
|
Counter |
Lookups that didn’t find the artifact, including artifacts whose file is missing from disk. |
Hits and misses are counted for both HTTP and gRPC requests. A client usually
checks whether an artifact exists before downloading it, so one download often
counts as two hits. The size limit is not enforced right away, so
jolt_cache_size_bytes can briefly go over it.
The cache hit ratio across all caches:
sum(rate(jolt_cache_hits_total[5m]))
/
(sum(rate(jolt_cache_hits_total[5m])) + sum(rate(jolt_cache_misses_total[5m])))
The query returns no value when there were no lookups in the interval.
Evictions per second across all caches:
sum(rate(jolt_cache_evictions_total[5m]))
Jolt doesn’t report CPU, memory or disk usage. Use your normal host or Kubernetes monitoring for those.