~/blog/fr/homelab-monitoring-grafana-loki-crowdsec
Surveiller votre Homelab avec Grafana, Loki et CrowdSec

Si vous gérez plusieurs conteneurs, vous avez besoin d’observabilité. Quand quelque chose casse — ou quand quelqu’un sonde vos services — vous voulez le savoir avant que cela ne devienne un problème.
Ma pile de surveillance :
- Alloy pour collecter les journaux
- Loki pour les stocker et les interroger
- Prometheus avec
node-exporterpour les métriques - Grafana pour les tableaux de bord et les alertes
- CrowdSec pour la détection d’intrusion et les menaces issues du crowd-sourcing
Tout tourne sur un petit conteneur, et rien ici n’a besoin d’un compte cloud.
Architecture
Conteneur A ──rsyslog──→ ┌─────────────────────────────┐
Conteneur B ──rsyslog──→ │ mon-srv │
node-exporter ─scrape──→ │ Alloy ──→ Loki ──┐ │
Frigate ──────scrape──→ │ Prometheus ──────┴→ Grafana│
serveur public ─scrape─→ └─────────────────────────────┘
│
CrowdSec (LAPI)
│
bouncer (nginx)
↓
Bloquer IP malveillantes
Les conteneurs envoient leurs journaux via rsyslog vers un répertoire central, Alloy suit ce répertoire et pousse vers Loki, et Prometheus scrape node-exporter sur chaque machine. Grafana lit les deux. CrowdSec analyse les mêmes journaux pour détecter des motifs d’attaque, partage des données anonymisées avec la communauté, et bloque les contrevenants récidivistes via un bouncer sur le proxy inverse.
Journaux : rsyslog → Alloy → Loki
Le premier maillon est rsyslog. Plutôt que d’installer un collecteur de journaux sur chaque conteneur, chacun transfère son syslog au conteneur de surveillance sur le port 514, qui l’écrit sous /var/log/remote/<hôte>/*.log. Une règle par conteneur, et tous les journaux atterrissent au même endroit.
Loki est le stockage. C’est un système d’agrégation de journaux de Grafana Labs qui n’indexe que les métadonnées — les étiquettes, pas le contenu — ce qui le rend assez léger pour tourner à côté de tout le reste.
wget https://github.com/grafana/loki/releases/latest/download/loki-linux-amd64.deb
dpkg -i loki-linux-amd64.deb
Configuration de Loki 3.x (/etc/loki-config.yaml), réduite à l’essentiel :
auth_enabled: false
server:
http_listen_port: 3100
common:
path_prefix: /var/lib/loki
storage_config:
boltdb_shipper:
active_index_directory: /var/lib/loki/boltdb-shipper-active
filesystem:
directory: /var/lib/loki/chunks
schema_config:
configs:
- from: 2024-01-01
store: boltdb-shipper
object_store: filesystem
schema: v11
index:
prefix: index_
period: 24h
limits_config:
retention_period: 1440h # 60 jours
retention_period est la ligne qui décide de l’espace disque que tout cela consomme, et c’est celle à laquelle penser en premier : le volume de journaux grossit sans bruit, et 60 jours d’un conteneur bavard représentent beaucoup de disque. Fixez-la volontairement, et placez le répertoire de données quelque part que vous surveillez réellement.
Collecter avec Alloy
C’était Promtail avant. Grafana a déprécié Promtail au profit de Grafana Alloy — même travail, un seul binaire qui gère aussi les métriques et les traces — la migration est donc surtout de la configuration. La vision qu’Alloy a de sa tâche (/etc/alloy/config.alloy) :
loki.source.file "remote_logs" {
targets = [{
__address__ = "localhost",
__path__ = "/var/log/remote/*/*.log",
job = "remote_syslog",
}]
forward_to = [loki.process.remote_logs.receiver]
file_match { enabled = true }
legacy_positions_file = "/var/lib/promtail/positions.yaml"
}
loki.write "default" {
endpoint { url = "http://localhost:3100/loki/api/v1/push" }
}
Deux choses valent la peine d’être reprises : file_match fait découvrir à Alloy les fichiers de journaux qui apparaissent après son démarrage, donc les journaux d’un nouveau conteneur sont pris en compte sans rechargement ; et réutiliser le positions.yaml de Promtail évite de renvoyer tous les fichiers depuis le début lors de la migration.
L’étape intermédiaire, loki.process, est l’endroit où les étiquettes sont attachées avant l’écriture — c’est ce qui rend les journaux interrogeables ensuite :
loki.process "remote_logs" {
forward_to = [loki.write.default.receiver]
stage.labels {
values = { traffic = "", country = "geoip_country_code" }
}
}
Une étiquette comme country transforme « montre-moi ce qui tape sur le proxy » en une requête d’une ligne plutôt qu’en un grep.
Métriques : Prometheus + node-exporter
Les journaux racontent ce qui s’est passé ; les métriques disent ce qui se passe. node-exporter sur chaque machine expose CPU, mémoire, disque, réseau et systèmes de fichiers sur :9100, et Prometheus les scrape :
scrape_configs:
- job_name: node
static_configs:
- targets: ['10.99.99.1:9100'] # l'hôte Proxmox
- targets: ['10.99.99.10:9100'] # le conteneur de surveillance lui-même
- targets: ['10.99.99.11:9100'] # le proxy inverse
- targets: ['10.99.99.25:9100'] # le contrôleur VPN
Incluez le conteneur de surveillance dans ses propres cibles. Une pile de surveillance incapable de vous montrer son propre disque qui se remplit, c’est ainsi qu’une surveillance meurt sans bruit.
Les machines hors du réseau local sont scrapées de la même manière, mais elles ont besoin de deux choses : le scrape doit traverser l’Internet public, et le port 9100 ne doit pas être ouvert au monde. Je l’expose en TLS avec une authentification basique, et Prometheus porte les identifiants :
- job_name: node-public
scheme: https
basic_auth:
username: prometheus
password_file: /etc/prometheus/public-server-exporter.pass
tls_config:
ca_file: /etc/prometheus/public-server-exporter.crt
static_configs:
- targets: ['mon.example.org:9100']
Sinon, node-exporter est un inventaire lisible par machine de votre hôte — version du noyau, points de montage, services actifs — publié pour quiconque trouve le port. Sur un serveur public, ne le laissez jamais sans authentification.
La caméra fonctionne pareil. J’utilise Frigate pour la détection d’objets, et il expose des métriques Prometheus, donc la santé des caméras se retrouve au même endroit que le reste :
- job_name: frigate
static_configs:
- targets: ['127.0.0.1:5000']
C’est toute l’astuce de Prometheus : tout ce qui peut répondre sur /metrics est un citoyen de première classe. Caméras, exporters et un conteneur de cinq lignes que vous avez écrit vous-même ont la même allure pour Grafana.
Grafana
Grafana est provisionné avec les deux sources de données — Loki pour les journaux, Prometheus pour les métriques :
apiVersion: 1
datasources:
- name: Loki
type: loki
url: http://localhost:3100
- name: Prometheus
type: prometheus
url: http://127.0.0.1:9090
Pouvoir passer de l’un à l’autre est la raison de faire tourner les deux. Quand une métrique décroche, le panneau de journaux juste à côté dit généralement pourquoi — d’après mon expérience, c’est cette corrélation qui fait gagner du temps, pas l’une ou l’autre source isolément.
Les tableaux de bord que je garde :
- Fleet Overview — est-ce que chaque machine est en marche, et quelque chose est-il sur le point de manquer de disque ?
- Security Overview — qui m’attaque, et quelque chose est-il bloqué ?
- CrowdSec Analysis — quels scénarios se déclenchent, depuis quels pays, contre quel service
- Log Analytics — qu’est-ce qui est le plus journalisé, et qu’est-ce qui a changé aujourd’hui ?
- Web Analytics — qui lit les sites, construit à partir des journaux d’accès plutôt que d’un suivi côté client
- Public Server — la machine hors du réseau local
- Home WiFi — quels clients ont rejoint le réseau sans fil, et ce qu’ils ont résolu
- Camera — les flux sont-ils vivants et détectent-ils ?
Règles d’Alerte
Grafana peut joindre l’email, Slack, un webhook, ou un service de notification auto-hébergé. J’ai des règles pour :
- Tentatives de brute force SSH au-dessus d’un seuil
- Certificat à renouveler dans les 7 jours
- Un conteneur arrêté depuis plus de 5 minutes
- Utilisation disque au-dessus de 85 %
- Un flux de caméra qui tombe
Une alerte que personne ne voit est pire que pas d’alerte : elle apprend à ignorer le canal. Je préfère quatre règles qui m’atteignent que quarante qui n’arrivent pas.
CrowdSec
CrowdSec est un IPS piloté par la communauté. Il détecte les attaques en analysant les journaux, puis bloque l’IP incriminée via un bouncer.
La configuration :
- LAPI (Local API) — traite les alertes et sert les décisions
- Scénarios — règles de détection (brute force SSH, scan HTTP, scan de ports)
- Bouncers — les composants qui bloquent réellement (iptables, nginx, nftables)
Il lit les mêmes journaux que tout le reste, ce qui est bien l’intérêt de les centraliser : un collecteur, trois consommateurs — Loki pour l’historique, Grafana pour les graphiques, CrowdSec pour les décisions.
cscli alerts list
cscli decisions list
cscli metrics
La liste noire communautaire est ce qui fait de CrowdSec plus qu’un IPS : quand quelqu’un attaque un utilisateur de CrowdSec, tous les autres bénéficient de cette intelligence — sans flux de menaces à maintenir à la main.
Tout Assembler
L’ensemble tourne sur un seul conteneur (mon-srv) avec 3 Go de RAM, utilisant actuellement environ 1,5 Go caméras comprises :
| Service | Mémoire |
|---|---|
| Grafana | ~226 Mo |
| Prometheus | ~135 Mo |
| Loki | ~134 Mo |
| CrowdSec | ~114 Mo |
| Alloy | ~106 Mo |
Les chiffres qui grossissent sont sur le disque, pas en RAM : Loki contenait 3,4 Go de journaux au moment de la rédaction et Prometheus 608 Mo de métriques. Les deux sont bornés par la rétention — ce qui est exactement pourquoi il faut la fixer volontairement plutôt que de la laisser à la valeur par défaut.
Pour un laboratoire avec 10 à 15 conteneurs, cette pile est plus que suffisante et entièrement gratuite. En production, vous ajouteriez des répartiteurs de charge, de la réplication et du stockage objet à long terme, mais pour un homelab, cela fonctionne parfaitement dès la sortie de la boîte.
Mis à jour en octobre 2026 : le collecteur de journaux est désormais Grafana Alloy et non plus Promtail, et le volet métriques — Prometheus, node-exporter et les métriques des caméras — fait maintenant partie de l’article au lieu d’être sous-entendu.