---
title: "Grafana — Dashboards et visualisation"
domain: monitoring
subdomain: grafana
type: snippet
tags: [grafana, prometheus, loki, dashboards, alerting, provisioning, docker, metrics]
difficulty: intermediate
status: stable
updated: "2026-05-26"
---
import { Tip, Warning } from '@/components/mdx';

## Déploiement Docker

```yaml
# docker-compose.yml
services:
  grafana:
    image: grafana/grafana:10.4.2
    ports: ["3000:3000"]
    environment:
      GF_SECURITY_ADMIN_USER: admin
      GF_SECURITY_ADMIN_PASSWORD: "${GRAFANA_PASSWORD}"
      GF_USERS_ALLOW_SIGN_UP: "false"
      GF_SERVER_ROOT_URL: "http://localhost:3000"
      GF_SMTP_ENABLED: "true"
      GF_SMTP_HOST: "smtp.example.com:587"
      GF_SMTP_FROM_ADDRESS: "grafana@example.com"
    volumes:
      - grafana_data:/var/lib/grafana
      - ./grafana/provisioning:/etc/grafana/provisioning
      - ./grafana/dashboards:/var/lib/grafana/dashboards

  loki:
    image: grafana/loki:3.0.0
    ports: ["3100:3100"]
    volumes:
      - ./grafana/loki-config.yml:/etc/loki/local-config.yaml
      - loki_data:/loki
    command: -config.file=/etc/loki/local-config.yaml

  promtail:
    image: grafana/promtail:3.0.0
    volumes:
      - /var/log:/var/log:ro
      - /var/lib/docker/containers:/var/lib/docker/containers:ro
      - ./grafana/promtail-config.yml:/etc/promtail/config.yml
    command: -config.file=/etc/promtail/config.yml

volumes:
  grafana_data:
  loki_data:
```

```bash
docker compose up -d grafana loki promtail
# Interface : http://localhost:3000  (admin / voir .env)
```

## Provisioning — Datasources

```yaml
# grafana/provisioning/datasources/datasources.yml
apiVersion: 1

datasources:
  - name: Prometheus
    type: prometheus
    access: proxy
    url: http://prometheus:9090
    isDefault: true
    jsonData:
      timeInterval: "15s"

  - name: Loki
    type: loki
    access: proxy
    url: http://loki:3100
    jsonData:
      maxLines: 1000

  - name: Elasticsearch
    type: elasticsearch
    access: proxy
    url: http://elasticsearch:9200
    basicAuth: true
    basicAuthUser: elastic
    secureJsonData:
      basicAuthPassword: "${ELASTIC_PASSWORD}"
    jsonData:
      index: "logstash-*"
      timeField: "@timestamp"
      esVersion: "8.0.0"
```

## Provisioning — Dashboards

```yaml
# grafana/provisioning/dashboards/dashboards.yml
apiVersion: 1

providers:
  - name: default
    folder: Infra
    type: file
    options:
      path: /var/lib/grafana/dashboards
      foldersFromFilesStructure: true
```

## Loki — Configuration

```yaml
# grafana/loki-config.yml
auth_enabled: false

server:
  http_listen_port: 3100

ingester:
  lifecycler:
    ring:
      kvstore:
        store: inmemory
      replication_factor: 1

schema_config:
  configs:
    - from: 2024-01-01
      store: boltdb-shipper
      object_store: filesystem
      schema: v11
      index:
        prefix: index_
        period: 24h

storage_config:
  boltdb_shipper:
    active_index_directory: /loki/index
    cache_location: /loki/index_cache
  filesystem:
    directory: /loki/chunks

limits_config:
  retention_period: 30d
  ingestion_rate_mb: 16
  max_query_series: 5000
```

## Promtail — Configuration

```yaml
# grafana/promtail-config.yml
server:
  http_listen_port: 9080

positions:
  filename: /tmp/positions.yaml

clients:
  - url: http://loki:3100/loki/api/v1/push

scrape_configs:
  - job_name: system
    static_configs:
      - targets: [localhost]
        labels:
          job: syslog
          __path__: /var/log/syslog

  - job_name: auth
    static_configs:
      - targets: [localhost]
        labels:
          job: auth
          __path__: /var/log/auth.log

  - job_name: docker
    docker_sd_configs:
      - host: unix:///var/run/docker.sock
        refresh_interval: 5s
    relabel_configs:
      - source_labels: [__meta_docker_container_name]
        target_label: container
      - source_labels: [__meta_docker_container_log_stream]
        target_label: stream
```

## Panels PromQL courants

