[[TOC]]
NOTE: WIP code not production ready
Ambassador microservice1 to handle the communication with a NATS2 message bus. Provides a generic interface for Prometheus to scrape exporter data that may be located behind firewalls or private networks.
Other more flexible solutions to solve this problem exists, however was looking for something small and lightwight with very minimal setup and dependancies.
Similar idea as PushProx3 and Kafka Adapter4 but with NATS as the backend messaging system using it's Request/Reply feature versus the remote write feature used by the Kafka Adapter4.
References:
- https://prometheus.io/docs/concepts/data_model/
- https://github.com/prometheus/prometheus/wiki/Default-port-allocations
- https://github.com/golang-standards/project-layout
- https://docs.dapr.io/
Example diagram showing targets behind a network and connecting out to a NATS system to communicate.
flowchart LR
subgraph nats_cluster[NATS Cluster]
server1 <--> server2
end
subgraph node1[Target 1]
node1_ambassador[nats_ambassador]
node1_node[node_exporter] <---> node1_ambassador
node1_mysql[mysql_exporter] <---> node1_ambassador
end
subgraph node2[Target 2]
node2_ambassador[nats_ambassador]
node2_node[node_exporter] <---> node2_ambassador
node2_postgres[postgres_exporter] <---> node2_ambassador
end
%% Prometheus setup
subgraph prometheus[Prometheus Server]
monitor_ambassador[nats_ambassador]
node_host_node1 <---> monitor_ambassador
node_host_node2 <---> monitor_ambassador
postgres_host_node2 <---> monitor_ambassador
mysql_host_node1 <---> monitor_ambassador
subgraph scraper
%% Prometheus Scraper Jobs
subgraph job_node
node_host_node1[Target 1]
node_host_node2[Target 2]
end
subgraph job_postgres
postgres_host_node2[Target 2]
end
subgraph job_mysql
mysql_host_node1[Target 1]
end
end
end
%% Request/Reply
monitor_ambassador <== publish request<br>get reply ==> nats_cluster
%% Request to Exporter
node1_ambassador <== request/reply ==> nats_cluster
node2_ambassador <== request/reply ==> nats_cluster
%% Subscribe events
node1_ambassador -. subscribe ..-> nats_cluster
node2_ambassador -. subscribe ..-> nats_cluster
System modules required to be install on OS to complete install and run the application is Go build tools, details can be found for your platform here.
- Go build tools - https://go.dev/
- Existing Prometheus scrapper - https://prometheus.io/
- Existing NATS system - https://nats.io/
-
Clone repo
git clone <this repo url>
-
Change into cloned repos directory
cd <this repo name>
-
Build go binaries with the following command, this will place files in the
bin/*
directory.make build
NOTE The
Makefile
build will override theGOBIN
environment to force install of binaries into thebin/*
directory. Themake
process will look forcmd/*/main.go
and create binary named after the directory. -
Run script after configuration (detailed below)
Below is a simple configuration with a per-proxy configuration for the
prometheus.yml
file used by Prometheus server/collector. The metrics path
used by the Prometheus NATS Ambassador is /proxy
.
NOTE: If you want to use TLS on the
proxy_url
then put the Prometheus NATS Ambassador behind a reverse proxy.
# https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config
# Each job name must be unique
- job_name: "nats_node_targets"
# Set metrics pull path for NATS Ambassador running locally
proxy_url: http://localhost:8181/
metrics_path: /proxy
static_configs:
- labels:
# Override the "job" label.
job: nats_node
targets:
- target1.example.com:9100
- target2.example.com:9100
- job_name: "nats_node_remote_tls"
# Set metrics pull path for NATS Ambassador via TLS behind a reverse proxy
proxy_url: https://remotes.example.com:8181/
metrics_path: /proxy
static_configs:
- labels:
# Override the "job" label.
job: nats_node
targets:
- target3.example.com:9100
An alternative configuration method with service discovery option.
Service discovery with regards to target configuration has some limitations with what can be set.
Note that the
proxy_url
cannot be set dynamically in the targets file5.
# https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config
# Each job name must be unique
- job_name: "nats_node_exporter_service_discovery"
# Set metrics pull path for NATS Ambassador
proxy_url: http://localhost:8181/
metrics_path: /proxy
file_sd_configs:
- files:
- '/opt/prometheus/config/targets_exporter.json'
File: targets_exporter.json
[
{
"targets": [
"target1.example.com:9100"
],
"labels": {
"job": "nats_node"
}
},
{
"targets": [
"target2.example.com:9100"
],
"labels": {
"job": "nats_node"
}
},
{
"targets": [
"target2.example.com:9187"
],
"labels": {
"job": "nats_postgres"
}
}
]
Listing of configuration items before application can be started.
Should use NATS credentials introduced in NATS 2.0, refer to the link below.
References:
NATS communication is via publish/subscribe subjects (also known as topics). By
default the NATS Ambassador will use the base subject io.prometheus.exporter.
when building the publishing requests.
Note the
.
at the end when defining base subject, refer to template below.
If something else is desired for your setup, this can be changed via CLI option
-subjbase
. This document we'll use the default base subject of
io.prometheus.exporter.
.
In light of NATS subject6 naming requirements, the system will
default replace the incoming hostname .
with _
. This is to limit the number
of sub-levels and for now to keep it one subject per-target/host. If you do not
want this behaviour set -subjfmt
to fwd
or rev
. The exporter is defined
by the port requested and referenced to Prometheus default port
allocations7.
Example:
- Hosts:
target1.example.com
- NATS Subject:
- Template:
<base subject><host>.<port of exporter>
- Default:
io.prometheus.exporter.target1_example_com.9100
fwd
Format:io.prometheus.exporter.target1.example.com.9100
rev
Format:io.prometheus.exporter.com.example.target1.9100
- Template:
Define NATS subscriptions for Prometheus exporters and the endpoint to pull from once the request is received. One or more array of objects can be set, this JSON format is defined by the programmatic approached documented by the dapr programmatic subscription method8. The topic JSON key should match what was set above for subject base and format.
[
{
"pubsubname": "node_exporter",
"topic": "io.prometheus.exporter.target1_example_com.9100",
"route": {
"default": "http://target1.localnet:9100/metrics",
"rules": []
}
},
{
"pubsubname": "node_exporter",
"topic": "io.prometheus.exporter.target2_example_com.9100",
"route": {
"default": "http://target2.localnet:9100/metrics",
"rules": []
}
}
]
NOTE: the JSON keys for route rules are ignored currently.
Once all files are configured, the script can be started up. There are 2 modes
that can be started up as, with or without a subscriptions.json
file.
- Purpose:
- Connect to NATS message bus and provide HTTP interface for Prometheus scraper and to listen for NATS requests on subscribed subjects for exporter response.
- Usage:
prometheus-nats-ambassador -creds /nats/cred/file/user.creds \ -urls nats://nats.example.com:4222 \ -subs /sub/file/loc/subscriptions.json \ -listen localhost:8181
- Purpose:
- Connect to NATS message bus and provide HTTP interface for Prometheus scraper.
- Usage:
prometheus-nats-ambassador -creds /nats/cred/file/user.creds \ -urls nats://nats.example.com:4222 \ -listen localhost:8181
Testing files will be located in cmd/<app>/main_test.go
, need to include CI
setup where a NATS server is setup with docker or other means automatically.
For now only testing if version is proper semver.
Footnotes
-
https://learn.microsoft.com/en-us/azure/architecture/patterns/ambassador ↩
-
https://github.com/prometheus/prometheus/issues/9074#issuecomment-887616786 ↩
-
https://github.com/nats-io/nats-architecture-and-design/blob/main/adr/ADR-6.md ↩
-
https://github.com/prometheus/prometheus/wiki/Default-port-allocations ↩
-
https://docs.dapr.io/developing-applications/building-blocks/pubsub/subscription-methods/#programmatic-subscriptions ↩