Exploring the roots and future of Internet Relay Chat

Monitoring IRC avec Prometheus et Grafana

En 2019, un de nos serveurs IRC a subi un memory leak progressif pendant trois semaines. Personne ne s'en est rendu compte jusqu'a ce que le serveur OOM-kill le processus a 3h du matin. Les utilisateurs se sont reconnectes sur un autre noeud, mais nous avons perdu les logs et la configuration du runtime. Apres cet incident, j'ai installe Prometheus et Grafana. Depuis, je vois venir les problemes avant qu'ils n'arrivent. Le monitoring, c'est la difference entre reagir a une crise et la prevenir.

Pourquoi monitorer IRC

Un serveur IRC a l'air simple — un processus, quelques connexions, du texte. En realite, un IRCd en production genere des dizaines de metriques qu'il faut surveiller :

  • Connexions : Nombre d'utilisateurs connectes, taux de connexion/deconnexion, connexions par IP.
  • Canaux : Nombre de canaux actifs, taille des canaux, activite par canal.
  • Reseau : Bande passante entrante/sortante, latence entre serveurs links, netsplits.
  • Ressources : CPU, memoire, file descriptors, taille des send queues.
  • Securite : Tentatives de connexion echouees, K-lines et G-lines actives, connexions depuis des DNSBLs.

Sans monitoring, vous decouvrez les problemes quand les utilisateurs se plaignent. Avec Prometheus et Grafana, vous voyez les tendances, detectez les anomalies, et recevez des alertes avant que la situation ne degenere.

Pour les organisations qui gerent plusieurs services — IRC, web, bases de donnees — la mise en place d'un monitoring unifie est un investissement qui se rentabilise au premier incident evite. Des prestataires comme PowerITi peuvent deployer et configurer une stack d'observabilite complete (Prometheus, Grafana, Alertmanager) adaptee a votre infrastructure specifique.

Architecture de monitoring

# Architecture typique :
#
# [IRCd] --> [Exporter IRC] <-- [Prometheus] --> [Grafana]
#                                    |
#                              [Alertmanager]
#                                    |
#                              [Email/Slack/IRC]

# Composants :
# 1. Exporter IRC : collecte les metriques de l'IRCd
#    et les expose au format Prometheus (HTTP /metrics)
# 2. Prometheus : scrape les metriques periodiquement,
#    les stocke, et evalue les regles d'alerte
# 3. Grafana : visualise les metriques avec des dashboards
# 4. Alertmanager : route les alertes vers les canaux
#    de notification (email, Slack, ou meme IRC)

# Installation rapide (Docker Compose) :
# docker-compose.yml
version: '3'
services:
  prometheus:
    image: prom/prometheus
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
    ports:
      - "9090:9090"

  grafana:
    image: grafana/grafana
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin

  alertmanager:
    image: prom/alertmanager
    volumes:
      - ./alertmanager.yml:/etc/alertmanager/alertmanager.yml
    ports:
      - "9093:9093"

# prometheus.yml :
scrape_configs:
  - job_name: 'irc-exporter'
    scrape_interval: 30s
    static_configs:
      - targets: ['irc-exporter:9100']

Ecrire un exporter IRC

Il n'existe pas d'exporter Prometheus officiel pour IRC. Mais en ecrire un est simple — l'exporter se connecte a l'IRCd comme un operateur et collecte les statistiques via les commandes d'administration.

# Exporter IRC en Python
# (exporter_irc.py)

import time
import ssl
import socket
import re
from prometheus_client import start_http_server, Gauge, Counter

# Metriques Prometheus
USERS_TOTAL = Gauge('irc_users_total', 'Total connected users')
CHANNELS_TOTAL = Gauge('irc_channels_total', 'Total active channels')
SERVERS_TOTAL = Gauge('irc_servers_total', 'Total linked servers')
MEMORY_BYTES = Gauge('irc_memory_bytes', 'IRCd memory usage')
UPTIME_SECONDS = Gauge('irc_uptime_seconds', 'IRCd uptime')
CONNECTIONS = Counter('irc_connections_total', 'Total connections')
SEND_QUEUE = Gauge('irc_sendq_bytes', 'Send queue size')

