La journalisation centralisée devient essentielle dès lors que vous gérez plus d’une poignée de serveurs, de conteneurs ou de microservices. Sans elle, déboguer un incident en production signifie se connecter en SSH sur chaque machine et fouiller dans des fichiers de logs dispersés. L’agrégation de logs avec Loki, Promtail et Grafana — le stack PLG — résout ce problème en envoyant tous vos logs vers un référentiel unique et interrogeable, tout en maintenant des coûts de ressources bien inférieurs aux solutions traditionnelles comme Elasticsearch.

Prérequis

  • Un serveur Linux (Ubuntu 22.04 ou Debian 12 recommandé) avec au moins 2 Go de RAM
  • Docker 24+ et Docker Compose v2 installés
  • Familiarité de base avec les tableaux de bord Grafana (voir Grafana Dashboards pour la Surveillance d’Infrastructure)
  • Ports 3000 (Grafana), 3100 (Loki) et 9080 (Promtail) disponibles
  • Accès sudo sur la machine hôte

Comprendre le Stack PLG

Le stack PLG se compose de trois composants qui fonctionnent ensemble comme un pipeline d’observabilité léger :

Promtail est l’agent de collecte de logs. Il s’exécute à côté de vos applications, suit les fichiers de logs ou lit depuis le journal systemd, attache des étiquettes (métadonnées clé-valeur) et envoie des streams de logs à Loki via HTTP.

Loki est le backend d’agrégation de logs. Contrairement à Elasticsearch, Loki n’indexe pas le contenu des logs — il indexe uniquement les étiquettes attachées par Promtail. Les lignes de logs brutes sont compressées et stockées sous forme de chunks. Cette architecture rend Loki nettement moins coûteux à exploiter : un cluster traitant des gigaoctets de logs par jour peut fonctionner avec quelques centaines de mégaoctets de RAM.

Grafana fournit la couche de requête et de visualisation. Il se connecte à Loki comme source de données et permet d’écrire des requêtes LogQL, de construire des tableaux de bord et de configurer des alertes — le tout dans la même interface que vous utilisez pour les métriques Prometheus (voir Configuration de Prometheus et Grafana pour la Surveillance des Serveurs).

Le flux de données est : Application → fichier log/journal → Promtail → Loki → Grafana.

Installation de Loki avec Docker Compose

Créez un répertoire de travail et le fichier docker-compose.yml suivant :

version: "3.8"

networks:
  logging:
    driver: bridge

volumes:
  loki_data:
  grafana_data:

services:
  loki:
    image: grafana/loki:3.0.0
    container_name: loki
    ports:
      - "3100:3100"
    volumes:
      - ./loki-config.yaml:/etc/loki/loki-config.yaml
      - loki_data:/loki
    command: -config.file=/etc/loki/loki-config.yaml
    networks:
      - logging

  promtail:
    image: grafana/promtail:3.0.0
    container_name: promtail
    volumes:
      - ./promtail-config.yaml:/etc/promtail/promtail-config.yaml
      - /var/log:/var/log:ro
      - /run/log/journal:/run/log/journal:ro
      - /etc/machine-id:/etc/machine-id:ro
    command: -config.file=/etc/promtail/promtail-config.yaml
    networks:
      - logging
    depends_on:
      - loki

  grafana:
    image: grafana/grafana:11.0.0
    container_name: grafana
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=changeme
    volumes:
      - grafana_data:/var/lib/grafana
    networks:
      - logging
    depends_on:
      - loki

Créez loki-config.yaml dans le même répertoire :

auth_enabled: false

server:
  http_listen_port: 3100
  grpc_listen_port: 9096

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

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

limits_config:
  retention_period: 30d

compactor:
  working_directory: /loki/compactor
  retention_enabled: true
  delete_request_store: filesystem

Démarrez le stack :

docker compose up -d
docker compose ps

Les trois services doivent afficher le statut healthy ou running en moins de 30 secondes.

Configuration de Promtail

Créez promtail-config.yaml :

server:
  http_listen_port: 9080
  grpc_listen_port: 0

positions:
  filename: /tmp/positions.yaml

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

