Changer le thème

Surveillance Ollama en production : logs et alertes Prometheus

Easton editorial illustration: agent rollout and rollback rail

Trois heures dix-sept du matin. Alerte rouge Slack : Ollama API timeout - service unavailable. Le système de support client est en ligne depuis deux semaines, avec des centaines d’appels par jour. Au déploiement, seuls des logs basiques étaient configurés — aucune surveillance ni alerte. Mémoire GPU saturée ? Processus planté ? Problème réseau ? Aucune visibilité. Retour à la normale seulement à six heures.

70 %
Projets IA qui n’atteignent pas la production
Source: Rapport Hyperion Consulting 2026

L’absence de surveillance en est l’une des causes principales.

Cet article partage une solution complète — de la configuration des logs à Prometheus + Grafana, jusqu’aux alertes AlertManager — avec des fichiers de config prêts à copier. En suivant ce guide, vous pouvez mettre en place une surveillance de niveau production en environ 30 minutes.

Les défis de la surveillance en production

Ollama n’est pas un service web ordinaire. C’est un « gros consommateur de ressources ». Chaque modèle chargé occupe 4 à 16 Go de mémoire (données Markaicode). Et le cold start — chargement du disque vers la mémoire — prend 10 à 30 secondes. Si le service redémarre après une panne, l’utilisateur attend une demi-minute avant toute réponse.

Les pièges que j’ai rencontrés :

Fuites mémoire et épuisement GPU. Après une longue exécution, Ollama oublie parfois de libérer la VRAM. J’ai vu une machine 24 Go de VRAM ne plus avoir que 2 Go disponibles après deux jours — toutes les nouvelles requêtes refusées. Le pire : je ne savais pas ce qui se passait jusqu’aux plaintes utilisateurs.

Accumulation de la file d’attente. L’inférence est lente — 5 à 20 secondes par requête. Des dizaines de requêtes simultanées font gonfler la file jusqu’au timeout. Comment savoir si la file s’accumule ? On devine.

Latence de chargement des modèles. En multi-modèles, le temps de chargement reste une boîte noire. L’utilisateur ne comprend pas la lenteur, vous non plus.

L’objectif de la surveillance est donc clair : disponibilité (le processus tourne-t-il ?), performance (quelle latence ?), utilisation des ressources (combien de VRAM reste-t-il ?), taux d’erreur (combien de requêtes échouent ?). Maîtriser ces quatre dimensions, c’est retrouver la sérénité.

Pour le choix d’outils, j’ai testé plusieurs combinaisons. Prometheus + Grafana suffit aux petites équipes ; pour tracer prompts et réponses LLM, Langfuse est excellent ; en entreprise, SigNoz basé sur OpenTelemetry unifie logs, métriques et traces. Je me concentre ici sur Prometheus — la base la plus universelle.

Configuration des logs et optimisation systemd

Démarrer Ollama est facile ; le faire tourner de façon stable commence par des logs corrects. J’ai déjà cherché des traces après un incident — rien n’était enregistré, ou le fichier faisait des dizaines de Go et a saturé le disque.

Configuration du service systemd

Avec l’installation officielle, un service systemd est créé automatiquement. La config par défaut est minimaliste — en production, il faut l’enrichir :

# /etc/systemd/system/ollama.service

[Unit]
Description=Ollama Service
After=network.target

[Service]
Type=simple
User=ollama
Group=ollama

# Répertoire de travail
WorkingDirectory=/usr/share/ollama

# Variables d'environnement
Environment="OLLAMA_HOST=0.0.0.0:11434"
Environment="OLLAMA_MODELS=/data/ollama/models"
Environment="OLLAMA_DEBUG=1"
Environment="OLLAMA_LOG_FORMAT=json"

# Limites de ressources (ajustez selon votre matériel)
LimitNOFILE=65535
LimitNPROC=4096
MemoryMax=32G

# Stratégie de redémarrage automatique
Restart=always
RestartSec=10

# Commande de démarrage
ExecStart=/usr/local/bin/ollama serve