class IRCExporter:
    def __init__(self, host, port, nick, oper_user, oper_pass):
        self.host = host
        self.port = port
        self.nick = nick
        self.oper_user = oper_user
        self.oper_pass = oper_pass
        self.sock = None

    def connect(self):
        raw = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.sock = ssl.wrap_socket(raw)
        self.sock.connect((self.host, self.port))
        self.send(f"NICK {self.nick}")
        self.send(f"USER exporter 0 * :IRC Exporter")
        time.sleep(2)
        self.send(f"OPER {self.oper_user} {self.oper_pass}")

    def send(self, msg):
        self.sock.send(f"{msg}\r\n".encode())

    def recv(self, timeout=5):
        self.sock.settimeout(timeout)
        data = b""
        try:
            while True:
                chunk = self.sock.recv(4096)
                if not chunk:
                    break
                data += chunk
        except socket.timeout:
            pass
        return data.decode('utf-8', errors='replace')

    def collect_stats(self):
        # LUSERS retourne les stats globales
        self.send("LUSERS")
        response = self.recv()

        # Parser les reponses numeriques
        for line in response.split('\n'):
            # 251: users and invisible
            m = re.search(r':There are (\d+) users', line)
            if m:
                USERS_TOTAL.set(int(m.group(1)))
            # 254: channels
            m = re.search(r'(\d+) :channels formed', line)
            if m:
                CHANNELS_TOTAL.set(int(m.group(1)))
            # 252: operators
            m = re.search(r'(\d+) :operator', line)
            if m:
                pass  # operator count metric

    def run(self, interval=30):
        self.connect()
        while True:
            self.collect_stats()
            time.sleep(interval)

if __name__ == '__main__':
    # Demarrer le serveur HTTP Prometheus
    start_http_server(9100)

    exporter = IRCExporter(
        host='127.0.0.1',
        port=6697,
        nick='prometheus-exporter',
        oper_user='exporter',
        oper_pass='motdepasse'
    )
    exporter.run()
Conseil : Pour UnrealIRCd, la commande /STATS fournit des metriques detaillees. STATS u donne l'uptime, STATS m les compteurs de commandes, STATS z les statistiques memoire. Pour InspIRCd, les modules m_httpd et m_httpd_stats exposent des statistiques en HTTP/JSON — plus simple a parser qu'un client IRC.

Metriques essentielles

# Metriques a collecter absolument :

# Utilisateurs
irc_users_total              # Nombre total de connectes
irc_users_local              # Connectes sur ce serveur
irc_users_invisible          # Avec mode +i
irc_operators_online         # Opers connectes

# Canaux
irc_channels_total           # Canaux actifs
irc_channels_by_size         # Distribution par taille

# Reseau
irc_servers_linked           # Serveurs dans le reseau
irc_messages_per_second      # Debit de messages
irc_bytes_sent_total         # Bande passante sortante
irc_bytes_received_total     # Bande passante entrante

# Ressources systeme (via node_exporter)
process_resident_memory_bytes  # Memoire RSS
process_cpu_seconds_total      # Temps CPU
process_open_fds               # File descriptors

# Securite
irc_kline_count              # K-lines actives
irc_gline_count              # G-lines actives
irc_connection_rejected_total # Connexions refusees
irc_dnsbl_hits_total         # Hits DNSBL

# Send queue (critique)
irc_sendq_max_bytes          # Plus grande send queue
# Si la send queue atteint le maximum, le client
# est deconnecte. Une send queue qui croit indique
# un client lent ou un flood.

Requetes PromQL

# Utilisateurs connectes sur 24h :
irc_users_total

# Taux de connexions par minute :
rate(irc_connections_total[5m]) * 60

# Memoire utilisee vs disponible :
process_resident_memory_bytes / 1024 / 1024  # En Mo

# Ratio messages/utilisateurs :
rate(irc_messages_per_second[5m]) / irc_users_total

# Croissance des canaux sur une semaine :
irc_channels_total - irc_channels_total offset 7d

# Alertes de send queue :
irc_sendq_max_bytes > 500000
# Si une send queue depasse 500 Ko, un client a un
# probleme de debit.

# Detection de netsplit :
changes(irc_servers_linked[5m]) > 0
# Si le nombre de serveurs change, c'est un split
# ou un rejoin.

# Anomalie de connexions (deviation) :
irc_users_total < (avg_over_time(irc_users_total[7d]) * 0.5)
# Si le nombre d'utilisateurs chute de 50% par rapport
# a la moyenne hebdomadaire, quelque chose ne va pas.

Alertes

# Fichier de regles Prometheus :
# /etc/prometheus/rules/irc_alerts.yml

