33800 Docs

← Retour

Proposition : Centralisation Logs sur O2switch

Date : 15-01-2026 Status : IMPLEMENTEE Priorité : HAUTE Estimation : ~4h


Problème actuel

Grafana ne voit que 3 sources de logs (containers Docker prod-portainer via Promtail).

Ce qui manque :

Source Type État actuel
PVE Système + crons Non collecté
Proxmox 155 Système Non collecté
nginx Access + error logs Fichiers locaux seulement
gitlab Application logs Non collecté
dev-portainer Containers DEV Non collecté
jellyfin Application Non collecté
vscode IDE logs Non collecté
win11 Windows events Non collecté
Crons (tous) Exécution Syslog local seulement

Architecture proposée

┌─────────────────────────────────────────────────────────────────────────────┐
│                              SOURCES                                         │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│   PVE          Proxmox155      nginx        prod-portainer    dev-portainer │
│     │              │             │                │                 │        │
│     └──────────────┴─────────────┴────────────────┴─────────────────┘        │
│                                  │                                           │
│                           [Promtail agents]                                  │
│                                  │                                           │
│                                  ▼                                           │
│                         ┌──────────────┐                                     │
│                         │     LOKI     │  (prod-portainer:3100)              │
│                         │  Hot storage │  Rétention: 7 jours                 │
│                         └──────────────┘                                     │
│                                  │                                           │
│                           Sync quotidien                                     │
│                                  │                                           │
│                                  ▼                                           │
│                         ┌──────────────┐                                     │
│                         │   O2SWITCH   │  Cold storage                       │
│                         │  ~/logs/     │  Rétention: 90 jours                │
│                         └──────────────┘                                     │
│                                  │                                           │
│                                  ▼                                           │
│                         ┌──────────────┐                                     │
│                         │   GRAFANA    │  Visualisation                      │
│                         │  Loki + Logs │  Hot (7j) + Cold (90j)              │
│                         └──────────────┘                                     │
└─────────────────────────────────────────────────────────────────────────────┘

Convention de nommage

Labels Loki (obligatoires)

labels:
  env: "prod" | "dev"           # Environnement
  host: "pve" | "nginx" | ...   # Machine source
  type: "system" | "app" | "cron" | "access" | "error"
  service: "docker" | "nginx" | "gitlab" | "zfs" | ...
  job: "promtail-{host}"        # Identifiant collecteur

Exemples de labeling

Source Labels
Nginx access env=prod, host=nginx, type=access, service=nginx
Docker container env=prod, host=prod-portainer, type=app, service=ntfy
Cron PVE env=prod, host=pve, type=cron, service=rsync-backup
Système PVE env=prod, host=pve, type=system, service=syslog
GitLab app env=prod, host=gitlab, type=app, service=gitlab

Fichiers O2switch (archivage)

~/logs/
├── prod/
│   ├── pve/
│   │   ├── system/
│   │   │   └── 2026-01-15.log.gz
│   │   └── cron/
│   │       └── 2026-01-15.log.gz
│   ├── nginx/
│   │   ├── access/
│   │   │   └── 2026-01-15.log.gz
│   │   └── error/
│   │       └── 2026-01-15.log.gz
│   ├── prod-portainer/
│   │   ├── docker/
│   │   │   └── 2026-01-15.log.gz
│   │   └── containers/
│   │       ├── ntfy-2026-01-15.log.gz
│   │       ├── loki-2026-01-15.log.gz
│   │       └── ...
│   └── gitlab/
│       └── app/
│           └── 2026-01-15.log.gz
└── dev/
    └── dev-portainer/
        └── ...

Format fichier : {service}-{YYYY-MM-DD}.log.gz ou {YYYY-MM-DD}.log.gz


Phase 1 : Déployer Promtail sur toutes les machines (~1h30)

1.1 PVE (système + crons)

# /etc/promtail/config.yml
server:
  http_listen_port: 9080

positions:
  filename: /var/lib/promtail/positions.yaml

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

