Les métriques vous disent quoi (combien de requêtes, quelle latence). Les logs vous disent pourquoi (le message d’erreur exact, la stack trace). Après avoir configuré Prometheus pour les métriques et Alertmanager pour les notifications, vous allez maintenant ajouter la gestion des logs centralisés.
Loki est souvent appelé le “Prometheus des logs”. Comme Prometheus, il indexe par labels plutôt que par contenu complet. Cela le rend léger, économique et rapide — parfait pour Kubernetes.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »À la fin de ce module, vous saurez :
- Expliquer l’architecture Loki/Promtail
- Installer Loki et Promtail sur Kubernetes
- Requêter les logs avec LogQL
- Parser les logs JSON pour extraire des champs
- Générer des métriques à partir des logs
- Créer des dashboards et alertes sur les logs
Prérequis
Section intitulée « Prérequis »-
Prometheus et Grafana fonctionnent
Fenêtre de terminal kubectl get pods -n observability -l "app.kubernetes.io/name in (prometheus,grafana)"Ce que vous devez voir : Les pods en
Running. -
L’application OpenTelemetry Demo génère du trafic
Fenêtre de terminal kubectl get pods -n otel-demo | head -5Ce que vous devez voir : Plusieurs pods en
Running. -
Le repo Helm Grafana est disponible
Fenêtre de terminal helm repo list | grep grafanaCe que vous devez voir : La ligne
grafana.Si absent :
Fenêtre de terminal helm repo add grafana https://grafana.github.io/helm-chartshelm repo update -
Vous êtes dans le répertoire du lab
Fenêtre de terminal cd ~/lab-observability
Comprendre l’architecture Loki
Section intitulée « Comprendre l’architecture Loki »Les composants
Section intitulée « Les composants »| Composant | Rôle | Analogie |
|---|---|---|
| Pods | Écrivent leurs logs sur stdout/stderr | L’application qui parle |
| Promtail | Collecte les logs et les enrichit avec les labels Kubernetes | Le coursier qui transporte le courrier |
| Loki | Stocke les logs, indexe par labels | L’archiviste qui classe le courrier |
| Grafana | Interface pour chercher et visualiser | Le guichet pour consulter les archives |
Pourquoi Loki est différent
Section intitulée « Pourquoi Loki est différent »L’approche traditionnelle (ELK/Splunk) :
- Indexe tout le contenu de chaque log
- Recherche full-text puissante
- Stockage coûteux (chaque mot est indexé)
- Ingestion lourde
L’approche Loki :
- Indexe seulement les labels (namespace, pod, container…)
- Stockage économique (les logs bruts sont compressés)
- Recherche par labels d’abord, puis filtrage de contenu
- Ingestion légère
Tableau comparatif
Section intitulée « Tableau comparatif »| Critère | Loki | ELK/Splunk |
|---|---|---|
| Indexation | Labels seulement | Contenu complet |
| Coût stockage | ✅ Faible | ❌ Élevé |
| Recherche full-text | ⚠️ Séquentielle | ✅ Indexée |
| Intégration Grafana | ✅ Native | ⚠️ Plugin |
| Syntaxe requête | LogQL (≈ PromQL) | Propre à chaque outil |
| Complexité déploiement | ✅ Simple | ⚠️ Complexe |
Conclusion : Loki est le choix recommandé pour la majorité des cas DevOps. Réservez ELK/Splunk aux besoins de recherche full-text intensive (logs d’application avec messages très variés, analyse forensique).
Installer Loki et Promtail
Section intitulée « Installer Loki et Promtail »Comprendre loki-stack
Section intitulée « Comprendre loki-stack »Le chart loki-stack de Grafana installe Loki et Promtail ensemble. C’est la méthode la plus simple pour commencer.
Configuration des values
Section intitulée « Configuration des values »Examinez la configuration que nous allons utiliser :
cat 05-loki/helm-values/loki-stack.yamlContenu :
# Loki - serveur de logsloki: enabled: true persistence: enabled: true size: 10Gi
config: limits_config: retention_period: 168h # 7 jours reject_old_samples: true reject_old_samples_max_age: 168h
chunk_store_config: max_look_back_period: 0s
table_manager: retention_deletes_enabled: true retention_period: 168h
# Promtail - collecteur de logspromtail: enabled: true
config: clients: - url: http://loki:3100/loki/api/v1/push
snippets: pipelineStages: # Parser les logs JSON automatiquement - cri: {} - json: expressions: level: level msg: msg trace_id: trace_id span_id: span_id - labels: level: trace_id: span_id:Ce que fait cette configuration :
| Section | Effet |
|---|---|
loki.persistence | Les logs sont stockés sur disque (pas perdus au redémarrage) |
loki.config.limits_config.retention_period | Les logs plus vieux que 7 jours sont supprimés |
promtail.config.snippets.pipelineStages | Les logs JSON sont parsés et le niveau extrait comme label |
Déployer
Section intitulée « Déployer »-
Installez loki-stack
Fenêtre de terminal helm upgrade --install loki grafana/loki-stack \-n observability \-f 05-loki/helm-values/loki-stack.yaml \--waitCe que vous devez voir :
Release "loki" does not exist. Installing it now.level=WARN msg="this chart is deprecated"NAME: lokiLAST DEPLOYED: [date]NAMESPACE: observabilitySTATUS: deployedPatientez 1-2 minutes que les pods démarrent.
-
Vérifiez que Loki tourne
Fenêtre de terminal kubectl get pods -n observability -l app=lokiCe que vous devez voir :
NAME READY STATUS RESTARTS AGEloki-0 1/1 Running 0 2mLe pod
loki-0est enRunning. -
Vérifiez que Promtail tourne
Fenêtre de terminal kubectl get pods -n observability -l app.kubernetes.io/name=promtailCe que vous devez voir :
NAME READY STATUS RESTARTS AGEloki-promtail-xxxxx 1/1 Running 0 2mPromtail est un DaemonSet : il y a un pod par node de votre cluster. Si vous avez 3 nodes, vous verrez 3 pods.
-
Vérifiez que Promtail collecte des logs
Fenêtre de terminal kubectl logs -n observability -l app.kubernetes.io/name=promtail --tail=10Ce que vous devez voir : Des lignes mentionnant les fichiers de logs découverts et les streams créés. Par exemple :
level=info msg="Adding target" ...level=info msg="start" ...Si vous voyez des erreurs de connexion à Loki, attendez quelques secondes et réessayez.
Ajouter Loki comme datasource Grafana
Section intitulée « Ajouter Loki comme datasource Grafana »Grafana doit savoir où trouver Loki pour pouvoir l’interroger.
-
Ouvrez Grafana
Grâce au NodePort configuré dans le module 03, Grafana est accessible directement :
Fenêtre de terminal minikube service grafana -n observability --urlOu accédez directement à http://<MINIKUBE_IP>:30030
Connectez-vous avec
admin/admin. -
Allez dans Connections → Data sources
-
Cliquez sur “Add data source”
-
Cherchez et sélectionnez “Loki”
-
Configurez la connexion
Champ Valeur Name LokiURL http://loki:3100Laissez les autres champs par défaut.
-
Cliquez sur “Save & test”
Ce que vous devez voir : Un message vert “Data source successfully connected”.
Si vous utilisez le provisioning Grafana (recommandé en production), ajoutez Loki dans le fichier 03-grafana/helm-values/grafana.yaml :
# Dans le bloc datasources existant, ajoutez Lokidatasources: datasources.yaml: apiVersion: 1 datasources: - name: Prometheus type: prometheus url: http://prometheus-server.observability.svc.cluster.local:80 access: proxy isDefault: true editable: false
- name: Loki type: loki url: http://loki.observability.svc.cluster.local:3100 access: proxy isDefault: false editable: falsePuis appliquez la mise à jour :
helm upgrade grafana grafana/grafana \ -n observability \ -f 03-grafana/helm-values/grafana.yaml \ --waitExplorer les logs avec LogQL
Section intitulée « Explorer les logs avec LogQL »Accéder à Explore
Section intitulée « Accéder à Explore »-
Dans Grafana, cliquez sur l’icône boussole (Explore) dans le menu latéral
-
En haut, sélectionnez “Loki” dans le dropdown des datasources
-
Vous voyez : Un éditeur de requête et un sélecteur de plage temporelle
Votre première requête
Section intitulée « Votre première requête »Tapez cette requête dans l’éditeur :
{namespace="otel-demo"}Cliquez sur Run query (ou Shift+Enter).
Ce que vous devez voir :
- Une liste de logs avec des timestamps
- Chaque log affiche ses labels (namespace, pod, container)
- Les logs les plus récents en haut
Filtrer par labels
Section intitulée « Filtrer par labels »Les labels disponibles dépendent de ce que Promtail a collecté. Les labels Kubernetes courants sont :
| Label | Description | Exemple |
|---|---|---|
namespace | Le namespace Kubernetes | otel-demo |
pod | Le nom du pod | otel-demo-frontend-xyz |
container | Le nom du conteneur | frontend |
app | Le label app du pod | frontend |
job | Le job de scraping Promtail | kubernetes-pods |
Exemples de sélecteurs :
# Logs d'un namespace spécifique{namespace="otel-demo"}
# Logs d'un pod spécifique{namespace="otel-demo", pod="otel-demo-frontend-abc123"}
# Logs de tous les pods dont le nom commence par "frontend"{namespace="otel-demo", pod=~".*frontend.*"}
# Logs de tous les namespaces sauf kube-system{namespace!="kube-system"}Les opérateurs de sélection :
| Opérateur | Signification | Exemple |
|---|---|---|
= | Égal exactement | {namespace="otel-demo"} |
!= | Différent de | {namespace!="kube-system"} |
=~ | Correspond au regex | {pod=~".*frontend.*"} |
!~ | Ne correspond pas au regex | {pod!~".*test.*"} |
Filtrer par contenu
Section intitulée « Filtrer par contenu »Après avoir sélectionné les streams, vous pouvez filtrer les lignes par leur contenu :
{namespace="otel-demo"} |= "error"Cette requête retourne les logs du namespace otel-demo qui contiennent le mot “error”.
Les opérateurs de filtre de ligne :
| Opérateur | Signification | Exemple |
|---|---|---|
|= | Contient | {app="frontend"} |= "error" |
!= | Ne contient pas | {app="frontend"} != "health" |
|~ | Correspond au regex | {app="frontend"} |~ "status=(4|5)[0-9]{2}" |
!~ | Ne correspond pas au regex | {app="frontend"} !~ "DEBUG" |
Exemples pratiques :
# Logs contenant "error" (case-insensitive avec regex){namespace="otel-demo"} |~ "(?i)error"
# Logs contenant "error" mais pas "healthcheck"{namespace="otel-demo"} |= "error" != "healthcheck"
# Logs avec un code HTTP 4xx ou 5xx{namespace="otel-demo"} |~ "HTTP/(4|5)[0-9]{2}"
# Logs contenant une trace_id (pour la corrélation){namespace="otel-demo"} |~ "trace_id"Parser les logs JSON
Section intitulée « Parser les logs JSON »La plupart des applications modernes génèrent des logs en JSON. LogQL peut les parser.
Exemple de log JSON :
{"level":"error","msg":"Failed to process order","order_id":"12345","trace_id":"abc123"}Parser avec | json :
{namespace="otel-demo"} | jsonAprès | json, les champs JSON deviennent des labels que vous pouvez utiliser :
# Filtrer par niveau d'erreur{namespace="otel-demo"} | json | level="error"
# Filtrer par code HTTP{namespace="otel-demo"} | json | status_code >= 400
# Combiner plusieurs filtres{namespace="otel-demo"} | json | level="error" | service_name="checkout"Formater la sortie
Section intitulée « Formater la sortie »Vous pouvez modifier l’affichage des logs :
# Afficher seulement certains champs{namespace="otel-demo"} | json | line_format "{{.level}} - {{.msg}}"
# Format plus lisible{namespace="otel-demo"} | json | line_format "[{{.level}}] {{.service_name}}: {{.msg}}"Exercice 1 : Explorer les logs de l’application démo
Section intitulée « Exercice 1 : Explorer les logs de l’application démo »-
Listez tous les logs du namespace otel-demo
{namespace="otel-demo"}Observation : Beaucoup de logs ! Réduisez la plage temporelle à 15 minutes.
-
Filtrez sur les erreurs
{namespace="otel-demo"} |= "error"Combien de logs d’erreur voyez-vous ?
-
Parsez le JSON et filtrez par niveau
{namespace="otel-demo"} | json | level="error"Question : Y a-t-il une différence avec la requête précédente ?
(Oui, la précédente cherche le texte “error” n’importe où ; celle-ci cherche le champ JSON
levelégal à “error”) -
Identifiez les pods les plus bavards
{namespace="otel-demo"} | jsonRegardez le panneau latéral (si disponible) pour voir la distribution par pod.
-
Cherchez les logs avec un trace_id
{namespace="otel-demo"} | json | trace_id != ""Ces logs pourront être corrélés avec les traces Tempo (module 07).
LogQL : générer des métriques
Section intitulée « LogQL : générer des métriques »LogQL peut compter et mesurer les logs, transformant des logs en métriques.
Compter les logs
Section intitulée « Compter les logs »Logs par seconde :
rate({namespace="otel-demo"}[5m])Cette requête retourne le taux de logs par seconde sur les 5 dernières minutes, groupé par stream.
Logs par seconde, agrégés par pod :
sum by (pod) (rate({namespace="otel-demo"}[5m]))Logs par seconde, agrégés par niveau :
sum by (level) (rate({namespace="otel-demo"} | json [5m]))Total sur une période
Section intitulée « Total sur une période »# Nombre total de logs d'erreur sur la dernière heurecount_over_time({namespace="otel-demo"} |= "error" [1h])
# Par podsum by (pod) (count_over_time({namespace="otel-demo"} |= "error" [1h]))Pourcentage d’erreurs
Section intitulée « Pourcentage d’erreurs »# Ratio erreurs / total logs par service( sum by (service_name) ( rate({namespace="otel-demo"} | json | level="error" [5m]) ))/( sum by (service_name) ( rate({namespace="otel-demo"} | json [5m]) ))Exercice 2 : Métriques sur les logs
Section intitulée « Exercice 2 : Métriques sur les logs »-
Comptez les logs par seconde de chaque pod
sum by (pod) (rate({namespace="otel-demo"}[5m]))Ce que vous devez voir : Un graphique avec une ligne par pod.
-
Identifiez le top 5 des pods les plus bavards
topk(5, sum by (pod) (rate({namespace="otel-demo"}[5m]))) -
Comptez les erreurs par niveau de log
sum by (level) (rate({namespace="otel-demo"} | json [5m])) -
Alertez si plus de 10 erreurs par minute
D’abord, vérifiez le taux d’erreurs :
sum(rate({namespace="otel-demo"} | json | level="error" [1m])) * 60Cette valeur (erreurs par minute) peut être utilisée pour une alerte Grafana.
Configuration avancée de Promtail
Section intitulée « Configuration avancée de Promtail »Promtail fait plus que collecter les logs. Il peut les transformer.
Pipeline stages
Section intitulée « Pipeline stages »Les “stages” sont des étapes de traitement appliquées à chaque log :
pipeline_stages: # 1. Parser le format CRI (Container Runtime Interface) - cri: {}
# 2. Parser le JSON - json: expressions: level: level message: msg trace_id: trace_id
# 3. Ajouter des labels - labels: level: trace_id:
# 4. Filtrer (drop) les logs inutiles - match: selector: '{app="frontend"}' stages: - drop: expression: "healthcheck"
# 5. Modifier le contenu - replace: expression: "password=.*" replace: "password=***"Explication de chaque stage :
| Stage | Rôle | Exemple |
|---|---|---|
cri | Parse le format des logs Kubernetes (CRI) | Extrait le timestamp |
json | Parse le contenu JSON | Extrait level, msg, etc. |
labels | Ajoute des labels à partir des champs extraits | level devient un label filtrable |
drop | Supprime les logs correspondants | Ignore les healthchecks |
replace | Modifie le contenu | Masque les mots de passe |
Multiline (logs sur plusieurs lignes)
Section intitulée « Multiline (logs sur plusieurs lignes) »Les stack traces Java ou Python s’étendent sur plusieurs lignes :
2024-01-15 10:30:45 ERROR Something went wrongjava.lang.NullPointerException at com.example.MyClass.method(MyClass.java:42) at com.example.Main.main(Main.java:10)Configuration pour les regrouper :
pipeline_stages: - multiline: firstline: '^\d{4}-\d{2}-\d{2}' # Nouvelle entrée = commence par une date max_wait_time: 3sCréer un dashboard de logs
Section intitulée « Créer un dashboard de logs »Panel avec visualisation logs
Section intitulée « Panel avec visualisation logs »-
Créez un nouveau dashboard ou ouvrez un existant
-
Add panel → Visualization: Logs
-
Configurez la requête
- Data source : Loki
- Query :
{namespace="otel-demo"} | json
-
Dans Panel options
Option Valeur recommandée Time Show Unique labels Show Common labels Hide Wrap lines On -
Sauvegardez le panel
Panel avec métrique sur logs
Section intitulée « Panel avec métrique sur logs »-
Add panel → Visualization: Time series
-
Configurez la requête
sum by (level) (rate({namespace="otel-demo"} | json [5m])) -
Dans Panel options
- Title : “Log rate par niveau”
- Legend : Table, valeurs
-
Sauvegardez
Panel “erreurs par service”
Section intitulée « Panel “erreurs par service” »-
Add panel → Visualization: Bar gauge
-
Configurez la requête
sum by (service_name) (count_over_time({namespace="otel-demo"} | json | level="error" [1h])) -
Dans Panel options
- Title : “Erreurs par service (dernière heure)”
- Thresholds : 0=vert, 10=jaune, 50=rouge
-
Sauvegardez
Alerter sur les logs
Section intitulée « Alerter sur les logs »Grafana peut créer des alertes basées sur des requêtes Loki.
-
Allez dans Alerting → Alert rules
-
Cliquez sur “New alert rule”
-
Configurez la requête
-
Data source : Loki
-
Query :
sum(count_over_time({namespace="otel-demo"} | json | level="error" [5m]))
-
-
Configurez la condition
- Expression :
count > 10 - For : 5 minutes
- Expression :
-
Configurez les labels et annotations
Summary: Plus de 10 erreurs en 5 minutes dans otel-demo -
Sauvegardez
Rétention et estimation du stockage
Section intitulée « Rétention et estimation du stockage »Calculer l’espace nécessaire
Section intitulée « Calculer l’espace nécessaire »Loki compresse bien les logs (ratio ~10:1 typique).
Formule d’estimation :
Espace = (logs_bruts_par_jour_GB / 10) × jours_rétentionExemple :
- 50 GB de logs bruts par jour
- Rétention 7 jours
- Espace nécessaire ≈ (50 / 10) × 7 = 35 GB
Configuration de la rétention
Section intitulée « Configuration de la rétention »Dans les values Helm :
loki: config: limits_config: retention_period: 168h # 7 jours reject_old_samples: true reject_old_samples_max_age: 168h
table_manager: retention_deletes_enabled: true retention_period: 168hDépannage
Section intitulée « Dépannage »Les logs n’apparaissent pas dans Grafana
Section intitulée « Les logs n’apparaissent pas dans Grafana »| Étape | Commande/Action | Ce que vous cherchez |
|---|---|---|
| 1. Promtail tourne ? | kubectl get pods -n observability -l app.kubernetes.io/name=promtail | Pods Running |
| 2. Loki tourne ? | kubectl get pods -n observability -l app=loki | Pod Running |
| 3. Logs Promtail | kubectl logs -n observability -l app.kubernetes.io/name=promtail --tail=50 | Pas d’erreurs de connexion |
| 4. Les pods cibles ont des logs ? | kubectl logs -n otel-demo <pod> --tail=5 | Des logs s’affichent |
| 5. Datasource Loki OK ? | Grafana → Connections → Data sources → Loki → Save & test | ”Successfully connected” |
Erreur “too many outstanding requests”
Section intitulée « Erreur “too many outstanding requests” »Loki est surchargé.
| Cause | Solution |
|---|---|
| Trop de requêtes simultanées | Réduisez les dashboards actifs |
| Requêtes trop larges | Ajoutez des filtres de labels |
| Ressources insuffisantes | Augmentez memory/CPU de Loki |
Requête lente
Section intitulée « Requête lente »| Cause | Solution |
|---|---|
| Plage temporelle trop large | Réduisez à 1h ou moins |
| Pas de filtre de labels | Ajoutez {namespace="..."} au minimum |
| Trop de parsing JSON | Filtrez par texte d’abord (` |
Logs tronqués
Section intitulée « Logs tronqués »| Cause | Solution |
|---|---|
| Limite de taille de ligne | Augmentez max_line_size dans Promtail |
| Logs multilignes non groupés | Ajoutez une stage multiline |
Validation finale
Section intitulée « Validation finale »-
Loki et Promtail sont déployés
Fenêtre de terminal kubectl get pods -n observability -l "app in (loki)" && \kubectl get pods -n observability -l app.kubernetes.io/name=promtailLes pods sont en
Running. -
La datasource Loki fonctionne
Dans Grafana → Connections → Data sources → Loki : “Successfully connected”
-
Les logs sont collectés
Dans Grafana → Explore → Loki :
{namespace="otel-demo"}Des logs s’affichent.
-
Le parsing JSON fonctionne
{namespace="otel-demo"} | json | level="error"Des logs filtrés s’affichent.
-
Vous savez générer des métriques
sum by (pod) (rate({namespace="otel-demo"}[5m]))Un graphique s’affiche.
Ce que vous avez appris
Section intitulée « Ce que vous avez appris »- Loki indexe les labels, pas le contenu — ce qui le rend léger et économique
- Promtail collecte les logs des pods et les enrichit avec les labels Kubernetes
- LogQL ressemble à PromQL : sélecteurs de stream
{}+ filtres|= |~+ transformations| json - Parser JSON permet de filtrer sur les champs :
| json | level="error" - Les fonctions
rate()etcount_over_time()transforment les logs en métriques - Grafana Alerting peut créer des alertes basées sur des requêtes Loki
Prochaine étape
Section intitulée « Prochaine étape »Vous avez maintenant les métriques (Prometheus) et les logs (Loki). Il vous manque le troisième pilier de l’observabilité : les traces distribuées. Dans le prochain module, nous ajoutons Tempo pour suivre les requêtes à travers tous vos services.