How It Works: Complete Guide
This guide explains every tool in the observability stack, how they work, how they're configured, and how they connect to each other.
Overview
The observability stack consists of 6 main tools:
| Tool | Role | Purpose |
|---|---|---|
| Prometheus | Metrics collection | Collects and stores metrics from applications |
| Loki | Log storage | Stores and indexes log data |
| Promtail | Log shipping | Reads log files and sends to Loki |
| Alertmanager | Alert routing | Routes alerts to Slack channels |
| Grafana | Visualization | Displays dashboards from Prometheus and Loki |
| Node Exporter | Host metrics | Collects CPU, memory, disk metrics |
Architecture Diagram
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β OBSERVABILITY STACK β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β ββββββββββββββββ βββββββββββββββββββββββββ β
β β Browser β β Django App β β
β β βββββ HTTP βββββββββΆβ obs-django:9000 β β
β ββββββββββββββββ β β β
β β - Serves pages β β
β β - Writes logs β β
β β - Exposes /metrics β β
β βββββββββββββ¬ββββββββββββ β
β β β
β βββββββββββββββββββββββΌββββββββββββββββββββββ β
β β β β β
β βΌ βΌ βΌ β
β βββββββββββββββββββ βββββββββββββββββββ βββββββββββββ β
β β Prometheus β β Promtail β β Sentry β β
β β :9090 β β (log shipper) β β (cloud) β β
β β β β β β β β
β β Pulls /metrics β β Tails log files β β Receives β β
β β every 15s β β β pushes to Lokiβ β errors β β
β ββββββββββ¬βββββββββ ββββββββββ¬βββββββββ βββββββββββββ β
β β β β
β β evaluate β push β
β β rules β logs β
β βΌ βΌ β
β βββββββββββββββββββ βββββββββββββββββββ β
β β Alertmanager β β Loki β β
β β :9093 β β :3100 β β
β β β β β β
β β Routes alerts β β Stores logs β β
β β to Slack β β with labels β β
β ββββββββββ¬βββββββββ ββββββββββ¬βββββββββ β
β β β β
β β notify β query β
β βΌ βΌ β
β βββββββββββββββββββ βββββββββββββββββββ β
β β Slack β β Grafana β β
β β #alerts-* β β :3000 β β
β β β β β β
β β Receives alert β β Dashboards & β β
β β notifications β β log exploration β β
β βββββββββββββββββββ βββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Prometheus
What Is Prometheus?
Prometheus is an open-source monitoring system that collects metrics from your applications. It uses a "pull model" - meaning it scrapes (pulls) data from your app, rather than your app pushing data to it.
How It Works
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β PROMETHEUS WORKFLOW β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Step 1: Application exposes metrics β
β βββββββββββββββββββββββββββββββββββββ β
β β
β Django App β
β β β
β β Exposes /metrics endpoint β
β βΌ β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β GET /metrics β β
β β β β
β β # HELP django_http_requests_total β β
β β # TYPE django_http_requests_total counter β β
β β django_http_requests_total{method="GET"} 1234 β β
β β β β
β β # HELP django_db_query_duration_seconds β β
β β django_db_query_duration_seconds 0.005 β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β Step 2: Prometheus scrapes (pulls) metrics β
β ββββββββββββββββββββββββββββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Prometheus β β
β β β β
β β Every 15 seconds: β β
β β 1. HTTP GET to obs-django:9000/metrics β β
β β 2. Parse response β β
β β 3. Store in TSDB β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β Step 3: Prometheus stores metrics β
β βββββββββββββββββββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Time Series Database (TSDB) β β
β β β β
β β Metric Name Timestamp Value β β
β β βββββββββββββββββββββββββββββββββββββββββββββ β β
β β django_http_...total 10:00:00 1234 β β
β β django_http_...total 10:00:15 1235 β β
β β django_http_...total 10:00:30 1236 β β
β β django_db_...seconds 10:00:00 0.005 β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β Step 4: Prometheus evaluates alert rules β
β βββββββββββββββββββββββββββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Alert Rules β β
β β β β
β β - alert: DjangoDown β β
β β expr: up{job="django"} == 0 β β
β β for: 10s β β
β β β β
β β Every 15 seconds: β β
β β 1. Evaluate expression β β
β β 2. If true for 10s β send alert β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Configuration Explained
File: prometheus/prometheus.yml
# Global settings
global:
scrape_interval: 15s # How often to scrape targets (default: 15 seconds)
evaluation_interval: 15s # How often to evaluate alert rules (default: 15 seconds)
# Alertmanager configuration
alerting:
alertmanagers:
- static_configs:
- targets: ["obs-alertmanager:9093"] # Where to send alerts
# Alert rules location
rule_files:
- /etc/prometheus/rules/*.yml # Load all .yml files in this directory
# What to scrape
scrape_configs:
# Job 1: Prometheus itself (self-monitoring)
- job_name: "prometheus" # Job name
static_configs:
- targets: ["localhost:9090"] # Scrape itself
# Job 2: Django application
- job_name: "django"
static_configs:
- targets: ["obs-django:9000"] # Docker container name and port
metrics_path: /metrics # Endpoint to scrape
# Job 3: Node Exporter (host metrics)
- job_name: "node"
static_configs:
- targets: ["obs-node-exporter:9100"]
# Job 4: Loki (log system metrics)
- job_name: "loki"
static_configs:
- targets: ["obs-loki:3100"]
metrics_path: /metrics
# Job 5: Grafana (UI metrics)
- job_name: "grafana"
static_configs:
- targets: ["obs-grafana:3000"]
metrics_path: /metrics
Key Concepts
| Concept | Meaning |
|---|---|
| Scrape | Pulling metrics from a target |
| Scrape interval | How often to pull (15s) |
| Target | Where to scrape (container:port) |
| Job | Group of targets |
| Metric | A measurement (e.g., request count) |
| Label | Metadata for filtering (e.g., method=GET) |
| TSDB | Time Series Database (storage) |
| Alert rule | Condition that triggers alert |
How to Test
# Check if Prometheus is running
curl http://localhost:9090/-/healthy
# Query a metric
curl 'http://localhost:9090/api/v1/query?query=up'
# View all targets
curl http://localhost:9090/api/v1/targets | jq
# View alert rules
curl http://localhost:9090/api/v1/rules | jq
Official Documentation
Loki
What Is Loki?
Loki is a log aggregation system designed to be cost-effective and easy to operate. Unlike Elasticsearch, Loki only indexes metadata (labels), not the full log text. Think of it as "Prometheus, but for logs."
How It Works
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β LOKI WORKFLOW β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Step 1: Promtail pushes logs β
β βββββββββββββββββββββββββββββ β
β β
β Promtail β
β β β
β β HTTP POST to /loki/api/v1/push β
β β β
β β { β
β β "streams": [{ β
β β "stream": {"app": "django", "level": "INFO"}, β
β β "values": [["1773536578000000000", "GET /todo/ 200"]] β
β β }] β
β β } β
β βΌ β
β β
β Step 2: Loki receives and stores β
β ββββββββββββββββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Loki β β
β β β β
β β 1. Parse request β β
β β 2. Extract labels (app, level) β β
β β 3. Store log line β β
β β 4. Update index β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β Step 3: Loki stores data β
β ββββββββββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Storage Structure β β
β β β β
β β INDEX (Labels) β β
β β βββββββββββββββββββββββββββββββββββββββββββββ β β
β β {app="django"} β chunk_001 β β
β β {app="django", level="ERROR"} β chunk_002 β β
β β {app="django", log_type="access"} β chunk_003 β β
β β β β
β β CHUNKS (Actual log data) β β
β β βββββββββββββββββββββββββββββββββββββββββββββ β β
β β chunk_001: β β
β β [1773536578000000000] "GET /todo/ 200" β β
β β [1773536578000000001] "POST /todo/ 302" β β
β β β β
β β chunk_002: β β
β β [1773536578000000002] "Error: DB timeout" β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β Step 4: Loki serves queries β
β ββββββββββββββββββββββββββ β
β β
β Grafana β GET /loki/api/v1/query?query={app="django"} β
β β
β Response: β
β { β
β "status": "success", β
β "data": { β
β "result": [{ β
β "stream": {"app": "django", "level": "INFO"}, β
β "values": [["1773536578000000000", "GET /todo/ 200"]] β
β }] β
β } β
β } β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Configuration Explained
File: loki/loki-config.yml
# Don't require authentication (development mode)
auth_enabled: false
# Server settings
server:
http_listen_port: 3100 # Port for HTTP API
# Common configuration
common:
instance_addr: 127.0.0.1 # Instance address
path_prefix: /loki # Root directory for Loki data
storage:
filesystem:
chunks_directory: /loki/chunks # Where log data is stored
rules_directory: /loki/rules # Where recording rules are stored
replication_factor: 1 # Number of replicas (1 for single node)
ring:
kvstore:
store: inmemory # Store ring info in memory
# Schema configuration (how data is organized)
schema_config:
configs:
- from: 2024-01-01 # Start date for this schema
store: tsdb # Storage backend
object_store: filesystem # Where to store objects
schema: v12 # Schema version
index:
prefix: index_ # Index file prefix
period: 24h # Rotate index every 24 hours
# Rate limits
limits_config:
retention_period: 30d # Keep logs for 30 days
ingestion_rate_mb: 16 # Max ingestion rate (MB/s)
ingestion_burst_size_mb: 32 # Max burst size (MB)
Key Concepts
| Concept | Meaning |
|---|---|
| Stream | A set of logs with same labels |
| Labels | Metadata tags (app=django, level=ERROR) |
| Chunks | Compressed log data |
| Index | Label-based search index |
| LogQL | Loki query language |
| Push API | HTTP POST to ingest logs |
| Query API | HTTP GET to search logs |
How to Test
# Check if Loki is ready
curl http://localhost:3100/ready
# Push a test log
curl -X POST 'http://localhost:3100/loki/api/v1/push' \
-H 'Content-Type: application/json' \
-d '{"streams":[{"stream":{"app":"test"},"values":[["1773536578000000000","test"]]}]}'
# Query logs
curl 'http://localhost:3100/loki/api/v1/query?query={app="test"}'
# List labels
curl http://localhost:3100/loki/api/v1/label | jq
Official Documentation
Promtail
What Is Promtail?
Promtail is an agent that reads log files and sends them to Loki. Think of it as a "log mailman" - it picks up logs and delivers them.
How It Works
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β PROMTAIL WORKFLOW β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Step 1: Watch log files β
β βββββββββββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β /app/logs/django.log β β
β β β β
β β {"asctime":"2026-03-15", β β
β β "levelname":"INFO", β β
β β "name":"django", β β
β β "message":"GET /todo/ 200"} β β
β β β β
β β β Promtail watches this file (tail -f) β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β Step 2: Parse JSON β
β ββββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Pipeline Stages β β
β β β β
β β Input JSON: β β
β β {"levelname":"INFO","name":"django", β β
β β "message":"GET /todo/ 200"} β β
β β β β
β β After json parsing: β β
β β level = "INFO" β β
β β logger = "django" β β
β β message = "GET /todo/ 200" β β
β β β β
β β After labels extraction: β β
β β Labels: {app="django", env="dev", level="INFO"} β β
β β Log line: "GET /todo/ 200" β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β Step 3: Push to Loki β
β ββββββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β HTTP POST β β
β β β β
β β URL: http://obs-loki:3100/loki/api/v1/push β β
β β β β
β β Body: β β
β β { β β
β β "streams": [{ β β
β β "stream": { β β
β β "app": "django", β β
β β "env": "dev", β β
β β "level": "INFO" β β
β β }, β β
β β "values": [ β β
β β ["1773536578000000000", β β
β β "GET /todo/ 200"] β β
β β ] β β
β β }] β β
β β } β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β Step 4: Track position β
β ββββββββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β positions.yaml β β
β β β β
β β /var/log/django/django.log: β β
β β value: 12345 β Last byte position read β β
β β β β
β β /var/log/django/access.log: β β
β β value: 67890 β Last byte position read β β
β β β β
β β This ensures Promtail doesn't re-send logs β β
β β after restart β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Configuration Explained
File: promtail/promtail-config.yml
# Server settings
server:
http_listen_port: 9080 # Port for metrics endpoint
grpc_listen_port: 0 # Disable gRPC (not needed)
# Position tracking (remembers where it left off)
positions:
filename: /tmp/positions.yaml # File to store read positions
# Where to send logs
clients:
- url: http://obs-loki:3100/loki/api/v1/push # Loki push endpoint
# What files to watch
scrape_configs:
# Job 1: Django application logs (JSON format)
- job_name: django # Job name
static_configs:
- targets:
- localhost # (required but ignored)
labels:
app: django # Label: app=django
env: dev # Label: env=dev
__path__: /var/log/django/django.log # File to watch
pipeline_stages:
- json: # Parse as JSON
expressions:
level: levelname # Extract "levelname" field β "level" label
logger: name # Extract "name" field β "logger" label
message: message # Extract "message" field
- labels: # Convert to Loki labels
level: # Make "level" a searchable label
logger: # Make "logger" a searchable label
# Job 2: Gunicorn access logs
- job_name: gunicorn_access
static_configs:
- targets:
- localhost
labels:
app: django
log_type: access # Label: log_type=access
env: dev
__path__: /var/log/django/access.log
pipeline_stages:
- labels:
log_type:
# Job 3: Gunicorn error logs
- job_name: gunicorn_error
static_configs:
- targets:
- localhost
labels:
app: django
log_type: error # Label: log_type=error
level: ERROR # Label: level=ERROR
env: dev
__path__: /var/log/django/error.log
pipeline_stages:
- labels:
level:
log_type:
Key Concepts
| Concept | Meaning |
|---|---|
__path__ |
File path to watch |
targets |
Required but ignored for file watching |
labels |
Metadata tags added to logs |
pipeline_stages |
How to process log lines |
json |
Parse JSON format |
positions.yaml |
Tracks last read position |
How to Test
# Check Promtail logs
docker logs obs-promtail --tail 20
# Check targets
curl http://localhost:9080/targets | jq
# Verify tailing
docker logs obs-promtail | grep "tail routine"
Official Documentation
Alertmanager
What Is Alertmanager?
Alertmanager receives alerts from Prometheus and routes them to different notification channels (Slack, email, etc.). Think of it as a "dispatcher" that decides where to send messages.
How It Works
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ALERTMANAGER WORKFLOW β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Step 1: Receive alert from Prometheus β
β βββββββββββββββββββββββββββββββββββββ β
β β
β Prometheus β
β β β
β β HTTP POST to /api/v1/alerts β
β β β
β β [ β
β β { β
β β "labels": { β
β β "alertname": "DjangoDown", β
β β "severity": "critical", β
β β "category": "http" β
β β }, β
β β "annotations": { β
β β "summary": "Django is DOWN" β
β β } β
β β } β
β β ] β
β βΌ β
β β
β Step 2: Group alerts β
β ββββββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Alertmanager β β
β β β β
β β group_by: ["alertname", "category"] β β
β β group_wait: 30s β β
β β β β
β β If multiple alerts come within 30s: β β
β β - Group by alertname and category β β
β β - Send one notification per group β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β Step 3: Route to receiver β
β βββββββββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Routing Rules β β
β β β β
β β If category=db β send to #alerts-db β β
β β If category=http β send to #alerts-http β β
β β If category=latencyβ send to #alerts-latency β β
β β If category=infra β send to #alerts-infra β β
β β Otherwise β send to #alerts β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β Step 4: Send to Slack β
β ββββββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Slack Message β β
β β β β
β β π₯ [FIRING] DjangoDown β β
β β β β
β β π¨ CRITICAL β Django is DOWN β β
β β β β
β β π Reason: App unreachable for 10s β β
β β π·οΈ Alert: `DjangoDown` β β
β β π Category: `http` β β
β β π Started: 2026-03-15 10:30:00 UTC β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Configuration Explained
File: alertmanager/alertmanager.yml
# Global settings
global:
resolve_timeout: 5m # Wait 5 minutes before marking as resolved
slack_api_url: "PLACEHOLDER" # Replaced at runtime with SLACK_WEBHOOK_URL
# Routing configuration
route:
group_by: ["alertname", "category"] # Group alerts by name and category
group_wait: 30s # Wait 30s to collect alerts
group_interval: 5m # Wait 5m between groups
repeat_interval: 12h # Repeat alert every 12 hours
receiver: "slack-default" # Default receiver
routes: # Specific routing rules
- match:
category: db # If category=db
receiver: "slack-db" # Send to #alerts-db
- match:
alertname: DjangoDown # If DjangoDown
category: http
receiver: "slack-http"
repeat_interval: 10m # Repeat every 10 minutes
- match:
category: http # Other HTTP alerts
receiver: "slack-http"
- match:
category: latency
receiver: "slack-latency"
- match:
category: infrastructure
receiver: "slack-infra"
# Receivers (notification targets)
receivers:
- name: "slack-default"
slack_configs:
- channel: "#alerts"
username: "Observability Bot"
icon_emoji: ":bell:"
color: '{{ if eq .Status "firing" }}#FF0000{{ else }}#36A64F{{ end }}'
title: '[{{ .Status | toUpper }}] {{ .GroupLabels.alertname }}'
text: |
{{ range .Alerts }}
{{ .Annotations.summary }}
{{ end }}
send_resolved: true
# Inhibit rules (suppress lower severity if higher is active)
inhibit_rules:
- source_match:
severity: "critical"
target_match:
severity: "warning"
equal: ["alertname"]
Key Concepts
| Concept | Meaning |
|---|---|
| Receiver | Where to send (Slack channel) |
| Route | How to match alerts to receivers |
| Group | Combine similar alerts |
| group_wait | Wait before sending |
| repeat_interval | How often to repeat |
| inhibit_rule | Suppress lower severity alerts |
How to Test
# Check Alertmanager status
curl http://localhost:9093/api/v1/status | jq
# View active alerts
curl http://localhost:9093/api/v1/alerts | jq
# Send test alert
curl -X POST http://localhost:9093/api/v1/alerts \
-H "Content-Type: application/json" \
-d '[{"labels":{"alertname":"Test","severity":"warning","category":"http"},"annotations":{"summary":"Test alert"}}]'
Official Documentation
Grafana
What Is Grafana?
Grafana is a visualization platform that creates dashboards from your data. Think of it as a "TV" that shows channels from Prometheus and Loki.
How It Works
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β GRAFANA WORKFLOW β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Step 1: Connect to datasources β
β ββββββββββββββββββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Grafana β β
β β β β
β β Datasources: β β
β β - Prometheus: http://obs-prometheus:9090 β β
β β - Loki: http://obs-loki:3100 β β
β β β β
β β Configured in: β β
β β grafana/provisioning/datasources/datasources.ymlβ β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β Step 2: Query datasources β
β βββββββββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β PromQL (Prometheus) β β
β β β β
β β rate(django_http_requests_total[5m]) β β
β β β β
β β This calculates requests per second over 5 min β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β LogQL (Loki) β β
β β β β
β β {app="django", level="ERROR"} β β
β β β β
β β This returns all error logs from Django β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β Step 3: Display dashboards β
β ββββββββββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Dashboard Panels β β
β β β β
β β ββββββββββββββββ ββββββββββββββββ β β
β β β Request Rate β β Error Rate β β β
β β β βββββ β β βββββ β β β
β β β β β β β β β β β β
β β β β β β β β β β β β
β β β βββββ β β βββββ β β β
β β β 0.5 req/s β β 0.01% β β β
β β ββββββββββββββββ ββββββββββββββββ β β
β β β β
β β ββββββββββββββββ ββββββββββββββββ β β
β β β P95 Latency β β Recent Logs β β β
β β β βββββ β β INFO: GET β β β
β β β β β β β ERROR: ... β β β
β β β βββββ β β β β β
β β β 45.2ms β β β β β
β β ββββββββββββββββ ββββββββββββββββ β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Configuration Explained
File: grafana/provisioning/datasources/datasources.yml
# Datasource provisioning format
apiVersion: 1
# Delete old datasources on startup (clean slate)
deleteDatasources:
- name: Prometheus
orgId: 1
- name: Loki
orgId: 1
# Define datasources
datasources:
# Prometheus datasource
- name: Prometheus # Display name
type: prometheus # Datasource type
uid: prometheus # Unique identifier
orgId: 1 # Organization ID
access: proxy # Access mode (proxy = through Grafana)
url: http://obs-prometheus:9090 # Prometheus URL
isDefault: true # Default datasource
editable: false # Can't edit in UI
version: 1 # Version number
# Loki datasource
- name: Loki
type: loki
uid: loki
orgId: 1
access: proxy
url: http://obs-loki:3100
isDefault: false
editable: false
version: 1
jsonData:
maxLines: 1000 # Max lines to display
Key Concepts
| Concept | Meaning |
|---|---|
| Datasource | Data source connection |
| Panel | Dashboard widget |
| Dashboard | Collection of panels |
| PromQL | Prometheus query language |
| LogQL | Loki query language |
| Provisioning | Auto-configure on startup |
How to Test
# Check Grafana health
curl http://localhost:3000/api/health
# List datasources
curl -u admin:admin http://localhost:3000/api/datasources | jq
# Query Prometheus via Grafana
curl -u admin:admin 'http://localhost:3000/api/datasources/uid/prometheus/api/v1/query?query=up'
Official Documentation
Node Exporter
What Is Node Exporter?
Node Exporter collects host-level metrics (CPU, memory, disk, network). Think of it as a "stethoscope" for your server.
How It Works
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β NODE EXPORTER WORKFLOW β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Step 1: Read host metrics β
β βββββββββββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Node Exporter β β
β β β β
β β Reads from: β β
β β - /proc (CPU, memory, network) β β
β β - /sys (system information) β β
β β - / (disk usage) β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β Step 2: Expose metrics β
β ββββββββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β GET :9100/metrics β β
β β β β
β β # HELP node_cpu_seconds_total β β
β β node_cpu_seconds_total{cpu="0",mode="idle"} β β
β β β β
β β # HELP node_memory_MemTotal_bytes β β
β β node_memory_MemTotal_bytes 8589934592 β β
β β β β
β β # HELP node_filesystem_avail_bytes β β
β β node_filesystem_avail_bytes{mountpoint="/"} β β
β β β β
β β # HELP node_network_receive_bytes_total β β
β β node_network_receive_bytes_total{device="eth0"} β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β Step 3: Prometheus scrapes β
β ββββββββββββββββββββββββ β
β β
β Prometheus β GET :9100/metrics β Store in TSDB β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Docker Configuration Explained
obs-node-exporter:
image: prom/node-exporter:latest
pid: host # Share host's PID namespace
volumes:
- /proc:/host/proc:ro # Mount host /proc (CPU, memory, processes)
- /sys:/host/sys:ro # Mount host /sys (system info)
- /:/rootfs:ro # Mount host filesystem (disk usage)
command:
- "--path.procfs=/host/proc" # Where to read proc
- "--path.sysfs=/host/sys" # Where to read sys
- "--path.rootfs=/rootfs" # Where to read rootfs
- "--collector.filesystem.mount-points-exclude=^/(sys|proc|..." # Exclude system mounts
Key Metrics
| Metric | Description |
|---|---|
node_cpu_seconds_total |
CPU usage by mode |
node_memory_MemAvailable_bytes |
Available memory |
node_memory_MemTotal_bytes |
Total memory |
node_filesystem_avail_bytes |
Available disk space |
node_network_receive_bytes_total |
Network traffic received |
How to Test
# View all metrics
curl http://localhost:9100/metrics
# View CPU metrics
curl http://localhost:9100/metrics | grep node_cpu
# View memory metrics
curl http://localhost:9100/metrics | grep node_memory
Official Documentation
Data Flow Summary
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β COMPLETE DATA FLOW β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β METRICS (Pull Model) β
β βββββββββββββββββββββ β
β β
β Django β GET /metrics β Prometheus β Alertmanager β Slack β
β β β
β Grafana β
β β
β LOGS (Push Model) β
β βββββββββββββββββ β
β β
β Django β Log File β Promtail β POST /loki/api/v1/push β Loki β
β β β
β Grafana β
β β
β HOST METRICS (Pull Model) β
β βββββββββββββββββββββββββ β
β β
β Host System β Node Exporter β GET /metrics β Prometheus β
β β β
β Grafana β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Quick Reference
| Tool | Port | URL | Purpose |
|---|---|---|---|
| Prometheus | 9090 | http://localhost:9090 | Metrics collection |
| Loki | 3100 | http://localhost:3100 | Log storage |
| Alertmanager | 9093 | http://localhost:9093 | Alert routing |
| Grafana | 3000 | http://localhost:3000 | Dashboards |
| Node Exporter | 9100 | http://localhost:9100 | Host metrics |
| pgAdmin | 5050 | http://localhost:5050 | DB admin |
| Django | 9000 | http://localhost:9000 | Application |