Supervision et journaux

Centraliser ses journaux avec Loki et Alloy

La supervision dit qu'un service va mal. Le journal dit pourquoi, à condition de l'avoir gardé, et de pouvoir le lire à côté de la courbe qui a décroché.

Niveau intermédiaire  ·  Lecture 14 min  ·  Base Loki 3 et Grafana Alloy

Une supervision dit qu'un service va mal. Elle ne dit presque jamais pourquoi. La réponse est écrite quelque part, dans un journal, sur l'une des quarante machines concernées, et elle aura disparu à la prochaine rotation si personne ne va la chercher à temps. Centraliser les journaux, c'est pouvoir poser une seule question à toute l'infrastructure : « qu'est-ce qui s'est passé à 3 h 12 ? »

01Ce que les métriques ne disent pas

  • La cause. La courbe montre que PostgreSQL a refusé des connexions à 3 h 12. Le journal dit « too many clients already », et celui de l'application dit lequel des pools s'est emballé.
  • Ce qui ne laisse pas de trace numérique. Une tentative de connexion SSH refusée, une sauvegarde qui saute un fichier, un disque qui signale une erreur de lecture corrigée : rien de cela ne fait bouger une courbe.
  • La chronologie entre machines. Le noeud a perdu le quorum, puis le service de haute disponibilité a déplacé trois machines, puis l'une n'a pas redémarré. Seule une vue centralisée et horodatée permet de lire cette histoire dans l'ordre.
  • La preuve. Après un incident de sécurité, les journaux de la machine compromise sont les premiers à disparaître. Une copie ailleurs est souvent la seule qui reste.

02Loki, Graylog ou autre

Deux philosophies s'affrontent. Graylog, comme Elasticsearch avant lui, indexe le contenu de chaque ligne : toute recherche plein texte est rapide, au prix d'un stockage et d'une mémoire conséquents. Loki n'indexe que quelques étiquettes (la machine, le service) et parcourt le texte au moment de la requête : il coûte une fraction du stockage, et s'intègre naturellement à Grafana, à côté des métriques.

CritèreLoki et AlloyGraylogVictoriaLogs
Ce qui est indexéLes étiquettes seulementTout le contenuLes champs, sans schéma
Ressources pour 50 hôtes2 vCPU, 4 Go8 vCPU, 16 Go et plus2 vCPU, 4 Go
DépendancesAucune en mode simpleMongoDB, OpenSearch ou Data NodeAucune
Recherche plein texte sur un anLenteRapideRapide
Analyse et transformation des lignesÀ la requêtePipelines, très complètesÀ la requête
Intégration GrafanaNativePar source de donnéesPar greffon
MaturitéGrandeGrandePlus jeune

Notre choix par défaut est Loki, pour une raison simple : nos clients ont déjà Grafana, et voir le journal d'une machine sous la courbe qui a décroché, dans le même tableau de bord, change la vitesse de diagnostic. Graylog s'impose quand les journaux sont un sujet en soi : une équipe sécurité qui cherche des chaînes arbitraires sur douze mois, ou des formats hétérogènes à normaliser par pipeline. VictoriaLogs est une alternative légère prometteuse, à suivre si vous utilisez déjà VictoriaMetrics.

03Installer Loki

Loki s'installe en mode monolithique, un seul processus, sur le serveur de supervision de la pile Grafana. Ce mode tient sans difficulté quelques dizaines de gigaoctets de journaux par jour, ce qui couvre largement la plupart des infrastructures de PME.

Ajout au docker-compose.yml de la pile
  loki:
    # relever la version 3.x courante et l'epingler
    image: grafana/loki:3.x.y
    restart: unless-stopped
    command: -config.file=/etc/loki/loki.yml
    volumes:
      - ./loki:/etc/loki:ro
      - ./loki-regles:/loki/rules:ro
      - loki_data:/loki
    ports:
      - "127.0.0.1:3100:3100"
Fichier loki/loki.yml
auth_enabled: false

server:
  http_listen_port: 3100

common:
  instance_addr: 127.0.0.1
  path_prefix: /loki
  replication_factor: 1
  ring:
    kvstore:
      store: inmemory
  storage:
    filesystem:
      chunks_directory: /loki/chunks
      rules_directory: /loki/rules

schema_config:
  configs:
    - from: 2026-01-01
      store: tsdb
      object_store: filesystem
      schema: v13
      index:
        prefix: index_
        period: 24h

limits_config:
  retention_period: 90d
  # protege Loki d'un emetteur emballe
  ingestion_rate_mb: 8
  ingestion_burst_size_mb: 16