scrape_configs:
  # Syslog système
  - job_name: pve-system
    static_configs:
      - targets: [localhost]
        labels:
          env: prod
          host: pve
          type: system
          service: syslog
          __path__: /var/log/syslog
    pipeline_stages:
      - regex:
          expression: '^(?P<timestamp>\w+\s+\d+\s+[\d:]+)\s+(?P<hostname>\S+)\s+(?P<process>[^:]+):\s+(?P<message>.*)$'

  # Crons
  - job_name: pve-cron
    static_configs:
      - targets: [localhost]
        labels:
          env: prod
          host: pve
          type: cron
          service: cron
          __path__: /var/log/cron.log

  # Monitoring script
  - job_name: pve-monitoring
    static_configs:
      - targets: [localhost]
        labels:
          env: prod
          host: pve
          type: cron
          service: monitoring-33800
          __path__: /var/log/monitoring-33800.log

  # ZFS events
  - job_name: pve-zfs
    static_configs:
      - targets: [localhost]
        labels:
          env: prod
          host: pve
          type: system
          service: zfs
          __path__: /var/log/zfs*.log

1.2 Nginx (access + error)

# /etc/promtail/config.yml
server:
  http_listen_port: 9080

positions:
  filename: /var/lib/promtail/positions.yaml

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

scrape_configs:
  # Access logs globaux
  - job_name: nginx-access
    static_configs:
      - targets: [localhost]
        labels:
          env: prod
          host: nginx
          type: access
          service: nginx
          __path__: /var/log/nginx/access.log

  # Error logs globaux
  - job_name: nginx-error
    static_configs:
      - targets: [localhost]
        labels:
          env: prod
          host: nginx
          type: error
          service: nginx
          __path__: /var/log/nginx/error.log

  # Logs par domaine (wildcard)
  - job_name: nginx-domains
    static_configs:
      - targets: [localhost]
        labels:
          env: prod
          host: nginx
          type: access
          service: nginx-domains
          __path__: /var/log/nginx/*.access.log
    pipeline_stages:
      - regex:
          source: filename
          expression: '/var/log/nginx/(?P<domain>[^.]+)\..*\.access\.log'
      - labels:
          domain:

1.3 GitLab

# /etc/promtail/config.yml
scrape_configs:
  - job_name: gitlab-app
    static_configs:
      - targets: [localhost]
        labels:
          env: prod
          host: gitlab
          type: app
          service: gitlab
          __path__: /var/log/gitlab/**/*.log

1.4 Proxmox 155

# /etc/promtail/config.yml
scrape_configs:
  - job_name: proxmox155-system
    static_configs:
      - targets: [localhost]
        labels:
          env: dev
          host: proxmox155
          type: system
          service: syslog
          __path__: /var/log/syslog

1.5 dev-portainer

# Même config que prod-portainer mais avec env: dev
scrape_configs:
  - job_name: dev-docker
    docker_sd_configs:
      - host: unix:///var/run/docker.sock
    relabel_configs:
      - source_labels: [__meta_docker_container_name]
        target_label: container
      - replacement: dev
        target_label: env
      - replacement: dev-portainer
        target_label: host
      - replacement: app
        target_label: type

Phase 2 : Enrichir Promtail prod-portainer (~30min)

Modifier /stock_8to/33800-stack/docker/stacks/monitoring/promtail-config.yml :

server:
  http_listen_port: 9080

positions:
  filename: /tmp/positions.yaml

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

scrape_configs:
  - job_name: docker-containers
    docker_sd_configs:
      - host: unix:///var/run/docker.sock
        refresh_interval: 5s
    relabel_configs:
      # Nom du container
      - source_labels: ['__meta_docker_container_name']
        regex: '/(.*)'
        target_label: container

      # Stack (compose project)
      - source_labels: ['__meta_docker_container_label_com_docker_compose_project']
        target_label: stack

      # Service (compose service)
      - source_labels: ['__meta_docker_container_label_com_docker_compose_service']
        target_label: service

      # Labels fixes
      - replacement: prod
        target_label: env
      - replacement: prod-portainer
        target_label: host
      - replacement: app
        target_label: type

    pipeline_stages:
      - json:
          expressions:
            log: log
            stream: stream
            time: time
      - timestamp:
          source: time
          format: RFC3339Nano
      - output:
          source: log

Phase 3 : Archivage O2switch (~1h)

3.1 Script d'export quotidien

#!/bin/bash
# /stock_8to/33800-stack/scripts/logs/sync-logs-o2switch.sh

set -e

DATE=$(date -d "yesterday" +%Y-%m-%d)
LOKI_URL="http://192.168.1.12:3100"
O2SWITCH="nowhere84@ssh-nowhere84.o2switch.net"
REMOTE_DIR="~/logs"
TMP_DIR="/tmp/logs-export"