# Sortie standard et erreurs
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

Quelques points d’expérience :

Restart=always et RestartSec=10 : redémarrage automatique après crash, avec 10 s de pause pour laisser le système respirer. Sans cet intervalle, après un épuisement mémoire, Ollama redémarre en boucle et les logs explosent.

MemoryMax=32G : limite la mémoire max d’Ollama. Essentiel si d’autres services tournent sur la machine. Sans limite, j’ai vu Ollama consommer 64 Go — impossible de se connecter en SSH.

OLLAMA_DEBUG=1 et OLLAMA_LOG_FORMAT=json : le mode debug en production facilite le diagnostic. Le format JSON permet un parsing automatisé.

Après modification, n’oubliez pas :

sudo systemctl daemon-reload
sudo systemctl restart ollama
sudo systemctl enable ollama  # démarrage au boot

Logs en déploiement Docker

Avec Docker, la gestion des logs est encore plus piégeuse. Par défaut, les logs vont dans /var/lib/docker/containers/ sans limite de taille.

Ma configuration docker-compose :

# docker-compose.yml
version: '3.8'

services:
  ollama:
    image: ollama/ollama:latest
    container_name: ollama
    restart: always
    ports:
      - "11434:11434"
    volumes:
      - ./ollama_data:/root/.ollama
    environment:
      - OLLAMA_HOST=0.0.0.0:11434
      - OLLAMA_DEBUG=1
    deploy:
      resources:
        limits:
          memory: 32G
    logging:
      driver: "json-file"
      options:
        max-size: "100m"
        max-file: "5"

max-size: "100m" : 100 Mo max par fichier. max-file: "5" : 5 fichiers conservés. Total max 500 Mo — suffisant sans saturer le disque.

Niveaux de log

Variables d’environnement Ollama :

VariableDescriptionRecommandation production
OLLAMA_DEBUG1 pour activer les logs détaillésRecommandé
OLLAMA_LOG_LEVELNiveau (INFO/DEBUG/WARN)INFO ou DEBUG
OLLAMA_LOG_FORMATFormat (text/json)JSON

Je laisse généralement DEBUG activé — l’espace disque coûte moins cher que des heures de diagnostic.

journalctl en pratique

Une fois configuré, consultez les logs avec journalctl :

# Logs en temps réel
sudo journalctl -u ollama -f

# 100 dernières lignes
sudo journalctl -u ollama -n 100

# Logs du jour
sudo journalctl -u ollama --since today

# Recherche par mot-clé
sudo journalctl -u ollama | grep -i "error"

# Export vers un fichier
sudo journalctl -u ollama --since "2026-04-12 00:00:00" > ollama-debug.log

Astuce avec logs JSON : filtrez avec jq :

sudo journalctl -u ollama -o json | jq 'select(.level=="error")'

Seuls les logs d’erreur — sans fouiller dans des pages d’INFO.

Surveillance Prometheus + Grafana

Les logs servent à l’analyse a posteriori ; la surveillance est l’œil qui prévient. Prometheus + Grafana, je l’utilise depuis plus de deux ans — la config est un peu lourde, mais c’est stable et la communauté est riche.

Déploiement ollama-exporter

Ollama n’expose pas directement de métriques Prometheus — il faut un exporter. J’utilise frcooper/ollama-exporter : peu d’étoiles (36), mais fonctionnel.

Deux modes : binaire direct ou Docker. Je recommande Docker :

# Ajouter le service exporter dans docker-compose.yml
services:
  ollama-exporter:
    image: frazco/ollama-exporter:latest
    container_name: ollama-exporter
    restart: always
    ports:
      - "9101:9101"
    environment:
      - OLLAMA_HOST=ollama:11434  # pointe vers le conteneur ollama
    depends_on:
      - ollama

Configuration Prometheus :

# prometheus.yml
global:
  scrape_interval: 30s  # intervalle d'échantillonnage, Markaicode recommande 30 s
  evaluation_interval: 30s

