Grafana Monitoring Stack
Nachdem ich im Bachelorprojekt gelernt habe verschiedene Metriken von Linux Systemen zu messen und die Daten mit gnuplot in Graphen zu Visualisieren, wollte ich eine allgemeine, einfach einzurichtende Lösung für das Messen und Überwachen von Systemen zusammenstellen. Angefangen mit einfachen Bash Skripten, einer Datenbank und gnuplot habe ich CPU-, RAM- und Festplattenauslastung, die Erreichbarkeit von Services und Verbindungsdaten vom Reverse-Proxy visualisiert.
Dann hat Grafana, mit interaktiven Graphen in denen die Zeitspanne angepasst werden kann und mehr
Informationen zu einem Datenpunkt angezeigt werden kann, gnuplot ersetzt.
Die Daten kamen weiterhin aus Bash Skripten die in eine Postgresql Datenbank schreiben.
Als ich dann die Maschine, die Daten sammelt, von der Maschine, die die Daten
speichert, trennen wollte und versucht habe Metriken zu verallgemeinern, um nicht für
jede neue Metrik das Datenbankschema zu verändern, ist mir aufgefallen, dass ich immer mehr Ideen
vom Grafana Stack neu implementiert habe.
Mit dieser Erkenntnis war ich motiviert mich mehr in die Grafana Welt einzulesen und
sie für mich nutzbar zu machen.
Prometheus + Grafana
Auf den Maschinen die überwacht werden sollen laufen diverse Exporter und stellen
Metriken für Systemressourcen, Docker Containern oder Nginx über eine HTTP Schnittstelle bereit.
Prometheus läuft ebenso auf diesen Maschinen und fragt diese Schnittstellen alle X Sekunden ab.
Prometheus und die Exporter die auf zu überwachenden Maschinen laufen nenne ich Outposts.
Prometheus speichert die Daten zwischen und sendet sie dann über das
remote_write
Verfahren an eine zentrale Sammelstelle, das HQ (Headquarter).
Auf dieser Maschine läuft eine weitere Prometheus Instanz um diese Daten entgegenzunehmen
und langfristig zu speichern.
Das HQ muss dafür die Outposts nicht kennen und sie müssen nicht öffentlich erreichbar sein. Die
HTTP Schnittstelle vom HQ Prometheus ist dagegen öffentlich erreichbar und muss entsprechend geschützt werden.
Auf dem HQ laufen außerdem diverse HTTP Probe Jobs die den Statuscode von definierten HTTP Endpoints
überprüfen und an die Prometheus Instanz senden.
Die gleichen Exporter wie in den Outposts können auch im HQ verwendet werden um die Maschine zu
überwachen.
In einer Grafana Instanz, die auch auf dem HQ gehostet ist, können alle Daten von allen Outposts
visualisiert werden. Die einzige benötigte Datenquelle ist die Prometheus Instanz die auf der gleichen
Maschine läuft.
Sicherheit
Die einzelnen Komponenten sind als Docker Images verfügbar und werden in jeweils einem Compose Projekt für den Outpost und das HQ verwaltet. Die Container kommunizieren über das HTTP Protokoll miteinander. Innerhalb der Outposts existieren sie in einem isolierten Docker Netzwerk und keine Komponente ist von außerhalb erreichbar. Im HQ sind Grafana und Prometheus öffentlich erreichbar. Grafana bringt eine eigene Userverwaltung mit und ist darauf ausgelegt öffentlich gehostet zu werden. Prometheus muss von den Outposts aus erreichbar sein aber bietet von sich aus keine Authorisierungsverfahren. Im Moment verwende ich für die Absicherung Basic Auth über einen Nginx Reverse-Proxy.
Erweiterungen
In Grafanas LGTM-Stack wird zusätzlich zu Grafana und Mimir noch Loki für Log-Aggregation und Tempo für Traces, mit denen eine Anfrage durch die einzelnen Komponenten einer Anwendung verfolgt werden kann, verwendet.
Composefiles und Config
HQ
compose.yaml:
name: promhq
services:
grafana:
image: grafana/grafana
restart: unless-stopped
ports:
- '3000:3000'
volumes:
- /var/opt/grafana:/var/lib/grafana
environment:
- TZ=Europe/Berlin
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=password
- GF_SERVER_DOMAIN=ultrakalteseis.de
- GF_SERVER_ROOT_URL=https://ultrakalteseis.de/grafana/
- GF_SERVER_SERVE_FROM_SUB_PATH=false
- GF_SERVER_PROTOCOL=http
prometheus:
image: prom/prometheus:latest
restart: unless-stopped
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- /var/opt/prometheus/data:/prometheus/data
command: --web.enable-remote-write-receiver --config.file=/etc/prometheus/prometheus.yml
node-exporter:
image: quay.io/prometheus/node-exporter:latest
restart: unless-stopped
volumes:
- '/:/host:ro,rslave'
command:
- '--path.rootfs=/host'
nginx-exporter:
image: nginx/nginx-prometheus-exporter
command: --nginx.scrape-uri=https://ultrakalteseis.de/nginx_status
cadvisor:
image: gcr.io/cadvisor/cadvisor:latest
command:
- '-housekeeping_interval=10s'
- '-docker_only=true'
volumes:
- /:/rootfs:ro
- /var/run:/var/run:rw
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
blackbox-exporter:
image: quay.io/prometheus/blackbox-exporter:latest
volumes:
- ./blackbox.yml:/config/blackbox.yml
command:
- "--config.file=/config/blackbox.yml"
prometheus.yml:
global:
scrape_interval: 15s
scrape_configs:
- job_name: "node_machine12"
scheme: "https"
static_configs:
- targets: ["node-exporter:9100"]
- job_name: "nginx_machine12"
scheme: "http"
static_configs:
- targets: ["nginx-exporter:9113"]
- job_name: "cadvisor-machine12"
static_configs:
- targets: ["cadvisor:8080"]
- job_name: 'blackbox'
metrics_path: /probe
params:
module: [http_2xx] # Look for a HTTP 200 response.
static_configs:
- targets:
- https://url1.de
- https://url2.de/api/
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- source_labels: [__param_target]
target_label: instance
- target_label: __address__
replacement: blackbox-exporter:9115 # The blackbox exporter's real hostname:port.
- job_name: 'blackbox_exporter' # collect blackbox exporter's operational metrics.
static_configs: # this is probably not needed...
- targets: ['blackbox-exporter:9115']
blackbox.yaml
# blackbox.yml
modules:
http_2xx:
prober: http
http:
preferred_ip_protocol: "ip4"
Outpost
compose.yaml:
services:
prometheus:
image: prom/prometheus:latest
restart: unless-stopped
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- ./.remote_write_password:/etc/.remote_write_password
node-exporter:
image: quay.io/prometheus/node-exporter:latest
restart: unless-stopped
volumes:
- '/:/host:ro,rslave'
command:
- '--path.rootfs=/host'
nginx-exporter:
image: nginx/nginx-prometheus-exporter
restart: unless-stopped
command: --nginx.scrape-uri=https://harbour.informatik.hs-bremerhaven.de/nginx_status
cadvisor:
image: gcr.io/cadvisor/cadvisor:latest
restart: unless-stopped
command:
- '-housekeeping_interval=15s'
- '-docker_only=true'
- '--disable_metrics=disk,network,tcp,udp,percpu,sched,process,referenced_memory'
volumes:
- /:/rootfs:ro
- /sys:/sys:ro
- /var/run/user/11687:/var/run:rw
- /var/run/user/11687/docker/containerd/containerd.sock:/var/run/containerd/containerd.sock:ro
- /home/harbour2025/.local/share/docker/:/home/harbour2025/.local/share/docker/
- /etc/machine-id:/etc/machine-id:ro
prometheus.yml:
global:
scrape_interval: 15s
remote_write:
- url: "https://prometheus.url.de/api/v1/write"
basic_auth:
username: "prometheus_remote_write"
password_file: "/etc/.remote_write_password"
scrape_configs:
- job_name: "node_machine17"
scheme: "http"
static_configs:
- targets: ["node-exporter:9100"]
- job_name: "nginx_machine17"
scheme: "http"
static_configs:
- targets: ["nginx-exporter:9113"]
- job_name: "cadvisor_machine17"
static_configs:
- targets: ["cadvisor:8080"]