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 :
- Allez dans Connexions > Sources de données > Ajouter une source de données
- Sélectionnez Loki
- Définissez l’URL sur
http://loki:3100 - 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 :
- Créez un nouveau tableau de bord et ajoutez un panneau
- Sélectionnez Loki comme source de données
- Changez la visualisation en Logs
- Saisissez une requête LogQL :
{job="varlogs"} |= "error" - 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é | Loki | ELK Stack | Graylog |
|---|---|---|---|
| Stratégie d’indexation | Étiquettes uniquement | Index plein texte | Index plein texte |
| Utilisation RAM (10 Go/jour) | ~512 Mo | 8–16 Go | 4–8 Go |
| Langage de requêtes | LogQL | KQL / Lucene | GELF / Elasticsearch |
| Intégration Grafana | Native | Plugin requis | Plugin requis |
| Complexité de configuration | Faible | Élevée | Moyenne |
| Scalabilité horizontale | Bonne (mode microservices) | Excellente | Bonne |
| Coût à l’échelle | Très faible | Élevé | Moyen |
| Idéal pour | Logs Kubernetes / conteneurs | Recherche plein texte | Conformité, 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 :
- 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.
- Ajoutez des étiquettes
hostetenvpour que chaque stream de logs soit tagué avec le serveur d’origine et l’environnement (production/staging). - Dans Grafana, créez un tableau de bord avec une variable
$hostliée aux valeurs de l’étiquettehost. Maintenant un seul tableau de bord affiche les logs des 15 serveurs, avec un menu déroulant pour filtrer par hôte. - 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.
- 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