scrape_configs:
  - job_name: 'ollama-exporter'
    static_configs:
      - targets: ['ollama-exporter:9101']
        labels:
          instance: 'ollama-prod'

  # Surveillance GPU (NVIDIA)
  - job_name: 'nvidia-gpu'
    static_configs:
      - targets: ['localhost:9835']

Ajoutez Prometheus au docker-compose :

services:
  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    restart: always
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus_data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'

volumes:
  prometheus_data:

Métriques clés

Métriques collectées par ollama-exporter — les plus importantes :

MétriqueDescriptionPoint d’attention
ollama_requests_totalNombre total de requêtesCalcul du taux d’erreur
ollama_requests_failedRequêtes échouéesSurveillance directe
ollama_model_load_duration_secondsDurée de chargement modèlePerformance cold start
ollama_request_duration_secondsTemps de réponseLatence P95/P99
ollama_tokens_per_secondVitesse d’inférenceDébit

Métriques système (avec node-exporter) :

  • Utilisation CPU : node_cpu_seconds_total
  • Utilisation mémoire : node_memory_MemAvailable_bytes
  • Trafic réseau : node_network_receive_bytes_total

Configuration surveillance GPU

Le GPU est le cœur d’un service LLM — la surveillance est indispensable. J’utilise nvidia_gpu_prometheus_exporter :

# Installer NVIDIA GPU exporter
docker run -d \
  --name nvidia-exporter \
  --restart always \
  -p 9835:9835 \
  --gpus all \
  nvidia/gpu-prometheus-exporter:latest

Métriques clés :

  • nvidia_gpu_utilization : utilisation GPU
  • nvidia_gpu_memory_used_bytes : VRAM utilisée
  • nvidia_gpu_memory_free_bytes : VRAM restante
  • nvidia_gpu_temperature : température GPU

En multi-GPU, les métriques portent le label gpu_id — affichage par carte dans Grafana.

Configuration du dashboard Grafana

Voici un JSON importable. Sauvegardez-le, puis Import Dashboard dans Grafana :

{
  "dashboard": {
    "title": "Ollama Production Monitor",
    "panels": [
      {
        "title": "Request Rate",
        "type": "graph",
        "targets": [
          {
            "expr": "rate(ollama_requests_total[5m])",
            "legendFormat": "Requests/sec"
          }
        ],
        "gridPos": {"x": 0, "y": 0, "w": 12, "h": 6}
      },
      {
        "title": "Error Rate",
        "type": "gauge",
        "targets": [
          {
            "expr": "rate(ollama_requests_failed[5m]) / rate(ollama_requests_total[5m]) * 100",
            "legendFormat": "Error %"
          }
        ],
        "gridPos": {"x": 12, "y": 0, "w": 6, "h": 6}
      },
      {
        "title": "GPU Memory Usage",
        "type": "graph",
        "targets": [
          {
            "expr": "nvidia_gpu_memory_used_bytes / nvidia_gpu_memory_total_bytes * 100",
            "legendFormat": "GPU {{gpu_id}}"
          }
        ],
        "gridPos": {"x": 0, "y": 6, "w": 12, "h": 6}
      },
      {
        "title": "Response Latency P95",
        "type": "stat",
        "targets": [
          {
            "expr": "histogram_quantile(0.95, rate(ollama_request_duration_seconds_bucket[5m]))",
            "legendFormat": "P95 Latency"
          }
        ],
        "gridPos": {"x": 12, "y": 6, "w": 6, "h": 6}
      }
    ]
  },
  "overwrite": true
}

Disposition typique :

  • En haut à gauche : courbe du débit de requêtes, pics visibles
  • En haut à droite : jauge taux d’erreur, rouge au-delà de 5 %
  • En bas à gauche : courbes VRAM multi-GPU
  • En bas à droite : valeur latence P95

J’ajoute aussi un panneau Tokens/s pour comparer la vitesse d’inférence entre modèles.

Source de données Grafana

Après le démarrage du conteneur Grafana, configurez Prometheus :

  1. Connexion Grafana (admin/admin par défaut)
  2. Configuration -> Data Sources -> Add data source
  3. Choisir Prometheus, URL http://prometheus:9090
  4. Save & Test

