Ma VM DNS écrivait près de 28 Go par jour sur Ceph. Avec un tel chiffre, je m’attendais à trouver un volume anormal de requêtes, un service bavard ou un buffer Vector qui débordait.
La réalité tenait dans une base SQLite de 12 Mo.
trust-dns-01 résout environ deux à trois requêtes par seconde. Ce n’est ni une attaque ni une charge impressionnante. Pourtant, le stockage travaillait suffisamment pour que la VM passe bien plus de temps à attendre ses entrées-sorties qu’à calculer.
Le DNS n’était pas débordé
Le 30 juillet 2026, j’ai repris les métriques sur les dernières 24 heures et l’état réel de la machine :
| Mesure | Valeur observée |
|---|---|
| trafic DNS | environ 2,5 requêtes/s |
| CPU actif, hors attente I/O | 6,3 % en moyenne |
| attente I/O (iowait) | 29,1 % en moyenne |
| données écrites sur le disque | 27,89 Go en 24 h |
| occupation du disque | 8,8 Go sur 20 Go |
La nuance entre CPU actif et iowait est importante. Un graphique calculé simplement comme 100 - idle donne l’impression que le processeur est occupé à 35 %. En réalité, sur cette VM limitée à un vCPU, près de 29 points correspondent au temps passé à attendre le stockage.
Le serveur DNS lui-même ne manque donc pas de puissance. Il attend surtout Ceph.
Une petite base qui réécrit sans arrêt
Technitium possède une application nommée Query Logs (Sqlite). Elle permet de retrouver précisément quel client a demandé quel domaine, à quelle heure et avec quelle réponse.
Sur mon instance, sa configuration contient notamment :
| |
La base fait environ 12 Mo et conserve autour de 10 000 entrées. Pendant une vérification, elle contenait 12 102 lignes couvrant un peu plus de 80 minutes, soit environ 2,5 requêtes par seconde.
Ce léger dépassement est normal : le plafond n’est pas appliqué à chaque insertion. Le nettoyage se déclenche toutes les quinze minutes. Entre deux passages, la table continue de grossir.
Dit comme cela, rien d’inquiétant. Le détail qui change tout se trouve dans les réglages effectifs de SQLite :
| |
La valeur 2 correspond au mode FULL. Avec le journal DELETE, SQLite crée un journal de rollback pour la transaction, synchronise les données, puis supprime le journal une fois le commit terminé. Ces choix privilégient la durabilité, comme l’explique la documentation officielle de SQLite sur les modes de journalisation et la synchronisation.
Ici, chaque nouvelle requête ajoute une ligne et met à jour treize index en plus de la clé primaire. L’application consomme les requêtes disponibles sans attendre de remplir un gros lot ; à faible trafic, elle peut donc committer très peu de lignes à la fois. Lorsque la limite est dépassée, les anciennes lignes sont aussi supprimées. La base reste petite, mais elle tourne en permanence : insertions, index, suppressions, journal, synchronisations.
Sur un disque local, cette activité passerait probablement inaperçue. Sur un stockage distribué comme Ceph, une succession de petites écritures synchrones coûte beaucoup plus cher que sa taille apparente. Le fichier querylogs.db-journal, présent et ouvert pendant le diagnostic, était le petit témoin de cette mécanique.
Deux mesures supplémentaires renforcent le diagnostic : le compteur du processus Technitium indiquait environ 15,5 Go écrits en 23 heures et, sur un échantillon de quinze secondes, 32 nouvelles requêtes ont accompagné 2,97 Mo d’écritures processus.
Je garde tout de même une réserve : Technitium écrit aussi ses statistiques et ses journaux, tandis que la métrique des 27,89 Go couvre toute la VM. Query Logs est la cause principale la mieux étayée, mais la preuve définitive viendra de la comparaison sur 24 heures après sa désactivation. Sans cet avant/après, lui attribuer chaque octet serait aller un peu vite.
L’espace disque racontait une autre histoire
L’amplification d’écriture n’explique pas à elle seule les 8,8 Go déjà occupés. Les plus gros consommateurs évitables étaient ailleurs :
| Données | Taille observée |
|---|---|
| sauvegardes Technitium | 3,7 Gio |
| statistiques DNS | 568 Mio |
| journaux systemd | 623 Mio |
Le script de sauvegarde actuel supprime les archives âgées de plus de 30 jours. Cela laisse tout de même 36 fichiers, car une sauvegarde quotidienne pèse désormais entre 100 et 123 Mo et quelques sauvegardes manuelles s’ajoutent au lot.
Ce sont donc deux problèmes distincts :
- Query Logs use le stockage avec beaucoup de petites écritures ;
- les sauvegardes, statistiques et journaux consomment réellement l’espace disponible.
Supprimer la base SQLite ne libérerait qu’une douzaine de mégaoctets. Pour faire maigrir la VM, il faut aussi agir sur les rétentions.
Garder les requêtes, sans garder SQLite
Ma première idée était de désactiver complètement les journaux détaillés et de conserver seulement les statistiques agrégées dans Grafana. C’était efficace, mais je perdais une fonction à laquelle je tiens : pouvoir rechercher les requêtes DNS dans VictoriaLogs.
Le compromis que je prépare est donc le suivant :
| |
L’application officielle Log Exporter de Technitium peut expédier les requêtes par HTTP ou Syslog sans les écrire dans un fichier local. Vector recevra les lots en mémoire, extraira les champs utiles — client, nom demandé, type, code de réponse et temps de traitement — puis les enverra vers VictoriaLogs.
Vector n’est pas seulement là pour faire joli au milieu du schéma. Le sink HTTP utilisé par Technitium produit des lots JSON enveloppés par Serilog, alors que l’endpoint /insert/jsonline de VictoriaLogs attend un objet JSON par ligne. Il faut donc convertir et normaliser les événements.
Je préfère un buffer mémoire borné pour ce flux. Cela évite de recréer une nouvelle source d’écritures locales, au prix d’un compromis assumé : une panne ou un redémarrage peut faire perdre quelques requêtes. Pour mon homelab, la visibilité opérationnelle compte davantage qu’une conservation forensique parfaitement exhaustive.
Ces données restent sensibles. Une suite de requêtes DNS raconte rapidement les habitudes d’un réseau. Les noms demandés et les adresses clientes resteront des champs recherchables, mais pas des champs de stream à forte cardinalité, et leur accès devra être plus restreint que celui des journaux système ordinaires.
La suite, avec un vrai avant/après
Au moment où j’écris ces lignes, aucune modification de production n’a encore été appliquée. Le déploiement suivra volontairement plusieurs étapes :
- activer Log Exporter et le flux Vector tout en gardant temporairement SQLite ;
- comparer les événements pendant 30 à 60 minutes ;
- mesurer pendant 24 à 72 heures le volume ingéré, la RAM et les écritures Ceph ;
- désactiver Query Logs SQLite seulement après cette validation.
Je conserverai ensuite la base quelques jours comme possibilité de retour arrière. Si les écritures chutent comme prévu, j’aurai enfin le chiffre qui manque à ce diagnostic : le gain réel, pas seulement le coupable le plus crédible.
Je pensais devoir redimensionner un serveur DNS. En réalité, il fallait surtout l’empêcher de tenir son journal intime sur Ceph.