```promql
# Gauge CPU (%)
100 - (avg(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)

# Time series — trafic réseau
rate(node_network_receive_bytes_total{device!="lo"}[5m]) * 8 / 1024 / 1024

# Stat — uptime serveur
(node_time_seconds - node_boot_time_seconds) / 3600 / 24

# Bar gauge — espace disque par volume
(node_filesystem_size_bytes - node_filesystem_avail_bytes) / node_filesystem_size_bytes * 100

# Table — containers actifs avec RAM
container_memory_usage_bytes{name!=""} / 1024 / 1024
```

## LogQL — Requêtes Loki

```logql
# Tous les logs d'erreur SSH
{job="auth"} |= "Failed password"

# Logs par container
{container="nginx"} |= "error"

# Parser les logs Nginx (pattern)
{job="nginx"} | pattern `<ip> - - [<date>] "<method> <uri> <proto>" <status> <size>`
  | status >= 500

# Compter les erreurs 5xx par minute
sum(rate({job="nginx"} | pattern `<_> <status> <_>` | status >= 500 [1m]))

# Filtrer par niveau de log
{container="app"} | json | level="error"
```

## Alerting Grafana (Unified Alerting)

```bash
# Via API — créer un contact point Slack
curl -X POST http://admin:password@localhost:3000/api/v1/provisioning/contact-points \
  -H "Content-Type: application/json" \
  -d '{
    "name": "slack-ops",
    "type": "slack",
    "settings": {
      "url": "<SLACK_WEBHOOK_URL>",
      "channel": "#alerts"
    }
  }'

# Créer une règle d alerte via API
curl -X POST http://admin:password@localhost:3000/api/v1/provisioning/alert-rules \
  -H "Content-Type: application/json" \
  -d '{
    "title": "CPU High",
    "condition": "C",
    "data": [
      {
        "refId": "A",
        "datasourceUid": "__expr__",
        "model": {
          "expr": "100 - (avg(rate(node_cpu_seconds_total{mode=\"idle\"}[5m])) * 100) > 85"
        }
      }
    ],
    "for": "5m",
    "labels": { "severity": "warning" },
    "annotations": { "summary": "CPU élevé" },
    "folderUID": "infra",
    "ruleGroup": "infra-alerts"
  }'
```

## Commandes utiles

```bash
# Vérifier la santé de Grafana
curl -s http://localhost:3000/api/health | jq

# Lister les datasources
curl -s http://admin:password@localhost:3000/api/datasources | jq '.[].name'

# Exporter un dashboard (récupérer l'UID depuis l'URL)
curl -s http://admin:password@localhost:3000/api/dashboards/uid/<UID> | jq '.dashboard' > dashboard.json

# Importer un dashboard
curl -X POST http://admin:password@localhost:3000/api/dashboards/import \
  -H "Content-Type: application/json" \
  -d '{"dashboard": <JSON>, "overwrite": true, "folderId": 0}'

# Vérifier Loki
curl -s http://localhost:3100/ready
curl -s "http://localhost:3100/loki/api/v1/query?query={job=\"auth\"}&limit=5" | jq

# Vérifier Promtail
curl -s http://localhost:9080/metrics | grep promtail_sent_entries
```

## Dashboards recommandés (import par ID)

| Dashboard | ID Grafana.com | Usage |
|---|---|---|
| Node Exporter Full | 1860 | Métriques système Linux |
| cAdvisor | 14282 | Métriques containers Docker |
| Docker | 893 | Vue globale Docker |
| Loki Dashboard | 13639 | Logs agrégés |
| Blackbox Exporter | 7587 | Sonde HTTP/TCP |
| Alertmanager | 9578 | État des alertes |

```bash
# Importer depuis Grafana.com via API
curl -X POST http://admin:password@localhost:3000/api/dashboards/import \
  -H "Content-Type: application/json" \
  -d '{"inputs":[{"name":"DS_PROMETHEUS","type":"datasource","pluginId":"prometheus","value":"Prometheus"}],"folderId":0,"overwrite":true,"path":"https://grafana.com/api/dashboards/1860/revisions/latest/download"}'
```

<Tip>
Utiliser le **provisioning** (fichiers YAML dans `provisioning/`) pour versionner datasources et dashboards dans Git. Évite la dérive de configuration et permet un redéploiement reproductible.
</Tip>

<Warning>
Changer le mot de passe admin par défaut et désactiver `GF_USERS_ALLOW_SIGN_UP`. En production, activer HTTPS via reverse proxy et configurer `GF_SERVER_ROOT_URL` avec le FQDN public.
</Warning>