Avec docker-compose, les conteneurs communiquent par nom de service.

Règles d’alerte et configuration AlertManager

La surveillance montre les problèmes ; les alertes vous disent « agissez maintenant ». J’ai commis l’erreur de tout mettre en critical — le téléphone vibrait des dizaines de fois par jour, j’ai fini par ignorer les alertes. Quand le vrai problème est arrivé, plus de réaction.

Stratégie de niveaux d’alerte

Trois niveaux, affinés après plusieurs incidents :

NiveauConditionsAction requise
CriticalService down, GPU >95 %, taux d’erreur >20 %Immédiat (Slack + push mobile)
WarningLatence >60 s, GPU >80 %, taux d’erreur >5 %Sous 1 h (Slack uniquement)
InfoChangement de modèle, nouveau déploiementJournalisation (récap email)

Principe clé : peu d’alertes Critical — chacune doit alerter vraiment.

Règles d’alerte Prometheus

Dans prometheus.yml, ajoutez :

rule_files:
  - 'ollama_alerts.yml'

Fichier ollama_alerts.yml :

# ollama_alerts.yml
groups:
  - name: ollama_critical
    rules:
      # Alerte service down
      - alert: OllamaServiceDown
        expr: up{job="ollama-exporter"} == 0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "Service Ollama indisponible"
          description: "Impossible de joindre l'exporter Ollama, le service est peut-être arrêté"

      # Alerte mémoire GPU (>95 %)
      - alert: GPUMemoryCritical
        expr: nvidia_gpu_memory_used_bytes / nvidia_gpu_memory_total_bytes > 0.95
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "Mémoire GPU presque épuisée"
          description: "GPU {{ gpu_id }} : utilisation mémoire >95 %, actuellement {{ $value | humanizePercentage }}"

      # Alerte taux d'erreur élevé
      - alert: HighErrorRate
        expr: rate(ollama_requests_failed[5m]) / rate(ollama_requests_total[5m]) > 0.20
        for: 3m
        labels:
          severity: critical
        annotations:
          summary: "Taux d'erreur des requêtes trop élevé"
          description: "Taux d'erreur >20 % sur les 5 dernières minutes, vérifiez les logs"

  - name: ollama_warning
    rules:
      # Alerte temps de réponse
      - alert: SlowResponseTime
        expr: histogram_quantile(0.95, rate(ollama_request_duration_seconds_bucket[5m])) > 60
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Latence P95 trop élevée"
          description: "95 % des requêtes dépassent 60 secondes"

      # Avertissement mémoire GPU
      - alert: GPUMemoryWarning
        expr: nvidia_gpu_memory_used_bytes / nvidia_gpu_memory_total_bytes > 0.80
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Utilisation mémoire GPU élevée"
          description: "GPU {{ gpu_id }} : utilisation mémoire >80 %"

      # Avertissement taux d'erreur
      - alert: ErrorRateWarning
        expr: rate(ollama_requests_failed[5m]) / rate(ollama_requests_total[5m]) > 0.05
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Taux d'erreur en hausse"
          description: "Taux d'erreur >5 % sur les 5 dernières minutes"

Points importants :

  • for: Xm : déclenchement après X minutes continues, évite les faux positifs sur pics instantanés
  • Seuil GPU 95 % : au-delà, les problèmes arrivent quasi immédiatement en pratique
  • rate() pour le taux d’erreur : les valeurs absolues ne suffisent pas, il faut la tendance

Configuration AlertManager

AlertManager envoie les alertes. Fichier alertmanager.yml :

global:
  resolve_timeout: 5m

# Routage
route:
  group_by: ['severity', 'alertname']
  group_wait: 30s      # attendre 30 s pour regrouper
  group_interval: 5m   # intervalle entre alertes du même groupe
  repeat_interval: 3h  # répétition si non résolu
  routes:
    - match:
        severity: critical
      receiver: 'critical-alerts'
      continue: false
    - match:
        severity: warning
      receiver: 'warning-alerts'
      continue: false
    - match:
        severity: info
      receiver: 'info-alerts'