compactor:
  working_directory: /loki/compactor
  # sans ces deux lignes, retention_period n'efface rien
  retention_enabled: true
  delete_request_store: filesystem

ruler:
  alertmanager_url: http://alertmanager:9093
  enable_api: true
La rétention ne fonctionne qu'avec le compacteur

C'est le piège classique de Loki : une durée de rétention déclarée dans les limites, mais un compacteur sans rétention activée. Les journaux s'accumulent alors indéfiniment, jusqu'au disque plein, alors que la configuration semble correcte.

Les agents des serveurs écrivent dans Loki : exposez-le derrière le proxy inverse, en TLS, et protégez l'écriture. Loki n'a pas d'authentification propre en mode simple ; une authentification de base sur le chemin /loki/api/v1/push, côté Nginx, suffit pour une infrastructure interne.

04Collecter avec Grafana Alloy

Grafana Alloy est l'agent de collecte de Grafana. Il remplace Promtail, arrivé en fin de vie au début de 2026 : les tutoriels qui l'installent encore sont à éviter pour une installation neuve. Alloy lit le journal systemd, des fichiers, ou reçoit du syslog, et sait aussi collecter des métriques, ce qui permet à terme de n'avoir qu'un agent par serveur.

Installation sur Debian, noeuds Proxmox compris
# depot Grafana : suivre la procedure officielle, puis
apt install alloy

# l'agent doit pouvoir lire le journal systemd et /var/log
usermod -aG adm,systemd-journal alloy
Fichier /etc/alloy/config.alloy
// destination : le Loki central
loki.write "central" {
  endpoint {
    url = "https://loki.exemple.fr/loki/api/v1/push"
    basic_auth {
      username = "alloy"
      password = sys.env("LOKI_PASSWORD")
    }
  }
}

// le journal systemd, avec le nom du service en etiquette
loki.relabel "journal" {
  forward_to = []
  rule {
    source_labels = ["__journal__systemd_unit"]
    target_label  = "unit"
  }
  rule {
    source_labels = ["__journal_priority_keyword"]
    target_label  = "niveau"
  }
}

loki.source.journal "systeme" {
  forward_to    = [loki.write.central.receiver]
  relabel_rules = loki.relabel.journal.rules
  max_age       = "12h"
  labels        = {job = "journal", host = constants.hostname}
}

// des fichiers classiques, ici les journaux Nginx
local.file_match "nginx" {
  path_targets = [{
    __path__ = "/var/log/nginx/*.log",
    job      = "nginx",
    host     = constants.hostname,
  }]
}

loki.source.file "nginx" {
  targets    = local.file_match.nginx.targets
  forward_to = [loki.write.central.receiver]
}
Démarrer et vérifier
echo 'LOKI_PASSWORD=...' >> /etc/default/alloy
systemctl enable --now alloy
journalctl -u alloy -n 30 --no-pager

# interface de diagnostic locale : etat de chaque composant
curl -s http://127.0.0.1:12345/-/ready
Les équipements qui ne parlent que syslog

Commutateurs, pare-feu, onduleurs et NAS savent rarement faire autre chose qu'envoyer du syslog. Le composant loki.source.syslog d'Alloy les reçoit directement ; vérifiez seulement le format, RFC 5424 par défaut, alors que beaucoup d'équipements émettent encore l'ancien RFC 3164. Installez cette réception sur un Alloy du réseau d'administration, jamais sur une interface publique : syslog en UDP n'a ni authentification ni chiffrement.

05Les étiquettes, piège numéro un

Chaque combinaison distincte d'étiquettes crée un flux séparé dans Loki. Quelques centaines de flux, c'est parfait ; des centaines de milliers, et Loki s'effondre. Toute valeur qui varie beaucoup va donc dans le texte de la ligne, jamais dans une étiquette.

Bonne étiquetteMauvaise étiquette
host, le nom de la machineL'adresse IP du client qui se connecte
unit, le service systemdL'identifiant de session ou de requête
job, la source de collecteL'URL complète demandée
niveau, la gravitéLe nom de l'utilisateur

Ce que l'on perd en ne mettant pas l'adresse IP en étiquette, on le retrouve à la requête : un filtre sur le texte, ou une extraction à la volée, retrouve tous les accès d'une adresse donnée. C'est plus lent qu'un index, et c'est le compromis assumé de Loki.

06Chercher dans les journaux

Loki s'interroge en LogQL, depuis l'onglet Explore de Grafana. La syntaxe ressemble volontairement à celle de Prometheus : on sélectionne des flux par étiquettes, puis on filtre et on transforme.