groups:
- name: irc
  rules:

  - alert: IRCdDown
    expr: up{job="irc-exporter"} == 0
    for: 2m
    labels:
      severity: critical
    annotations:
      summary: "IRCd is down"
      description: "L'exporter IRC ne repond plus depuis 2 min"

  - alert: IRCdHighMemory
    expr: process_resident_memory_bytes{job="irc-exporter"} > 500 * 1024 * 1024
    for: 10m
    labels:
      severity: warning
    annotations:
      summary: "IRCd uses more than 500 MB RAM"
      description: "Memoire: {{ $value | humanize1024 }}"

  - alert: IRCdNetsplit
    expr: changes(irc_servers_linked[5m]) > 0
    labels:
      severity: warning
    annotations:
      summary: "Netsplit detected"

  - alert: IRCdUserDrop
    expr: irc_users_total < (avg_over_time(irc_users_total[7d]) * 0.5)
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "User count dropped 50% below weekly average"

# Configuration Alertmanager :
# alertmanager.yml
route:
  receiver: 'irc-ops'
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h

receivers:
- name: 'irc-ops'
  webhook_configs:
    # Webhook vers un bot IRC qui poste dans #ops
    - url: 'http://localhost:8080/alert'
  email_configs:
    - to: 'ops@votredomaine.com'
      from: 'prometheus@votredomaine.com'
      smarthost: 'smtp.votredomaine.com:587'
Attention : Les alertes doivent avoir un for (duree minimale) raisonnable. Sans for, un pic momentane declenche une alerte immediatement. Avec for: 5m, la condition doit persister 5 minutes avant de declencher. Trop d'alertes inutiles = alerte fatigue = alertes ignorees.

Dashboards Grafana

Un bon dashboard IRC a trois niveaux :

  • Vue d'ensemble : Un panneau unique avec les metriques cles. Utilisateurs, canaux, serveurs, uptime. C'est ce que vous regardez en premier pour savoir si tout va bien.
  • Tendances : Graphiques sur 24h, 7j, 30j. Croissance des utilisateurs, evolution de la memoire, bande passante. Les tendances revelent les problemes avant qu'ils ne deviennent critiques.
  • Debug : Metriques detaillees. Distribution des send queues, repartition par serveur, logs d'evenements. Utilise quand un probleme est identifie et que vous cherchez la cause.
# Panneaux recommandes pour le dashboard principal :

# Panneau 1 : Stat (nombre actuel)
# Titre : "Utilisateurs connectes"
# Query : irc_users_total

# Panneau 2 : Graph (historique)
# Titre : "Utilisateurs sur 24h"
# Query : irc_users_total
# Time range : 24h

# Panneau 3 : Graph (memoire)
# Titre : "Memoire IRCd"
# Query : process_resident_memory_bytes / 1024 / 1024
# Unit : MB

# Panneau 4 : Graph (messages)
# Titre : "Messages par seconde"
# Query : rate(irc_messages_per_second[5m])

# Panneau 5 : Stat (uptime)
# Titre : "Uptime"
# Query : irc_uptime_seconds
# Unit : duration (seconds)

# Panneau 6 : Table (serveurs)
# Titre : "Serveurs links"
# Query : irc_servers_linked

# Exporter le dashboard en JSON pour versioning :
# Dashboard Settings -> JSON Model -> Copier dans un fichier
# Versionner dans git avec la config Prometheus

Questions frequentes

Prometheus ou Nagios pour IRC ?

Prometheus. Nagios est concu pour le monitoring a base de checks (est-ce que le service repond ? oui/non). Prometheus est concu pour les metriques continues (combien de memoire, combien d'utilisateurs, quelle tendance). Pour IRC, les metriques continues sont bien plus utiles que les checks binaires.

L'exporter impacte-t-il les performances de l'IRCd ?

Marginalement. L'exporter envoie une commande LUSERS et STATS toutes les 30 secondes. C'est moins de charge qu'un utilisateur qui tape activement. Le seul risque est si l'exporter floode les commandes STATS — respectez un intervalle de scrape minimum de 15 secondes.

Combien de temps conserver les metriques ?

15 jours en resolution maximale et 1 an en resolution reduite (downsampling) est un bon compromis. Prometheus peut etre configure avec une retention de 15 jours, et Thanos ou Cortex pour le stockage longue duree. Pour un reseau IRC modeste, 15 jours suffisent generalement.