# Récepteurs
receivers:
  - name: 'critical-alerts'
    slack_configs:
      - api_url: 'https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK'
        channel: '#ollama-critical'
        send_resolved: true
        title: '{{ .Status | toUpper }}: {{ .CommonAnnotations.summary }}'
        text: '{{ .CommonAnnotations.description }}'

  - name: 'warning-alerts'
    slack_configs:
      - api_url: 'https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK'
        channel: '#ollama-monitor'
        send_resolved: true

  - name: 'info-alerts'
    email_configs:
      - to: '[email protected]'
        send_resolved: true

Configuration Slack Webhook

  1. Créer une App Slack (ou Incoming Webhooks)
  2. Ajouter l’URL Webhook dans api_url
  3. Canaux séparés : critical dédié, warning général

J’ajoute aussi une notification mobile. PagerDuty et OpsGenie s’intègrent à AlertManager. Gratuitement, un bot Telegram fonctionne aussi.

Silences et règles d’inhibition

Parfois il faut couper les alertes temporairement — maintenance par exemple. Dans l’UI AlertManager :

# Accéder à l'UI AlertManager
http://your-server:9093

# Silences -> New Silence
# Définir durée et labels correspondants

Ou via API :

curl -X POST http://localhost:9093/api/v1/silences \
  -d '{
    "matchers": [{"name": "alertname", "value": "OllamaServiceDown", "isRegex": false}],
    "startsAt": "2026-04-12T10:00:00Z",
    "endsAt": "2026-04-12T12:00:00Z",
    "createdBy": "admin",
    "comment": "Scheduled maintenance"
  }'

Outils de surveillance LLM avancés

Prometheus + Grafana couvre l’infrastructure, mais les LLM ont des besoins spécifiques : traçage des prompts, coût en tokens, évaluation de la qualité. Difficile avec les outils classiques.

Langfuse : traçage LLM et gestion des prompts

Langfuse est une plateforme de surveillance dédiée aux applications LLM, open source MIT, auto-hébergeable. Fonctionnalités :

  • Tracer chaque conversation : prompt d’entrée, réponse, tokens, durée
  • Gestion des versions de prompts : comparer l’effet des modifications
  • Évaluation qualité : feedback utilisateur, annotation manuelle, suivi de la qualité

L’intégration est simple — Langfuse propose un adaptateur Ollama :

# Exemple d'intégration Python
from langfuse import Langfuse
import requests

langfuse = Langfuse(
    public_key="pk-xxx",
    secret_key="sk-xxx",
    host="https://cloud.langfuse.com"  # ou adresse auto-hébergée
)

# Enregistrer chaque appel
trace = langfuse.trace(
    name="ollama-chat",
    input={"prompt": user_prompt},
    metadata={"model": "llama3.1"}
)

response = requests.post(
    "http://localhost:11434/api/generate",
    json={"model": "llama3.1", "prompt": user_prompt}
)

trace.update(
    output=response.json()["response"],
    metadata={"tokens": response.json().get("eval_count", 0)}
)

Déploiement auto-hébergé avec Docker :

services:
  langfuse-server:
    image: langfuse/langfuse:latest
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgres://user:pass@db:5432/langfuse
      - NEXTAUTH_SECRET=your-secret

Avec LangChain, l’intégration est encore plus simple via le callback handler officiel.

SigNoz : surveillance unifiée OpenTelemetry

SigNoz est une plateforme d’observabilité basée sur OpenTelemetry — logs, métriques et traces en un seul endroit. Plus besoin de maintenir Prometheus, Jaeger et ELK séparément.

Pour les applications LLM, le traçage SigNoz montre la chaîne complète : entrée API → inférence modèle → requête base de données.

Le déploiement demande des ressources — minimum 4 Go RAM. Docker Compose officiel en une commande :

git clone https://github.com/SigNoz/signoz.git
cd signoz/deploy/docker
docker compose up -d