Requêtes LogQL utiles
# toutes les erreurs d'un noeud, tous services confondus
{host="pve-01", niveau=~"err|crit|alert|emerg"}

# ce que corosync a raconte pendant la perte de quorum
{unit="corosync.service"} |= "link"

# les reponses 5xx de Nginx, sur toutes les machines
{job="nginx"} |~ "\" 5[0-9]{2} "

# echecs SSH par machine, comptes par tranche de 5 minutes
sum by (host) (count_over_time({unit="ssh.service"} |= "Failed password" [5m]))

# ce qui s'est passe partout entre 3 h 10 et 3 h 15 : regler la plage dans Grafana
{job="journal"} != "systemd-timesyncd"
Le geste qui change tout

Dans un tableau de bord Grafana, ajoutez sous les courbes d'un hôte un panneau de journaux filtré sur la même machine, et liez la plage de temps. Le jour de l'incident, on sélectionne le pic sur la courbe et les lignes de journal de ce moment-là s'affichent juste en dessous. C'est la raison principale de notre préférence pour Loki.

07Alerter sur un journal

Le composant ruler de Loki évalue des règles au format Prometheus, en LogQL, et envoie les alertes au même Alertmanager que vos métriques. Elles suivent donc les mêmes routes, vers les mêmes canaux, y compris ntfy.

Fichier loki-regles/fake/journaux.yml
# sans authentification, Loki range les regles du locataire unique sous fake/
groups:
  - name: journaux
    rules:
      - alert: ForceBruteSSH
        expr: |
          sum by (host) (count_over_time({unit="ssh.service"} |= "Failed password" [5m])) > 30
        for: 0m
        labels: {severity: alerte}
        annotations:
          summary: "Plus de 30 echecs SSH en 5 minutes sur {{ $labels.host }}"

      - alert: ErreurDisqueNoyau
        expr: |
          sum by (host) (count_over_time({job="journal"} |~ "I/O error|Medium Error|UNC" [10m])) > 0
        labels: {severity: critique}
        annotations:
          summary: "Erreurs d'entree-sortie dans le noyau de {{ $labels.host }}"

      - alert: JournauxMuets
        expr: |
          sum by (host) (count_over_time({job="journal"}[15m])) == 0
        for: 15m
        labels: {severity: alerte}
        annotations:
          summary: "{{ $labels.host }} n'envoie plus de journaux"
La règle des journaux muets ne voit que ceux qui ont déjà parlé

Une requête qui compte des lignes ne renvoie rien pour une machine disparue : il n'y a plus de série à comparer à zéro. Pour détecter un agent Alloy arrêté, comptez plutôt sur la supervision de l'agent lui-même, par exemple son service systemd dans Zabbix ou ses métriques internes dans Prometheus.

08Combien de temps garder

Les journaux contiennent des adresses IP, des identifiants, parfois des adresses de messagerie : ce sont des données personnelles au sens du RGPD. Leur durée de conservation doit donc être définie, justifiée et respectée, ce qui exclut le « on garde tout, on verra ». Pour la journalisation de sécurité, la CNIL recommande en règle générale une durée de six mois à un an. Certaines activités ont leurs propres obligations : un hébergeur, par exemple, conserve les données d'identification de ceux qui publient un contenu.

JournauxDurée que nous retenonsRaison
Système et services, tous serveurs90 joursDiagnostic, comparaison avec le trimestre
Authentifications et accès d'administration1 anEnquête après incident de sécurité
Journaux applicatifs verbeux14 à 30 joursVolume, faible valeur au-delà
Accès web des sites hébergésSelon vos obligations d'hébergeurÀ valider avec votre conseil

Dans Loki, la durée se règle globalement par retention_period, et par flux grâce à retention_stream dans les limites, qui permet par exemple de garder un an {unit="ssh.service"} et trente jours le reste. Documentez ces durées dans votre registre des traitements : c'est une ligne, et c'est la première question posée lors d'un contrôle.

Ce n'est pas un avis juridique

Les durées ci-dessus sont nos choix d'exploitant pour des infrastructures courantes. Votre secteur, vos contrats et votre statut d'hébergeur peuvent imposer d'autres durées : faites-les valider par votre délégué à la protection des données ou votre conseil.

Une supervision qui veille pendant que vous dormez

Nous concevons, installons et exploitons des plateformes de supervision pour des PME, des hébergeurs et des collectivités : Zabbix, Prometheus et Grafana, ou plus léger quand le besoin l'est. Reprise d'une supervision existante, réglage des alertes qui sonnent pour rien, astreinte : nous intervenons au forfait comme au long cours.