This commit is contained in:
Toby
2026-01-27 17:35:45 -05:00
commit 2639af6531
176 changed files with 27104 additions and 0 deletions
@@ -0,0 +1,445 @@
---
name: prometheus-grafana
description: Set up metrics collection and visualization with Prometheus and Grafana. Configure scrape targets, create PromQL queries, build dashboards, and implement alerting. Use when implementing monitoring, metrics collection, or visualization for applications and infrastructure.
license: MIT
metadata:
author: devops-skills
version: "1.0"
---
# Prometheus & Grafana
Collect metrics and visualize system performance with the Prometheus-Grafana stack.
## When to Use This Skill
Use this skill when:
- Setting up metrics collection infrastructure
- Creating monitoring dashboards
- Writing PromQL queries for analysis
- Configuring alerting rules
- Monitoring Kubernetes clusters
## Prerequisites
- Docker or Kubernetes for deployment
- Network access to monitored targets
- Basic understanding of metrics concepts
## Prometheus Setup
### Docker Deployment
```yaml
# docker-compose.yml
version: '3.8'
services:
prometheus:
image: prom/prometheus:v2.48.0
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- ./rules:/etc/prometheus/rules
- prometheus-data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--storage.tsdb.retention.time=15d'
grafana:
image: grafana/grafana:10.2.0
ports:
- "3000:3000"
volumes:
- grafana-data:/var/lib/grafana
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
prometheus-data:
grafana-data:
```
### Configuration
```yaml
# prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
alerting:
alertmanagers:
- static_configs:
- targets:
- alertmanager:9093
rule_files:
- /etc/prometheus/rules/*.yml
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'node'
static_configs:
- targets:
- 'node-exporter:9100'
- job_name: 'applications'
static_configs:
- targets:
- 'app1:8080'
- 'app2:8080'
metrics_path: /metrics
```
## Kubernetes Deployment
### Using Helm
```bash
# Add Prometheus community Helm repo
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
# Install kube-prometheus-stack
helm install prometheus prometheus-community/kube-prometheus-stack \
--namespace monitoring \
--create-namespace \
--set grafana.adminPassword=admin
```
### ServiceMonitor
```yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: myapp
namespace: monitoring
spec:
selector:
matchLabels:
app: myapp
endpoints:
- port: metrics
interval: 30s
path: /metrics
namespaceSelector:
matchNames:
- default
```
## PromQL Queries
### Basic Queries
```promql
# Current CPU usage
node_cpu_seconds_total{mode="idle"}
# Rate of HTTP requests per second
rate(http_requests_total[5m])
# Average response time
avg(http_request_duration_seconds_sum / http_request_duration_seconds_count)
# Memory usage percentage
(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100
```
### Aggregations
```promql
# Sum requests by status code
sum by (status_code) (rate(http_requests_total[5m]))
# Average CPU by instance
avg by (instance) (rate(node_cpu_seconds_total{mode!="idle"}[5m]))
# Top 5 endpoints by request count
topk(5, sum by (endpoint) (rate(http_requests_total[5m])))
# 95th percentile latency
histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))
```
### Time-Based Queries
```promql
# Compare to 1 hour ago
http_requests_total - http_requests_total offset 1h
# Predict disk space in 4 hours
predict_linear(node_filesystem_avail_bytes[1h], 4 * 3600)
# Changes in last 5 minutes
changes(up[5m])
# Average over 24 hours
avg_over_time(http_requests_total[24h])
```
## Alerting Rules
```yaml
# rules/alerts.yml
groups:
- name: application
rules:
- alert: HighErrorRate
expr: |
sum(rate(http_requests_total{status=~"5.."}[5m]))
/ sum(rate(http_requests_total[5m])) > 0.05
for: 5m
labels:
severity: critical
annotations:
summary: "High error rate detected"
description: "Error rate is {{ $value | humanizePercentage }}"
- alert: ServiceDown
expr: up == 0
for: 1m
labels:
severity: critical
annotations:
summary: "Service {{ $labels.instance }} is down"
- alert: HighMemoryUsage
expr: |
(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) > 0.9
for: 5m
labels:
severity: warning
annotations:
summary: "High memory usage on {{ $labels.instance }}"
description: "Memory usage is {{ $value | humanizePercentage }}"
- alert: DiskSpaceLow
expr: |
(node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) < 0.1
for: 5m
labels:
severity: warning
annotations:
summary: "Disk space low on {{ $labels.instance }}"
```
## Alertmanager
```yaml
# alertmanager.yml
global:
resolve_timeout: 5m
slack_api_url: 'https://hooks.slack.com/services/xxx'
route:
receiver: 'slack-notifications'
group_by: ['alertname', 'severity']
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
routes:
- match:
severity: critical
receiver: 'pagerduty'
receivers:
- name: 'slack-notifications'
slack_configs:
- channel: '#alerts'
send_resolved: true
title: '{{ .Status | toUpper }}: {{ .CommonAnnotations.summary }}'
text: '{{ .CommonAnnotations.description }}'
- name: 'pagerduty'
pagerduty_configs:
- service_key: 'xxx'
severity: critical
```
## Grafana Dashboards
### Dashboard JSON Structure
```json
{
"dashboard": {
"title": "Application Metrics",
"panels": [
{
"title": "Request Rate",
"type": "graph",
"targets": [
{
"expr": "sum(rate(http_requests_total[5m])) by (status_code)",
"legendFormat": "{{ status_code }}"
}
],
"gridPos": {"x": 0, "y": 0, "w": 12, "h": 8}
},
{
"title": "Latency P95",
"type": "gauge",
"targets": [
{
"expr": "histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))"
}
],
"gridPos": {"x": 12, "y": 0, "w": 6, "h": 8}
}
]
}
}
```
### Provisioning Dashboards
```yaml
# grafana/provisioning/dashboards/dashboards.yml
apiVersion: 1
providers:
- name: 'default'
orgId: 1
folder: ''
type: file
disableDeletion: false
updateIntervalSeconds: 30
options:
path: /var/lib/grafana/dashboards
```
### Data Source Provisioning
```yaml
# grafana/provisioning/datasources/prometheus.yml
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
access: proxy
url: http://prometheus:9090
isDefault: true
editable: false
```
## Recording Rules
```yaml
# rules/recording.yml
groups:
- name: aggregations
interval: 30s
rules:
- record: job:http_requests:rate5m
expr: sum by (job) (rate(http_requests_total[5m]))
- record: instance:node_cpu:avg_rate5m
expr: |
avg by (instance) (
rate(node_cpu_seconds_total{mode!="idle"}[5m])
)
- record: job:http_latency:p95
expr: |
histogram_quantile(0.95,
sum by (job, le) (rate(http_request_duration_seconds_bucket[5m]))
)
```
## Application Instrumentation
### Go Application
```go
import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
var httpRequests = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "http_requests_total",
Help: "Total HTTP requests",
},
[]string{"method", "endpoint", "status"},
)
func init() {
prometheus.MustRegister(httpRequests)
}
// Expose metrics endpoint
http.Handle("/metrics", promhttp.Handler())
```
### Node.js Application
```javascript
const client = require('prom-client');
const httpRequests = new client.Counter({
name: 'http_requests_total',
help: 'Total HTTP requests',
labelNames: ['method', 'endpoint', 'status']
});
// Middleware
app.use((req, res, next) => {
res.on('finish', () => {
httpRequests.inc({
method: req.method,
endpoint: req.path,
status: res.statusCode
});
});
next();
});
// Expose metrics
app.get('/metrics', async (req, res) => {
res.set('Content-Type', client.register.contentType);
res.end(await client.register.metrics());
});
```
## Common Issues
### Issue: Targets Not Discovered
**Problem**: Prometheus not scraping targets
**Solution**: Check network connectivity, verify target labels
### Issue: High Memory Usage
**Problem**: Prometheus using excessive memory
**Solution**: Reduce retention, use recording rules, limit cardinality
### Issue: Slow Queries
**Problem**: PromQL queries timing out
**Solution**: Use recording rules, limit time ranges, optimize queries
### Issue: Missing Data Points
**Problem**: Gaps in metrics data
**Solution**: Check scrape interval, verify target availability
## Best Practices
- Use recording rules for frequently-used queries
- Limit label cardinality to prevent memory issues
- Set appropriate retention based on storage capacity
- Use histogram metrics for latency measurement
- Implement proper alerting thresholds
- Version control dashboards as code
- Use federation for large-scale deployments
- Regularly review and prune unused metrics
## Related Skills
- [alerting-oncall](../alerting-oncall/) - Alert management
- [loki-logging](../loki-logging/) - Log aggregation
- [kubernetes-ops](../../orchestration/kubernetes-ops/) - K8s monitoring
@@ -0,0 +1,198 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": "-- Grafana --",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"gnetId": null,
"graphTooltip": 0,
"id": null,
"links": [],
"panels": [
{
"datasource": "Prometheus",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{"color": "green", "value": null},
{"color": "yellow", "value": 70},
{"color": "red", "value": 90}
]
},
"unit": "percent"
}
},
"gridPos": {"h": 8, "w": 6, "x": 0, "y": 0},
"id": 1,
"options": {
"reduceOptions": {
"calcs": ["lastNotNull"],
"fields": "",
"values": false
},
"showThresholdLabels": false,
"showThresholdMarkers": true
},
"pluginVersion": "8.0.0",
"targets": [
{
"expr": "100 - (avg(irate(node_cpu_seconds_total{mode=\"idle\"}[5m])) * 100)",
"refId": "A"
}
],
"title": "CPU Usage",
"type": "gauge"
},
{
"datasource": "Prometheus",
"fieldConfig": {
"defaults": {
"color": {"mode": "palette-classic"},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{"color": "green", "value": null},
{"color": "yellow", "value": 70},
{"color": "red", "value": 90}
]
},
"unit": "percent"
}
},
"gridPos": {"h": 8, "w": 6, "x": 6, "y": 0},
"id": 2,
"options": {
"reduceOptions": {
"calcs": ["lastNotNull"],
"fields": "",
"values": false
}
},
"targets": [
{
"expr": "(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100",
"refId": "A"
}
],
"title": "Memory Usage",
"type": "gauge"
},
{
"datasource": "Prometheus",
"fieldConfig": {
"defaults": {
"color": {"mode": "palette-classic"},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"drawStyle": "line",
"fillOpacity": 10,
"lineInterpolation": "linear",
"lineWidth": 1,
"showPoints": "never"
},
"unit": "reqps"
}
},
"gridPos": {"h": 8, "w": 12, "x": 12, "y": 0},
"id": 3,
"options": {
"legend": {"displayMode": "list", "placement": "bottom"},
"tooltip": {"mode": "single"}
},
"targets": [
{
"expr": "sum(rate(http_requests_total[5m])) by (status)",
"legendFormat": "{{status}}",
"refId": "A"
}
],
"title": "Request Rate by Status",
"type": "timeseries"
},
{
"datasource": "Prometheus",
"fieldConfig": {
"defaults": {
"color": {"mode": "palette-classic"},
"custom": {
"drawStyle": "line",
"fillOpacity": 10,
"lineWidth": 1
},
"unit": "s"
}
},
"gridPos": {"h": 8, "w": 12, "x": 0, "y": 8},
"id": 4,
"targets": [
{
"expr": "histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le))",
"legendFormat": "P95",
"refId": "A"
},
{
"expr": "histogram_quantile(0.50, sum(rate(http_request_duration_seconds_bucket[5m])) by (le))",
"legendFormat": "P50",
"refId": "B"
}
],
"title": "Request Latency",
"type": "timeseries"
},
{
"datasource": "Prometheus",
"fieldConfig": {
"defaults": {
"color": {"mode": "palette-classic"},
"custom": {
"drawStyle": "line",
"fillOpacity": 10
},
"unit": "percent"
}
},
"gridPos": {"h": 8, "w": 12, "x": 12, "y": 8},
"id": 5,
"targets": [
{
"expr": "sum(rate(http_requests_total{status=~\"5..\"}[5m])) / sum(rate(http_requests_total[5m])) * 100",
"legendFormat": "Error Rate",
"refId": "A"
}
],
"title": "Error Rate",
"type": "timeseries"
}
],
"schemaVersion": 30,
"style": "dark",
"tags": ["template", "prometheus"],
"templating": {
"list": []
},
"time": {
"from": "now-1h",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "Application Dashboard Template",
"version": 1
}
@@ -0,0 +1,115 @@
# Prometheus Configuration Template
# Customize for your environment
global:
scrape_interval: 15s
evaluation_interval: 15s
external_labels:
cluster: production
environment: prod
# Alertmanager configuration
alerting:
alertmanagers:
- static_configs:
- targets:
- alertmanager:9093
# Rule files
rule_files:
- /etc/prometheus/rules/*.yaml
# Scrape configurations
scrape_configs:
# Prometheus self-monitoring
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
# Node Exporter
- job_name: 'node'
static_configs:
- targets:
- 'node1:9100'
- 'node2:9100'
- 'node3:9100'
# Kubernetes API Server
- job_name: 'kubernetes-apiservers'
kubernetes_sd_configs:
- role: endpoints
scheme: https
tls_config:
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
relabel_configs:
- source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name]
action: keep
regex: default;kubernetes;https
# Kubernetes Nodes
- job_name: 'kubernetes-nodes'
kubernetes_sd_configs:
- role: node
scheme: https
tls_config:
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
relabel_configs:
- action: labelmap
regex: __meta_kubernetes_node_label_(.+)
# Kubernetes Pods with prometheus.io annotations
- job_name: 'kubernetes-pods'
kubernetes_sd_configs:
- role: pod
relabel_configs:
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
action: keep
regex: true
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
action: replace
target_label: __metrics_path__
regex: (.+)
- source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
action: replace
regex: ([^:]+)(?::\d+)?;(\d+)
replacement: $1:$2
target_label: __address__
- action: labelmap
regex: __meta_kubernetes_pod_label_(.+)
- source_labels: [__meta_kubernetes_namespace]
action: replace
target_label: kubernetes_namespace
- source_labels: [__meta_kubernetes_pod_name]
action: replace
target_label: kubernetes_pod_name
# Kubernetes Services with prometheus.io annotations
- job_name: 'kubernetes-services'
kubernetes_sd_configs:
- role: service
metrics_path: /probe
params:
module: [http_2xx]
relabel_configs:
- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_probe]
action: keep
regex: true
- source_labels: [__address__]
target_label: __param_target
- target_label: __address__
replacement: blackbox-exporter:9115
- source_labels: [__param_target]
target_label: instance
- action: labelmap
regex: __meta_kubernetes_service_label_(.+)
- source_labels: [__meta_kubernetes_namespace]
target_label: kubernetes_namespace
- source_labels: [__meta_kubernetes_service_name]
target_label: kubernetes_name
# Remote write (optional - for long-term storage)
# remote_write:
# - url: "http://thanos-receive:19291/api/v1/receive"
@@ -0,0 +1,170 @@
# Prometheus Alerting Rules Guide
## Rule Structure
```yaml
groups:
- name: example
rules:
- alert: AlertName
expr: <PromQL expression>
for: <duration>
labels:
severity: <critical|warning|info>
team: <team-name>
annotations:
summary: "Brief description"
description: "Detailed description with {{ $labels.instance }}"
runbook_url: "https://wiki.example.com/alerts/AlertName"
```
## Essential Alerts
### Infrastructure Alerts
```yaml
groups:
- name: infrastructure
rules:
# Node down
- alert: NodeDown
expr: up{job="node"} == 0
for: 5m
labels:
severity: critical
annotations:
summary: "Node {{ $labels.instance }} is down"
# High CPU
- alert: HighCPU
expr: |
100 - (avg by (instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
for: 10m
labels:
severity: warning
annotations:
summary: "High CPU on {{ $labels.instance }}"
description: "CPU usage is {{ $value | printf \"%.1f\" }}%"
# High Memory
- alert: HighMemory
expr: |
(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > 85
for: 5m
labels:
severity: warning
annotations:
summary: "High memory on {{ $labels.instance }}"
# Disk Space Low
- alert: DiskSpaceLow
expr: |
(node_filesystem_avail_bytes{fstype!~"tmpfs|overlay"}
/ node_filesystem_size_bytes{fstype!~"tmpfs|overlay"}) * 100 < 15
for: 5m
labels:
severity: warning
annotations:
summary: "Low disk space on {{ $labels.instance }}"
description: "{{ $labels.mountpoint }} has {{ $value | printf \"%.1f\" }}% free"
# Disk Space Critical
- alert: DiskSpaceCritical
expr: |
(node_filesystem_avail_bytes{fstype!~"tmpfs|overlay"}
/ node_filesystem_size_bytes{fstype!~"tmpfs|overlay"}) * 100 < 5
for: 1m
labels:
severity: critical
annotations:
summary: "Critical disk space on {{ $labels.instance }}"
```
### Application Alerts
```yaml
groups:
- name: application
rules:
# High Error Rate
- alert: HighErrorRate
expr: |
sum by (service) (rate(http_requests_total{status=~"5.."}[5m]))
/ sum by (service) (rate(http_requests_total[5m])) > 0.05
for: 5m
labels:
severity: critical
annotations:
summary: "High error rate for {{ $labels.service }}"
description: "Error rate is {{ $value | printf \"%.2f\" }}%"
# High Latency
- alert: HighLatency
expr: |
histogram_quantile(0.95,
sum by (le, service) (rate(http_request_duration_seconds_bucket[5m]))
) > 0.5
for: 5m
labels:
severity: warning
annotations:
summary: "High latency for {{ $labels.service }}"
description: "P95 latency is {{ $value | printf \"%.2f\" }}s"
# Service Down
- alert: ServiceDown
expr: up{job="app"} == 0
for: 1m
labels:
severity: critical
annotations:
summary: "Service {{ $labels.instance }} is down"
```
### Kubernetes Alerts
```yaml
groups:
- name: kubernetes
rules:
# Pod CrashLooping
- alert: PodCrashLooping
expr: |
rate(kube_pod_container_status_restarts_total[15m]) * 60 * 15 > 0
for: 5m
labels:
severity: warning
annotations:
summary: "Pod {{ $labels.pod }} is crash looping"
# Pod Not Ready
- alert: PodNotReady
expr: |
kube_pod_status_ready{condition="true"} == 0
for: 10m
labels:
severity: warning
annotations:
summary: "Pod {{ $labels.pod }} is not ready"
# Deployment Replicas Mismatch
- alert: DeploymentReplicasMismatch
expr: |
kube_deployment_spec_replicas != kube_deployment_status_replicas_available
for: 10m
labels:
severity: warning
annotations:
summary: "Deployment {{ $labels.deployment }} has replica mismatch"
```
## Best Practices
1. **Use `for` duration** - Avoid alert flapping
2. **Include runbook URLs** - Link to remediation docs
3. **Use severity labels** - Route alerts appropriately
4. **Template annotations** - Include relevant context
5. **Test alerts** - Use `promtool check rules`
@@ -0,0 +1,168 @@
# PromQL Cheat Sheet
## Basic Queries
### Instant Vectors
```promql
# Simple metric
http_requests_total
# With label filter
http_requests_total{status="200"}
# Multiple labels
http_requests_total{status="200", method="GET"}
# Regex matching
http_requests_total{status=~"2.."}
http_requests_total{status!~"5.."}
```
### Range Vectors
```promql
# Last 5 minutes
http_requests_total[5m]
# Last 1 hour
http_requests_total[1h]
# Time units: s, m, h, d, w, y
```
## Functions
### Rate and Increase
```promql
# Per-second rate over 5m
rate(http_requests_total[5m])
# Total increase over 1h
increase(http_requests_total[1h])
# For gauges that can decrease
irate(http_requests_total[5m]) # instant rate
```
### Aggregations
```promql
# Sum across all instances
sum(http_requests_total)
# Sum by label
sum by (status) (http_requests_total)
# Sum excluding label
sum without (instance) (http_requests_total)
# Other aggregations
avg, min, max, count, stddev, stdvar
topk(5, http_requests_total)
bottomk(3, http_requests_total)
```
### Histogram Quantiles
```promql
# 95th percentile
histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))
# With grouping
histogram_quantile(0.95,
sum by (le, endpoint) (
rate(http_request_duration_seconds_bucket[5m])
)
)
```
## Common Patterns
### Request Rate
```promql
# Total request rate
sum(rate(http_requests_total[5m]))
# Request rate by endpoint
sum by (endpoint) (rate(http_requests_total[5m]))
```
### Error Rate
```promql
# Error percentage
sum(rate(http_requests_total{status=~"5.."}[5m]))
/
sum(rate(http_requests_total[5m]))
* 100
```
### Latency
```promql
# Average latency
rate(http_request_duration_seconds_sum[5m])
/
rate(http_request_duration_seconds_count[5m])
# P99 latency
histogram_quantile(0.99,
sum by (le) (rate(http_request_duration_seconds_bucket[5m]))
)
```
### Resource Usage
```promql
# CPU usage percentage
100 - (avg by (instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
# Memory usage percentage
(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100
# Disk usage percentage
(1 - (node_filesystem_avail_bytes / node_filesystem_size_bytes)) * 100
```
### Kubernetes
```promql
# Pod CPU usage
sum by (pod) (rate(container_cpu_usage_seconds_total{container!=""}[5m]))
# Pod memory usage
sum by (pod) (container_memory_usage_bytes{container!=""})
# Pod restart count
sum by (pod) (kube_pod_container_status_restarts_total)
```
## Alert Examples
### High Error Rate
```yaml
- alert: HighErrorRate
expr: |
sum(rate(http_requests_total{status=~"5.."}[5m]))
/ sum(rate(http_requests_total[5m])) > 0.05
for: 5m
labels:
severity: critical
annotations:
summary: "High error rate detected"
```
### High Latency
```yaml
- alert: HighLatency
expr: |
histogram_quantile(0.95,
sum by (le) (rate(http_request_duration_seconds_bucket[5m]))
) > 0.5
for: 5m
labels:
severity: warning
```
### Low Disk Space
```yaml
- alert: LowDiskSpace
expr: |
(node_filesystem_avail_bytes / node_filesystem_size_bytes) < 0.1
for: 5m
labels:
severity: warning
```
@@ -0,0 +1,64 @@
#!/bin/bash
# Grafana Dashboard Backup Script
# Usage: ./backup-grafana.sh [grafana-url] [api-key] [output-dir]
set -euo pipefail
GRAFANA_URL="${1:-http://localhost:3000}"
API_KEY="${2:-$GRAFANA_API_KEY}"
OUTPUT_DIR="${3:-./grafana-backup-$(date +%Y%m%d)}"
if [ -z "$API_KEY" ]; then
echo "Error: API key required. Set GRAFANA_API_KEY or pass as argument."
exit 1
fi
mkdir -p "$OUTPUT_DIR/dashboards"
mkdir -p "$OUTPUT_DIR/datasources"
mkdir -p "$OUTPUT_DIR/folders"
echo "========================================="
echo "Grafana Backup"
echo "URL: $GRAFANA_URL"
echo "Output: $OUTPUT_DIR"
echo "========================================="
echo ""
# Backup datasources
echo "Backing up datasources..."
curl -s -H "Authorization: Bearer $API_KEY" \
"$GRAFANA_URL/api/datasources" > "$OUTPUT_DIR/datasources/datasources.json"
DS_COUNT=$(jq length "$OUTPUT_DIR/datasources/datasources.json")
echo " Backed up $DS_COUNT datasources"
# Backup folders
echo "Backing up folders..."
curl -s -H "Authorization: Bearer $API_KEY" \
"$GRAFANA_URL/api/folders" > "$OUTPUT_DIR/folders/folders.json"
FOLDER_COUNT=$(jq length "$OUTPUT_DIR/folders/folders.json")
echo " Backed up $FOLDER_COUNT folders"
# Get all dashboards
echo "Backing up dashboards..."
DASHBOARDS=$(curl -s -H "Authorization: Bearer $API_KEY" \
"$GRAFANA_URL/api/search?type=dash-db")
DASH_COUNT=0
echo "$DASHBOARDS" | jq -r '.[].uid' | while read uid; do
DASH=$(curl -s -H "Authorization: Bearer $API_KEY" \
"$GRAFANA_URL/api/dashboards/uid/$uid")
TITLE=$(echo "$DASH" | jq -r '.dashboard.title' | tr ' /' '_')
echo "$DASH" > "$OUTPUT_DIR/dashboards/${TITLE}_${uid}.json"
echo " - $TITLE"
DASH_COUNT=$((DASH_COUNT + 1))
done
echo ""
echo "Backup complete!"
echo "Location: $OUTPUT_DIR"
echo ""
echo "To restore:"
echo " 1. Datasources: POST to /api/datasources"
echo " 2. Folders: POST to /api/folders"
echo " 3. Dashboards: POST to /api/dashboards/db"
@@ -0,0 +1,82 @@
#!/bin/bash
# Prometheus Health Check Script
# Usage: ./prometheus-health-check.sh [prometheus-url]
set -euo pipefail
PROMETHEUS_URL="${1:-http://localhost:9090}"
echo "========================================="
echo "Prometheus Health Check"
echo "URL: $PROMETHEUS_URL"
echo "========================================="
echo ""
# Check Prometheus health
echo -n "Prometheus Health: "
HEALTH=$(curl -s "$PROMETHEUS_URL/-/healthy" 2>/dev/null)
if [ "$HEALTH" == "Prometheus Server is Healthy." ]; then
echo "✓ Healthy"
else
echo "✗ Unhealthy"
fi
# Check readiness
echo -n "Prometheus Ready: "
READY=$(curl -s "$PROMETHEUS_URL/-/ready" 2>/dev/null)
if [ "$READY" == "Prometheus Server is Ready." ]; then
echo "✓ Ready"
else
echo "✗ Not Ready"
fi
# Get runtime info
echo ""
echo "Runtime Information:"
echo "--------------------"
curl -s "$PROMETHEUS_URL/api/v1/status/runtimeinfo" 2>/dev/null | jq -r '
.data |
"Start Time: \(.startTime)",
"Uptime: \(.CWD // "N/A")",
"Storage Retention: \(.storageRetention)",
"TSDB Info:",
" - Head Chunks: \(.TSDB.headChunks // "N/A")",
" - Head Series: \(.TSDB.headSeries // "N/A")"
' 2>/dev/null || echo "Could not retrieve runtime info"
# Get active targets summary
echo ""
echo "Target Status:"
echo "--------------"
curl -s "$PROMETHEUS_URL/api/v1/targets" 2>/dev/null | jq -r '
.data.activeTargets |
group_by(.health) |
map({health: .[0].health, count: length}) |
.[] |
"\(.health): \(.count) targets"
' 2>/dev/null || echo "Could not retrieve targets"
# List unhealthy targets
echo ""
echo "Unhealthy Targets:"
echo "------------------"
curl -s "$PROMETHEUS_URL/api/v1/targets" 2>/dev/null | jq -r '
.data.activeTargets[] |
select(.health != "up") |
"- \(.labels.job)/\(.labels.instance): \(.lastError)"
' 2>/dev/null || echo "Could not check unhealthy targets"
# Check for firing alerts
echo ""
echo "Firing Alerts:"
echo "--------------"
curl -s "$PROMETHEUS_URL/api/v1/alerts" 2>/dev/null | jq -r '
.data.alerts[] |
select(.state == "firing") |
"- [\(.labels.severity // "unknown")] \(.labels.alertname): \(.annotations.summary // .annotations.description // "No description")"
' 2>/dev/null || echo "Could not retrieve alerts"
echo ""
echo "========================================="
echo "Health check complete"
echo "========================================="