mkdir -p "$TMP_DIR"

# Fonction export par query
export_logs() {
    local query="$1"
    local output="$2"
    local start="${DATE}T00:00:00Z"
    local end="${DATE}T23:59:59Z"

    curl -s -G "$LOKI_URL/loki/api/v1/query_range" \
        --data-urlencode "query=$query" \
        --data-urlencode "start=$start" \
        --data-urlencode "end=$end" \
        --data-urlencode "limit=100000" \
        | jq -r '.data.result[].values[][1]' \
        > "$TMP_DIR/$output"

    if [ -s "$TMP_DIR/$output" ]; then
        gzip "$TMP_DIR/$output"
        echo "Exported: $output.gz"
    fi
}

# Export par source
export_logs '{env="prod",host="pve",type="system"}' "prod/pve/system/$DATE.log"
export_logs '{env="prod",host="pve",type="cron"}' "prod/pve/cron/$DATE.log"
export_logs '{env="prod",host="nginx",type="access"}' "prod/nginx/access/$DATE.log"
export_logs '{env="prod",host="nginx",type="error"}' "prod/nginx/error/$DATE.log"
export_logs '{env="prod",host="prod-portainer",type="app"}' "prod/prod-portainer/docker/$DATE.log"
export_logs '{env="prod",host="gitlab",type="app"}' "prod/gitlab/app/$DATE.log"
export_logs '{env="dev",host="dev-portainer"}' "dev/dev-portainer/$DATE.log"

# Sync vers O2switch
rsync -avz --mkpath "$TMP_DIR/" "$O2SWITCH:$REMOTE_DIR/"

# Cleanup
rm -rf "$TMP_DIR"

echo "Logs $DATE synced to O2switch"

3.2 Cron PVE

# /etc/cron.d/sync-logs-o2switch
0 2 * * * gouroubleu /stock_8to/33800-stack/scripts/logs/sync-logs-o2switch.sh >> /var/log/sync-logs.log 2>&1

3.3 Structure O2switch

ssh $O2SWITCH "mkdir -p ~/logs/{prod,dev}/{pve,nginx,prod-portainer,gitlab,proxmox155,dev-portainer}/{system,cron,access,error,app,docker}"

Phase 4 : Réduire rétention Loki local (~15min)

Modifier loki-config.yml :

limits_config:
  retention_period: 168h  # 7 jours (au lieu de 720h/30j)

compactor:
  retention_enabled: true
  retention_delete_delay: 2h

Avantage : Loki stocke 7 jours en "hot", O2switch stocke 90 jours en "cold".


Phase 5 : Dashboards Grafana (~45min)

5.1 Dashboard "Logs Overview"

Panels :

5.2 Variables Grafana

$env: prod, dev
$host: pve, nginx, prod-portainer, gitlab, dev-portainer, proxmox155
$type: system, app, cron, access, error
$service: (dynamique depuis labels)

5.3 Queries utiles

# Tous les logs d'un host
{host="$host"}

# Erreurs seulement
{type="error"} |= "error" or |= "ERROR" or |= "Error"

# Crons échoués
{type="cron"} |= "failed" or |= "error"

# Nginx 5xx
{host="nginx", type="access"} |~ " 5[0-9]{2} "

# Logs d'un container spécifique
{host="prod-portainer", container="ntfy"}

Résumé des fichiers à créer/modifier

Action Fichier Machine
Créer /etc/promtail/config.yml PVE
Créer /etc/promtail/config.yml nginx
Créer /etc/promtail/config.yml gitlab
Créer /etc/promtail/config.yml proxmox155
Créer /etc/promtail/config.yml dev-portainer
Modifier promtail-config.yml prod-portainer (stack monitoring)
Modifier loki-config.yml prod-portainer (rétention 7j)
Créer sync-logs-o2switch.sh PVE
Créer /etc/cron.d/sync-logs-o2switch PVE
Créer Structure ~/logs/ O2switch
Créer Dashboard "Logs Overview" Grafana

Résultat attendu

Métrique Avant Après
Sources dans Grafana 3 15+
Machines monitorées 1 6
Rétention hot (Loki) 30j 7j
Rétention cold (O2switch) 0 90j
Stockage local logs ~1.2 GB ~300 MB
Archivage distant Non Oui

Validation