Recommandations de choix d’outils

Selon le contexte :

ScénarioSolution recommandéeRaison
Petite équipe (<5 personnes)Prometheus + GrafanaSimple, communauté riche
Traçage de prompts requisPrometheus + LangfuseLangfuse pour la couche LLM, complémentaire
Entreprise multi-servicesSigNoz + OpenTelemetryPlateforme unifiée, coût ops réduit
Cloud natif purService managéMoins d’effort ops

Mon setup actuel : Prometheus + Grafana + Langfuse. Prometheus pour l’infrastructure, Langfuse pour la couche application LLM — responsabilités claires.

Pour conclure

En résumé : n’attendez pas la panne pour penser à la surveillance.

Mon alerte à trois heures du matin m’a conduit à cette solution complète. Mon service Ollama tourne depuis plus d’un an ; quelques alertes mémoire GPU, toutes traitées au niveau Warning — plus de réveil nocturne.

Le coût de mise en place reste modeste. J’ai regroupé tous les fichiers de config :

  • Configuration service systemd
  • Docker Compose complet (Ollama + Exporter + Prometheus + Grafana)
  • Règles d’alerte Prometheus
  • Modèle AlertManager
  • JSON dashboard Grafana

Le dépôt GitHub est en bas de l’article. Configuré, comptez 20 minutes si vous êtes à l’aise, une demi-heure pour débuter.

Prochaines étapes :

  1. Démarrer avec Prometheus + Grafana pour les métriques de base
  2. Observer 3 à 5 jours pour connaître les plages normales
  3. Ajuster les seuils d’alerte selon la réalité
  4. Ajouter Langfuse si vous devez tracer les prompts

La surveillance, c’est un investissement unique à retour continu. J’espère que vous n’aurez pas besoin d’une alerte à trois heures du matin pour l’apprendre.


Dépôt de configuration : github.com/yourname/ollama-monitoring-config (lien exemple — remplacez en déploiement réel)

Articles de la série :

FAQ

Quelles métriques essentielles surveiller pour Ollama en production ?
Quatre dimensions : disponibilité du service (processus actif), performance (latence P95/P99), utilisation mémoire GPU (éviter l'épuisement) et taux d'erreur des requêtes (suivre les tendances anormales).
Quelle différence entre Prometheus + Grafana et Langfuse ?
Prometheus + Grafana surveille l'infrastructure (CPU, GPU, mémoire, volume de requêtes) ; Langfuse se concentre sur la couche application LLM (traçage des prompts, coût en tokens, évaluation de la qualité des réponses). Les deux sont complémentaires — combinez-les.
Comment définir des seuils d'alerte raisonnables ?
Trois niveaux recommandés : Critical (GPU >95 %, taux d'erreur >20 %, service down) à traiter immédiatement ; Warning (GPU >80 %, taux d'erreur >5 %) à consulter sous 1 h. L'essentiel : peu d'alertes Critical — chacune doit compter.
Comment éviter la croissance illimitée des logs en déploiement Docker ?
Dans docker-compose.yml, ajoutez max-size: "100m" et max-file: "5" dans logging : un fichier max 100 Mo, 5 fichiers conservés, soit 500 Mo au total.
Comment surveiller chaque carte GPU séparément ?
Les métriques de nvidia_gpu_prometheus_exporter portent le label gpu_id. Dans Grafana, utilisez {{gpu_id}} comme legendFormat en PromQL pour afficher chaque carte individuellement.
Comment diagnostiquer rapidement une alerte à 3 h du matin ?
Commencez par la courbe mémoire GPU (saturation ?), puis la tendance du taux d'erreur (pic brutal ou dégradation progressive), enfin journalctl pour les messages d'erreur. Cette séquence permet de trouver la cause en 10 minutes.

12 min de lecture · Publié le: 12 avr. 2026 · Mis à jour le: 27 juil. 2026

Commentaires

Connectez-vous avec GitHub pour laisser un commentaire

Easton BlogEaston Blog