scrape_configs:
  - job_name: journal
    journal:
      max_age: 12h
      labels:
        job: systemd-journal
        host: ${HOSTNAME}
    relabel_configs:
      - source_labels: [__journal__systemd_unit]
        target_label: unit

  - job_name: varlogs
    static_configs:
      - targets:
          - localhost
        labels:
          job: varlogs
          host: ${HOSTNAME}
          __path__: /var/log/*.log

  - job_name: docker
    docker_sd_configs:
      - host: unix:///var/run/docker.sock
        refresh_interval: 5s
    relabel_configs:
      - source_labels: [__meta_docker_container_name]
        regex: "/(.*)"
        target_label: container
      - source_labels: [__meta_docker_container_label_com_docker_compose_service]
        target_label: service

Le fichier positions enregistre jusqu’où Promtail a lu dans chaque fichier de log, évitant ainsi l’ingestion en double après les redémarrages.

Redémarrez Promtail pour appliquer la configuration :

docker compose restart promtail
docker compose logs promtail --tail=20

Interrogation des Logs avec LogQL

LogQL est le langage de requêtes de Loki, inspiré de PromQL. Les requêtes comportent deux parties : un sélecteur de stream entre accolades et des étapes de pipeline optionnelles.

Sélecteur de stream basique — récupérer tous les logs du job varlogs :

{job="varlogs"}

Filtre de ligne — trouver les lignes contenant “error” :

{job="varlogs"} |= "error"

Filtre regex — trouver les lignes correspondant à un motif :

{job="systemd-journal"} |~ "fail(ed)?"

Filtre d’étiquette après parsing — parser les logs JSON et filtrer par niveau :

{job="varlogs"} | json | level="error"

Requête métrique — taux d’erreurs dans le temps :

rate({job="varlogs"} |= "error" [5m])

Compter les erreurs par hôte :

sum by (host) (count_over_time({job="varlogs"} |= "error" [1h]))

Pour des modèles de parsing de logs plus complexes, comparez avec l’approche Journalctl pour Interroger et Analyser les Logs du Système Linux pour les scénarios uniquement locaux.

Configuration des Tableaux de Bord Grafana

Ouvrez Grafana à l’adresse http://votre-serveur:3000 et connectez-vous avec admin / changeme.

Ajouter Loki comme source de données :

  1. Allez dans Connexions > Sources de données > Ajouter une source de données
  2. Sélectionnez Loki
  3. Définissez l’URL sur http://loki:3100
  4. Cliquez sur Enregistrer et tester — vous devriez voir “Source de données connectée et étiquettes trouvées”

Créer un panneau d’exploration de logs :

  1. Créez un nouveau tableau de bord et ajoutez un panneau
  2. Sélectionnez Loki comme source de données
  3. Changez la visualisation en Logs
  4. Saisissez une requête LogQL : {job="varlogs"} |= "error"
  5. Activez la Déduplication et le Retour à la ligne dans les options du panneau

Construire un tableau de bord de taux :

Ajoutez un panneau de Séries temporelles avec la requête :

sum by (host) (rate({job="varlogs"} |= "error" [5m]))

Cela fournit un graphique de taux d’erreurs en temps réel décomposé par serveur.

Alertes sur les Modèles de Logs

Loki inclut un composant ruler qui évalue des requêtes métriques LogQL selon un calendrier et déclenche des alertes vers Alertmanager.

Ajoutez la configuration du ruler à loki-config.yaml :

ruler:
  storage:
    type: local
    local:
      directory: /loki/rules
  rule_path: /loki/rules-temp
  alertmanager_url: http://alertmanager:9093
  enable_api: true

Créez /loki/rules/fake/rules.yaml :

groups:
  - name: log-alerts
    rules:
      - alert: HighErrorRate
        expr: |
          sum(rate({job="varlogs"} |= "error" [5m])) > 0.1
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Taux d'erreurs élevé détecté dans les logs système"
          description: "Le taux de logs d'erreur a dépassé 0.1 ligne/sec pendant 5 minutes."

      - alert: SSHAuthFailures
        expr: |
          sum(count_over_time({unit="sshd.service"} |= "Failed password" [10m])) > 10
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "Multiples échecs d'authentification SSH"
          description: "Plus de 10 échecs d'auth SSH en 10 minutes — possible attaque par force brute."

Loki vs ELK vs Graylog — Comparaison

FonctionnalitéLokiELK StackGraylog
Stratégie d’indexationÉtiquettes uniquementIndex plein texteIndex plein texte
Utilisation RAM (10 Go/jour)~512 Mo8–16 Go4–8 Go
Langage de requêtesLogQLKQL / LuceneGELF / Elasticsearch
Intégration GrafanaNativePlugin requisPlugin requis
Complexité de configurationFaibleÉlevéeMoyenne
Scalabilité horizontaleBonne (mode microservices)ExcellenteBonne
Coût à l’échelleTrès faibleÉlevéMoyen
Idéal pourLogs Kubernetes / conteneursRecherche plein texteConformité, SIEM

Loki l’emporte sur le coût et la simplicité lorsque votre besoin principal est l’agrégation et la corrélation de logs avec des métriques. Pour une comparaison avec la configuration d’Elasticsearch, voir Configuration d’Elasticsearch pour l’Analyse de Logs.

Scénario Réel

Vous gérez 15 serveurs de production exécutant une combinaison de Nginx, PostgreSQL et des API Python personnalisées. Les incidents surviennent mais l’analyse des causes premières prend des heures car les logs sont répartis sur 15 machines. Voici comment PLG résout ce problème :

  1. Déployez Promtail sur chaque serveur en utilisant un outil de gestion de configuration (Ansible/Puppet), en pointant toutes les instances vers un unique endpoint Loki.
  2. Ajoutez des étiquettes host et env pour que chaque stream de logs soit tagué avec le serveur d’origine et l’environnement (production/staging).
  3. Dans Grafana, créez un tableau de bord avec une variable $host liée aux valeurs de l’étiquette host. Maintenant un seul tableau de bord affiche les logs des 15 serveurs, avec un menu déroulant pour filtrer par hôte.
  4. Ajoutez une règle d’alerte qui se déclenche lorsqu’un hôte produit plus de 50 lignes d’erreur par minute — vous recevez une notification sur Slack avant que les utilisateurs ne signalent le problème.
  5. Lors d’incidents, utilisez la vue Explore pour corréler logs et métriques Prometheus côte à côte : pic d’erreurs dans les logs à 14h32 → pic CPU sur le même hôte à 14h31.

Ce flux de travail remplace une investigation SSH manuelle de 45 minutes par un drill-down Grafana de 3 minutes.

Pièges et Cas Particuliers

Explosion de cardinalité des étiquettes. Chaque combinaison unique d’étiquettes crée un stream de logs distinct. N’utilisez jamais des valeurs à haute cardinalité (IDs utilisateur, UUIDs, IDs de requête) comme étiquettes. Limitez les étiquettes à des valeurs stables et peu nombreuses : host, job, env, service.

Configuration de la rétention. Le retention_period dans limits_config nécessite que le compactor soit activé avec retention_enabled: true. Sans cela, Loki stocke les logs indéfiniment jusqu’à ce que le disque soit plein.

Cache de chunks et mémoire. Loki met en cache des chunks non compressés en mémoire pendant l’ingestion. Sur des serveurs avec moins de 2 Go de RAM, définissez chunk_target_size: 1048576 et max_chunk_age: 1h pour limiter la pression mémoire.

Perte du fichier de positions Promtail. Si le conteneur Promtail est recréé sans volume persistant pour /tmp/positions.yaml, il relit tous les fichiers de logs depuis le début et ingère des doublons. Montez toujours le fichier de positions sur un volume persistant.

Ordre des logs. Loki exige que les lignes de logs au sein d’un même stream soient ingérées dans l’ordre des timestamps. Si votre application écrit des logs avec des timestamps désordonnés, activez unordered_writes: true dans la configuration de l’ingester Loki.

Accès au socket Docker. La configuration Docker SD dans Promtail nécessite l’accès au socket Docker. Le conteneur Promtail doit s’exécuter en tant que root ou être ajouté au groupe docker.

Résumé

  • Le stack PLG (Promtail + Loki + Grafana) fournit une agrégation centralisée de logs à une fraction du coût d’ELK
  • Loki indexe uniquement les étiquettes, pas le contenu des logs — minimisant l’utilisation du stockage et de la RAM
  • Les sélecteurs de stream LogQL ({job="varlogs"}) plus les étapes de pipeline (|= "error", | json) permettent un filtrage puissant des logs
  • Promtail scrappe le journal systemd, les fichiers et les logs des conteneurs Docker avec attachement automatique des étiquettes
  • La source de données Loki de Grafana permet des tableaux de bord unifiés combinant logs (Loki) et métriques (Prometheus)
  • Les étiquettes à haute cardinalité sont l’erreur de performance la plus courante avec Loki — maintenez des valeurs d’étiquettes stables et peu nombreuses
  • Les règles d’alerte Loki utilisent des requêtes métriques LogQL évaluées par le composant ruler

Articles Connexes