Le noyau : modules et paramètres
Pourquoi
En parcourant sig-app-1 à la leçon 1, vous avez trouvé dans /etc/sysctl.conf trois lignes ajoutées par Camille, sans autre explication qu'un commentaire :
# pic de février, la mairie a relayé l'appli sur les réseaux
net.core.somaxconn = 65535
vm.swappiness = 10
net.ipv4.ip_local_port_range = 1024 65535Sur sig-outils, un fichier semblable contenait vm.overcommit_memory = 1. Or sig-outils a été réinstallé en Debian 13 il y a trois mois, et sur Debian 13 ce fichier n'est plus lu au démarrage. Le réglage a disparu sans bruit. Personne ne sait si c'est grave, parce que personne ne sait pourquoi il avait été posé.
Cette situation est typique. Le noyau Linux expose plusieurs milliers de réglages, et chacun a un jour été modifié par quelqu'un, sur la foi d'un article de blog ou d'une réponse trouvée en pleine panne. Certains sont utiles, d'autres inutiles, quelques-uns dangereux (une plage de ports qui commence à 1024 entre en collision avec des services qui écoutent sur des ports élevés). Sans comprendre ce que fait chaque réglage, vous ne pouvez ni le défendre, ni le retirer.
Le noyau est aussi celui qui parle le premier quand quelque chose casse vraiment : un processus tué faute de mémoire, un disque qui renvoie des erreurs, un système de fichiers repassé en lecture seule. Ces messages ne sont pas dans le journal de votre application. Ils sont dans le tampon du noyau, et il faut savoir les lire.
Cette leçon traite le noyau comme un ensemble configurable sans recompilation : les modules qu'il charge, les paramètres qu'on lui donne à chaud, et ce qu'il raconte.
Les concepts
Un noyau monolithique, mais modulaire
Le noyau Linux est monolithique : pilotes, systèmes de fichiers, pile réseau et ordonnanceur s'exécutent tous dans le même espace mémoire, avec tous les privilèges. Mais il est aussi modulaire : une grande partie de ce code peut être compilée à part, sous forme de modules (fichiers .ko, kernel object), et chargée seulement si on en a besoin.
Un même pilote peut donc exister sous deux formes, selon la configuration choisie par la distribution au moment de compiler le noyau :
- intégré (built-in) : il fait partie de l'image du noyau (
/boot/vmlinuz-...), il est toujours présent, on ne peut ni le décharger ni le recharger ; - en module : un fichier sous
/lib/modules/<version du noyau>/kernel/, souvent compressé (.ko.zst,.ko.xz), chargé à la demande.
La distinction compte en pratique. lsmod ne montre que les modules chargés, jamais les pilotes intégrés. Et une option écrite dans /etc/modprobe.d/ n'a aucun effet sur un pilote intégré : il faut alors passer par la ligne de commande du noyau (vue à la leçon 2).
Chaque version de noyau a son propre répertoire de modules. Après une mise à jour du noyau, /lib/modules/ en contient plusieurs, et c'est uname -r qui dit lequel est utilisé. Un module compilé pour une version ne se charge pas dans une autre : il porte une « signature de version » (vermagic) que le noyau vérifie.
Les fichiers d'index de depmod
Dans /lib/modules/$(uname -r)/, à côté des modules, se trouvent des fichiers d'index produits par depmod à l'installation de chaque noyau :
| Fichier | Contenu |
|---|---|
modules.dep (et modules.dep.bin) | pour chaque module, ceux dont il dépend |
modules.alias | les alias : correspondances entre identifiants matériels ou noms de protocole et modules |
modules.builtin | la liste des pilotes intégrés à ce noyau |
modules.softdep | les dépendances facultatives déclarées par les modules |
modprobe lit ces index ; il ne parcourt jamais l'arborescence. Si vous copiez un module à la main sans relancer depmod -a, modprobe ne le trouve pas.
Trois façons dont un module se charge
Sur un serveur en marche, presque aucun module n'a été chargé par un humain. Ils arrivent par trois chemins :
- Le matériel apparaît. Le noyau détecte un périphérique (au démarrage, ou à chaud quand on attache un volume Block Storage à l'instance) et émet un événement, un uevent, qui contient un identifiant normalisé du matériel, le
MODALIAS.systemd-udevdreçoit l'événement ; sa règle80-drivers.rulesdit, en substance, « s'il y a unMODALIAS, charge le module correspondant ». La correspondance est cherchée dansmodules.alias. - Le noyau a besoin d'une fonction. Quand un programme ouvre une socket d'une famille de protocoles inconnue, monte un type de système de fichiers absent, ou qu'une règle de pare-feu utilise une extension non chargée, le noyau lance lui-même
modprobeavec un nom d'alias (par exemplefs-xfsounet-pf-10). Le chemin du programme lancé est dans/proc/sys/kernel/modprobe(/sbin/modprobepar défaut). - La configuration l'exige. Les fichiers de
/etc/modules-load.d/listent des modules à charger à chaque démarrage, sans attendre un besoin. C'estsystemd-modules-load.servicequi s'en charge.
Le quatrième chemin, sudo modprobe <module>, existe surtout pour les tests et le dépannage.
Les paramètres de module
Un module peut accepter des paramètres, fixés au chargement. On les donne de trois façons :
- sur la ligne de commande de
modprobe:modprobe nf_conntrack acct=1; - dans un fichier de
/etc/modprobe.d/, par une ligneoptions nf_conntrack acct=1, appliquée à chaque chargement, même quand le module est tiré comme dépendance ; - sur la ligne de commande du noyau, sous la forme
<module>.<paramètre>=<valeur>: c'est la seule qui fonctionne aussi pour un pilote intégré.
Une fois le module chargé, ses paramètres apparaissent dans /sys/module/<module>/parameters/. Certains sont modifiables à chaud en écrivant dans ce fichier ; la plupart sont en lecture seule.
Les paramètres du noyau : /proc/sys et sysctl
Indépendamment des modules, le cœur du noyau expose des réglages de fonctionnement sous /proc/sys/ : un fichier par paramètre, rangé par domaine (kernel/, vm/ pour la mémoire, fs/, net/). Lire le fichier donne la valeur, écrire dedans la change immédiatement, et le changement est perdu au redémarrage : /proc est un système de fichiers virtuel, rien n'y est stocké.
sysctl est l'outil qui manipule ces fichiers avec une notation à points : la clé net.core.somaxconn correspond au fichier /proc/sys/net/core/somaxconn. Il n'y a pas de magie : sysctl -w net.core.somaxconn=4096 écrit 4096 dans ce fichier, exactement comme le ferait echo.
Pour qu'un réglage survive au redémarrage, on l'écrit dans un fichier de /etc/sysctl.d/. Au démarrage, très tôt, systemd-sysctl.service lit tous ces fichiers et applique leurs valeurs.
sysfs : /sys
/sys est l'autre grand système de fichiers virtuel du noyau. Là où /proc/sys contient des réglages globaux, /sys décrit des objets : chaque périphérique, chaque pilote, chaque module, chaque interface réseau, chaque disque y a son répertoire, avec un fichier par attribut. La règle de sysfs est « une valeur par fichier ». Exemples :
| Fichier | Ce qu'il dit |
|---|---|
/sys/class/net/eth0/mtu | la MTU de l'interface |
/sys/class/net/eth0/device/modalias | l'identifiant matériel qui a servi à choisir le pilote |
/sys/block/sda/queue/scheduler | l'ordonnanceur d'entrées-sorties du disque, l'actif entre crochets |
/sys/module/<module>/parameters/ | les paramètres d'un module chargé (ou intégré) |
/sys/kernel/mm/transparent_hugepage/enabled | la politique des grandes pages transparentes |
C'est dans /sys que udev lit les attributs des périphériques, et c'est le même arbre que la leçon 11 utilisera pour les cgroups, sous /sys/fs/cgroup.
Le tampon du noyau
Le noyau écrit ses messages (détection du matériel, erreurs, avertissements) dans un tampon circulaire en mémoire, de taille fixe : quand il est plein, les messages les plus anciens sont écrasés. Chaque message a un niveau de priorité, de 0 (emerg) à 7 (debug), les mêmes que syslog. On le lit avec dmesg, ou à travers /dev/kmsg.
systemd-journald lit aussi /dev/kmsg et recopie chaque message du noyau dans le journal. D'où une différence importante : dmesg ne montre que le tampon actuel, qui disparaît au redémarrage et peut avoir été écrasé ; journalctl -k montre les messages du noyau conservés dans le journal persistant, y compris ceux des démarrages précédents.
Paramètres propres à un espace de noms, paramètres globaux
Un conteneur n'a pas son propre noyau : il partage celui de l'hôte, isolé par des espaces de noms (namespaces). Une partie des paramètres de /proc/sys est propre à chaque espace de noms (namespaced) : chaque espace de noms réseau a ses propres valeurs pour presque tout net.* (dont net.core.somaxconn et net.ipv4.ip_local_port_range), et chaque espace de noms IPC a les siennes pour kernel.shm*, kernel.msg*, kernel.sem et fs.mqueue.*.
Tout le reste est global : vm.*, fs.file-max, kernel.dmesg_restrict, kernel.panic... Une valeur globale vaut pour l'hôte et pour tous ses conteneurs. Conséquence directe : un conteneur peut recevoir sa propre valeur de net.core.somaxconn, mais pas sa propre valeur de vm.swappiness. On y revient en production.
En pratique
Aucune des sorties ci-dessous n'est une capture : elles sont construites d'après la documentation et le code source des outils, au format exact qu'ils produisent, avec des valeurs plausibles pour sig-app-1 (Ubuntu 24.04) et sig-outils (Debian 13).
Inventaire des modules
$ lsmod | head -5
La sortie ressemble à ceci :
Module Size Used by
xt_conntrack 12288 4
nf_conntrack 196608 2 xt_conntrack,nf_nat
nf_defrag_ipv6 24576 1 nf_conntrack
nf_defrag_ipv4 12288 1 nf_conntracklsmod ne fait que mettre en forme /proc/modules. Trois colonnes : le nom du module, la taille qu'il occupe en mémoire en octets, et le compteur d'utilisation suivi de la liste des modules qui dépendent de lui. Un module dont le compteur n'est pas nul ne peut pas être déchargé. Ici, nf_conntrack (le suivi de connexion du pare-feu, voir suivi de connexion) est utilisé par xt_conntrack et nf_nat : c'est la présence d'ufw qui l'a fait charger, par le deuxième chemin décrit plus haut.
Pour tout savoir d'un module, chargé ou non :
$ modinfo br_netfilter
Sortie typique (abrégée) :
filename: /lib/modules/6.8.0-85-generic/kernel/net/bridge/br_netfilter.ko.zst
description: Linux ethernet netfilter firewall bridge
author: Lennert Buytenhek <buytenh@gnu.org>
author: Bart De Schuymer <bdschuym@pandora.be>
license: GPL
depends: bridge
intree: Y
name: br_netfilter
vermagic: 6.8.0-85-generic SMP preempt mod_unload modversions
sig_id: PKCS#7
signer: Build time autogenerated kernel key
sig_hashalgo: sha512Les champs utiles : filename (où est le fichier), depends (ses dépendances directes), intree: Y (il fait partie des sources officielles du noyau ; un module externe, comme un pilote propriétaire, n'a pas ce champ), vermagic (la version de noyau pour laquelle il a été compilé), les champs sig_* (il est signé, ce qui compte avec Secure Boot), et parm quand le module accepte des paramètres. modinfo -F <champ> n'affiche qu'un champ, modinfo -p seulement les paramètres.
Pour un pilote intégré, modinfo répond quand même, avec filename: (builtin). Pour savoir si un pilote est intégré à votre noyau :
$ grep -E '/(virtio_net|virtio_blk|virtio_scsi)\.ko' /lib/modules/$(uname -r)/modules.builtin
Une ligne trouvée signifie « intégré » ; aucune, « module (ou absent) ». Les pilotes virtio, par lesquels une instance Scaleway voit sa carte réseau et ses disques virtuels, sont un bon test : selon la configuration du noyau de la distribution, ils sont intégrés ou en modules, et c'est ce qui décide s'ils doivent figurer dans l'initramfs.
Enfin, pour voir ce que ferait un chargement, dépendances comprises, sans rien charger :
$ modprobe --show-depends br_netfilter
insmod /lib/modules/6.8.0-85-generic/kernel/net/802/stp.ko.zst
insmod /lib/modules/6.8.0-85-generic/kernel/net/llc/llc.ko.zst
insmod /lib/modules/6.8.0-85-generic/kernel/net/bridge/bridge.ko.zst
insmod /lib/modules/6.8.0-85-generic/kernel/net/bridge/br_netfilter.ko.zst
modprobe résout l'arbre des dépendances depuis modules.dep et charge les modules dans l'ordre. insmod, l'outil de bas niveau, charge un seul fichier sans rien résoudre : on ne s'en sert presque jamais.
Charger et décharger
$ sudo modprobe -n -v br_netfilter # simulation : affiche, ne charge rien
$ sudo modprobe br_netfilter # charge le module et ses dépendances
$ lsmod | grep br_netfilter
$ sudo modprobe -r br_netfilter # décharge, et ses dépendances devenues inutiles
-n(--dry-run) fait tout sauf charger ; avec-v, il affiche les commandesinsmodqu'il exécuterait, y compris les options lues dans/etc/modprobe.d/. C'est la vérification à faire après avoir écrit une configuration.-r(--remove) décharge le module, puis tente de décharger les dépendances qu'il avait tirées et que plus personne n'utilise.- Les tirets et les soulignés sont équivalents dans un nom de module :
br-netfilteretbr_netfilterdésignent le même.
Décharger un module en production est rarement une bonne idée : il peut être utilisé d'une façon que le compteur ne voit pas encore, et certains pilotes ne supportent pas bien un rechargement. On le fait sur une machine de test, pour vérifier un comportement.
Rendre un chargement permanent
Pour qu'un module soit chargé à chaque démarrage, un fichier dans /etc/modules-load.d/, un nom de module par ligne :
$ printf '%s\n' '# Requis par les règles de pont du runtime de conteneurs (ticket OPS-412)' 'br_netfilter' \
| sudo tee /etc/modules-load.d/60-conteneurs.conf
La page modules-load.d(5) fixe les règles : extension .conf obligatoire, lignes vides et lignes commençant par # ou ; ignorées, et des répertoires lus par ordre de priorité, /etc/modules-load.d/ d'abord, puis /run/, /usr/local/lib/, /usr/lib/. Un fichier de /etc/ portant le même nom qu'un fichier de /usr/lib/ le remplace. La page recommande de préfixer les noms par deux chiffres, de 60 à 90 pour les fichiers de l'administrateur.
Sur Ubuntu et Debian, l'ancien fichier /etc/modules est toujours pris en compte : le paquet systemd installe un lien /etc/modules-load.d/modules.conf qui pointe vers lui. Préférez tout de même un fichier par besoin dans modules-load.d, qu'un outil d'automatisation peut déposer et retirer sans toucher aux autres.
Pour appliquer sans redémarrer : sudo systemctl restart systemd-modules-load.
La même page rappelle que ce mécanisme est de moins en moins nécessaire : la plupart des pilotes se chargent seuls à partir de l'identifiant du matériel. Un fichier dans modules-load.d se justifie pour un module sans matériel dont on veut la présence avant qu'un besoin ne le déclenche, comme br_netfilter dont dépendent des paramètres sysctl (on verra pourquoi plus bas).
Interdire un module
/etc/modprobe.d/ contient la configuration de modprobe. Deux directives servent à empêcher un chargement, et elles ne font pas la même chose :
# /etc/modprobe.d/60-interdits.conf
blacklist floppy
install floppy /bin/falseblacklist floppyempêche seulement le chargement par alias. udev, qui demande « le module pour tel identifiant matériel », ne chargera plusfloppy. Maismodprobe floppy, écrit en toutes lettres, le charge quand même, et un autre module qui en dépend le tire aussi. La pagemodprobe.d(5)est explicite sur ce point.install floppy /bin/falseremplace le chargement par une commande. Toute tentative de chargerfloppy, directe ou par dépendance, exécute/bin/falseet échoue. C'est la forme qui interdit réellement.
blacklist sert donc à choisir entre deux pilotes concurrents pour le même matériel ; install ... /bin/false sert à interdire. La liste des modules à interdire pour réduire la surface d'attaque (stockage USB, protocoles réseau rares) relève du durcissement : la leçon 14 la dresse avec le guide de l'ANSSI.
Pour vérifier ce que modprobe a compris de toute sa configuration :
$ modprobe -c | grep -E '^(blacklist|install|options) '
-c (--show-config) affiche la configuration effective, tous fichiers fusionnés. Les fichiers de /etc/modprobe.d/, /run/modprobe.d/, /usr/local/lib/modprobe.d/ et /lib/modprobe.d/ sont triés ensemble par nom, et pour deux fichiers de même nom seul le premier trouvé, dans cet ordre de répertoires, est lu.
Donner une option à un module
Le suivi de connexion peut compter les octets et paquets de chaque connexion, ce qui sert au diagnostic. C'est le paramètre acct du module nf_conntrack :
$ modinfo -p nf_conntrack
enable_hooks:Always enable conntrack hooks (bool)
acct:Enable connection tracking flow accounting. (bool)
tstamp:Enable connection tracking flow timestamping. (bool)
$ cat /sys/module/nf_conntrack/parameters/acct
N
$ echo 'options nf_conntrack acct=1' | sudo tee /etc/modprobe.d/60-conntrack.conf
$ sudo modprobe -n -v nf_conntrack
La sortie de modinfo -p reprend les descriptions déclarées dans le code du module. Le fichier options ne s'applique qu'au prochain chargement du module : il ne modifie pas un module déjà chargé. Pour acct, déclaré modifiable à chaud dans le code, on peut aussi écrire directement 1 dans /sys/module/nf_conntrack/parameters/acct. La dernière commande affiche la ligne insmod avec acct=1, preuve que l'option est lue.
Warning
Si un module est chargé dès l'initramfs (pilote de disque, de réseau nécessaire au démarrage), ses options et ses interdictions doivent aussi y figurer. Après toute modification de /etc/modprobe.d/, régénérez l'initramfs : sudo update-initramfs -u sur Ubuntu et Debian, sudo dracut -f sur Red Hat. Sinon, l'initramfs charge le module avec l'ancienne configuration, avant même que le système de fichiers racine ne soit monté.
udev au travail
Attachez un nouveau volume Block Storage à sig-outils depuis la console Scaleway, et observez ce que voit la machine :
$ sudo udevadm monitor --kernel --udev --subsystem-match=block
Sortie typique, au moment de l'attachement :
monitor will print the received events for:
UDEV - the event which udev sends out after rule processing
KERNEL - the kernel uevent
KERNEL[86432.118204] add /devices/pci0000:00/0000:00:05.0/virtio2/host1/target1:0:1/1:0:1:0/block/sdb (block)
UDEV [86432.141877] add /devices/pci0000:00/0000:00:05.0/virtio2/host1/target1:0:1/1:0:1:0/block/sdb (block)Chaque événement apparaît deux fois : KERNEL est l'événement brut émis par le noyau, UDEV le même événement après passage dans les règles de udev (création des liens /dev/disk/by-id/..., chargement de pilote si besoin). Le nombre entre crochets est l'instant, en secondes depuis le démarrage. --kernel et --udev choisissent les sources, --subsystem-match filtre, et --property (-p) ajouterait toutes les variables de l'événement, dont MODALIAS quand il y en a une. La suite de l'histoire de ce disque (partition, système de fichiers, montage) est la leçon 6.
Pour remonter d'un périphérique à son pilote :
$ cat /sys/class/net/eth0/device/modalias
virtio:d00000001v00001AF4
$ modprobe --resolve-alias "$(cat /sys/class/net/eth0/device/modalias)"
virtio_net
$ udevadm info -q property -n /dev/sdb | grep -E '^(ID_SERIAL|ID_PATH|DEVLINKS)='
L'alias virtio:d00000001v00001AF4 se lit « périphérique virtio de type 1 (réseau), fabricant 1AF4 (l'identifiant de Red Hat, qui a défini virtio) ». --resolve-alias (-R) cherche cet alias dans modules.alias et donne le nom du module : c'est exactement la recherche que fait udev. udevadm info -q property affiche ce que udev sait d'un périphérique, et udevadm info -a remonte toute la chaîne de ses attributs sysfs, ce qui sert à écrire des règles.
Lire et modifier un paramètre du noyau
$ sysctl net.core.somaxconn
net.core.somaxconn = 4096
$ cat /proc/sys/net/core/somaxconn
4096
$ sysctl -n vm.swappiness
60
$ sysctl -a 2>/dev/null | grep -c .
$ sysctl -a -r '^net\.ipv4\.tcp_'
-nn'affiche que la valeur, pratique dans un script.-aaffiche toutes les clés lisibles. Lancé sanssudo, il signale les clés que seul root peut lire, d'où le2>/dev/null; il y en a plusieurs milliers.-rfiltre par expression rationnelle étendue, sans passer pargrep.
Modifier une valeur, à chaud :
$ sudo sysctl -w net.core.somaxconn=8192
net.core.somaxconn = 8192
-w force l'interprétation des arguments comme des affectations. L'effet est immédiat pour les nouvelles opérations, et sera perdu au prochain redémarrage. C'est la bonne façon de tester un réglage avant de l'écrire dans un fichier.
Une subtilité de notation : quand un composant de la clé contient lui-même un point, comme une interface VLAN eth0.100, la notation à points devient ambiguë. On écrit alors la clé avec des barres obliques, sysctl net/ipv4/conf/eth0.100/rp_filter : sysctl et sysctl.d(5) acceptent les deux séparateurs.
Rendre un paramètre persistant
Créez un fichier dédié, commenté, dans /etc/sysctl.d/ :
# /etc/sysctl.d/60-signalements.conf
# Réglages réseau de l'API Signalements. Revu le 2026-10-07, voir la fiche du serveur.
# File d'attente des connexions en attente d'accept() ; Gunicorn demande 2048.
net.core.somaxconn = 4096Puis appliquez et vérifiez :
$ sudo sysctl --system
$ sudo sysctl -p /etc/sysctl.d/60-signalements.conf
$ systemd-analyze cat-config sysctl.d
--systemrelit tous les fichiers de configuration, dans l'ordre que l'on détaille juste après, et affiche une ligne* Applying <fichier> ...par fichier. C'est la commande à lancer après une modification.-p <fichier>(--load) n'applique qu'un fichier. Sans argument, il lit/etc/sysctl.conf.systemd-analyze cat-config sysctl.daffiche le contenu de tous les fichiers quesystemd-sysctllira au démarrage, dans l'ordre, chacun précédé de son chemin. C'est la vérification la plus fiable, parce qu'elle montre ce que verra le démarrage et non ce que faitsysctlà la main.
L'ordre de lecture
sysctl.d(5) décrit quatre répertoires, du plus prioritaire au moins prioritaire : /etc/sysctl.d/, /run/sysctl.d/, /usr/local/lib/sysctl.d/, /usr/lib/sysctl.d/. Le mécanisme est le même que pour les modules :
- On rassemble les noms de fichiers
.confde tous les répertoires. Pour un nom présent à plusieurs endroits, seul le fichier du répertoire le plus prioritaire est retenu :/etc/sysctl.d/50-default.confmasque complètement/usr/lib/sysctl.d/50-default.conf. - On trie tous les fichiers retenus par nom, quel que soit leur répertoire.
- On les applique dans cet ordre ; si une clé apparaît plusieurs fois, la dernière valeur lue gagne.
D'où la convention des préfixes numériques : les paquets déposent leurs fichiers dans /usr/lib/sysctl.d/ avec des numéros de 10 à 40 ou 50, l'administrateur dépose les siens dans /etc/sysctl.d/ avec des numéros de 60 à 90, et passe donc après. Un fichier nommé 10-tuning.conf dans /etc/ serait appliqué avant le 50-default.conf du paquet, qui pourrait l'écraser.
Ce que vous trouvez sur chaque machine :
Ubuntu 24.04 (sig-app-1) | Debian 13 (sig-outils) | |
|---|---|---|
| Fichiers de la distribution | /etc/sysctl.d/10-*.conf du paquet procps (10-kernel-hardening.conf, 10-network-security.conf, 10-ptrace.conf, 10-map-count.conf...) ; /usr/lib/sysctl.d/50-pid-max.conf de systemd | /usr/lib/sysctl.d/50-default.conf du paquet linux-sysctl-defaults |
/etc/sysctl.conf | existe, et pris en compte au démarrage grâce au lien /etc/sysctl.d/99-sysctl.conf créé par le paquet systemd | ignoré au démarrage : les notes de publication de Debian 13 annoncent que ce fichier n'est plus pris en compte |
Les fichiers 10-*.conf d'Ubuntu sont dans /etc/, et non dans /usr/lib/ : ce sont des fichiers de configuration du paquet, que vous pouvez modifier (le gestionnaire de paquets vous demandera quoi faire à la prochaine mise à jour). Préférez pourtant ne pas y toucher et surcharger par un fichier 60- ou plus : vos réglages restent groupés et lisibles.
C'est ici que le réglage perdu de sig-outils s'explique : vm.overcommit_memory = 1 était dans /etc/sysctl.conf, que systemd-sysctl ne lit pas sur Debian 13. La correction est de le déplacer dans /etc/sysctl.d/, après avoir vérifié qu'il a une raison d'être (ce qu'on fait dans la section suivante).
Les réglages de Signalements, un par un
Un réglage du noyau se justifie par un symptôme mesuré, pas par une liste trouvée en ligne. Voici les six que vous rencontrerez le plus, ce qu'ils font vraiment, et ce qu'il faut en penser pour Signalements.
net.core.somaxconn : le plafond de la file d'attente d'une socket en écoute. Quand une connexion TCP termine sa poignée de main, elle attend dans cette file que l'application l'accepte par accept(). L'application demande une taille de file à l'appel listen() ; le noyau la ramène silencieusement à somaxconn si elle est plus grande. La documentation du noyau indique une valeur par défaut de 4096 depuis Linux 5.4 (128 avant). Gunicorn demande 2048 par défaut (réglage --backlog). Sur Ubuntu 24.04 ou Debian 13, la valeur de Camille (65535) ne sert donc à rien tant que Gunicorn demande 2048 : elle datait d'un vieux noyau où le plafond était 128. On le vérifie sur la socket elle-même :
$ ss -ltn 'sport = :8000'
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 2048 172.16.8.11:8000 0.0.0.0:*
$ nstat -az TcpExtListenOverflows TcpExtListenDrops
Pour une socket en écoute, Send-Q est la taille effective de la file (le minimum de la demande et de somaxconn) et Recv-Q le nombre de connexions qui y attendent en ce moment. Les compteurs ListenOverflows et ListenDrops de nstat augmentent quand la file a débordé : s'ils restent à zéro, augmenter somaxconn n'apportera rien. S'ils montent, la vraie question est souvent : pourquoi les workers n'acceptent-ils pas assez vite ? Une file plus longue ne fait que rendre l'attente plus longue.
net.ipv4.ip_local_port_range : la plage de ports source que le noyau attribue aux connexions sortantes. Par défaut 32768 60999, soit 28 232 ports. Une connexion est identifiée par son quintuplet : la limite porte donc sur le nombre de connexions simultanées (y compris celles en TIME_WAIT) vers une même adresse et un même port de destination. sig-app-1 ouvre peu de connexions vers sig-db grâce à un pool, et n'en a pas besoin. sig-outils, qui envoie des milliers d'objets vers l'Object Storage pendant les sauvegardes, pourrait en avoir besoin un jour. La valeur de Camille (1024 65535) est dangereuse : un port source attribué au hasard peut tomber sur un port qu'un service local voudra ouvrir ensuite (un exportateur de métriques sur 9100, par exemple), qui échouera alors avec Address already in use. Si vous élargissez la plage, gardez un début au-dessus des ports de vos services, et protégez ceux-ci avec net.ipv4.ip_local_reserved_ports = 9100. La documentation recommande aussi que les deux bornes soient de parités différentes.
vm.swappiness : la propension du noyau à envoyer en espace d'échange (swap) la mémoire anonyme des processus plutôt qu'à vider le cache des fichiers. Valeur de 0 à 200, 60 par défaut ; la documentation la décrit comme le coût relatif estimé d'une entrée-sortie d'échange par rapport à une relecture de fichier, à 100 les deux étant jugés égaux. Elle ne change rien sur une machine sans espace d'échange (vérifiez avec swapon --show : une ligne vide signifie qu'il n'y en a pas, cas fréquent des images cloud). La valeur 10 de Camille était donc probablement sans effet. Le choix d'ajouter ou non de l'échange relève de la leçon 6.
fs.file-max et fs.nr_open : deux plafonds différents sur les descripteurs de fichiers. file-max est le nombre total de fichiers ouverts que le noyau acceptera pour toute la machine ; nr_open est le plafond qu'aucun processus ne peut dépasser, quelle que soit sa limite propre (1 048 576 par défaut d'après la documentation). Au-dessous des deux, chaque processus a sa propre limite, RLIMIT_NOFILE, la seule qu'on règle vraiment (LimitNOFILE= dans l'unité systemd, leçon 11). Sur les deux distributions, systemd relève lui-même fs.file-max à sa valeur maximale au démarrage (9223372036854775807), parce que la mémoire des descripteurs est désormais comptée par les cgroups. Il fait de même pour fs.nr_open sur Debian 13 ; le paquet d'Ubuntu désactive ce second relèvement à la compilation, nr_open y reste à 1 048 576. Conclusion : un réglage fs.file-max dans un fichier de configuration est aujourd'hui presque toujours inutile. cat /proc/sys/fs/file-nr donne trois nombres : descripteurs alloués, toujours 0, et le maximum.
net.ipv4.ip_forward : autorise la machine à faire passer des paquets d'une interface à une autre, c'est-à-dire à se comporter en routeur. Par défaut 0, et c'est ce qu'il faut sur sig-app-1 et sig-app-2, qui ne routent rien. Une valeur 1 sur un serveur d'application n'est pas anodine : elle peut faire de lui un relais vers le réseau privé. Si vous la trouvez à 1, cherchez qui l'a posée (Docker l'active à son démarrage, par exemple) avant de la changer. Attention aussi à une particularité notée dans la documentation : écrire dans cette variable réinitialise tous les paramètres de configuration IPv4 des interfaces à leur valeur par défaut.
vm.overcommit_memory : la politique du noyau quand un processus réserve de la mémoire qu'il n'utilise pas encore. Linux promet plus de mémoire qu'il n'en a (surengagement, overcommit), parce que la plupart des programmes n'utilisent jamais tout ce qu'ils réservent. Trois modes :
| Valeur | Comportement | Usage |
|---|---|---|
0 (défaut) | heuristique : refuse seulement les demandes manifestement impossibles | presque tous les serveurs |
1 | accepte toujours ; on découvre le manque au moment où la mémoire est réellement touchée | quelques logiciels le demandent explicitement (Redis l'exige pour ses sauvegardes en arrière-plan) |
2 | refuse toute réservation au-delà de l'échange plus overcommit_ratio % de la RAM (50 par défaut) | serveurs de bases de données dédiés : la documentation de PostgreSQL le recommande, pour qu'une réservation échoue proprement plutôt que l'OOM killer ne tue le processus maître |
Pour sig-outils, qui n'héberge ni Redis ni PostgreSQL (sig-db est managée), 1 n'a aucune justification : il rend seulement les situations de manque plus brutales. Le réglage perdu peut rester perdu. Notez-le dans la fiche du serveur, avec la raison.
Tip
Avant de modifier un paramètre, notez sa valeur actuelle (sysctl <clé>), la raison du changement, et la mesure qui prouvera qu'il a servi. Un réglage sans mesure avant et après est une superstition.
Agir sur sysfs
Les attributs de /sys se lisent et s'écrivent comme ceux de /proc/sys, et sont aussi perdus au redémarrage :
$ cat /sys/block/sda/queue/scheduler
[none] mq-deadline
$ cat /sys/kernel/mm/transparent_hugepage/enabled
always [madvise] never
Les crochets indiquent la valeur active parmi les valeurs possibles. Pour rendre un réglage sysfs persistant, il n'y a pas de sysfs.d. Deux outils conviennent selon le cas :
- une règle udev, quand l'attribut appartient à un périphérique qui peut apparaître à tout moment (un disque attaché à chaud) :
ACTION=="add", SUBSYSTEM=="block", KERNEL=="sd[a-z]", ATTR{queue/scheduler}="mq-deadline"dans/etc/udev/rules.d/60-scheduler.rules; - une ligne
wde systemd-tmpfiles, pour un attribut global présent dès le démarrage :w /sys/kernel/mm/transparent_hugepage/enabled - - - - madvisedans/etc/tmpfiles.d/thp.conf.
Lire le tampon du noyau
$ sudo dmesg -T --level=err,warn
$ sudo dmesg -T -w
$ sudo dmesg -x | tail -20
$ journalctl -k -b -1 -p warning
-T(--ctime) convertit l'horodatage, en secondes depuis le démarrage, en date lisible. La page de manuel avertit que cette conversion peut être fausse après une mise en veille, ce qui ne concerne guère un serveur ; elle reste une approximation calculée à partir de l'heure de démarrage.--level(-l) filtre par priorité :emerg,alert,crit,err,warn,notice,info,debug.-w(--follow) attend les nouveaux messages, commetail -f.-x(--decode) affiche la catégorie et la priorité de chaque ligne en clair (kern :err :).journalctl -klit les mêmes messages depuis le journal ; avec-b -1, ceux du démarrage précédent, ce quedmesgne peut pas faire. Après un redémarrage inattendu, c'est la première commande à lancer.
Pourquoi sudo ? Parce que kernel.dmesg_restrict vaut 1 : seul un processus disposant de la capacité CAP_SYSLOG peut lire le tampon. Ubuntu a activé cette restriction dans ses noyaux à partir de la version 20.10, au motif que le tampon peut révéler des adresses mémoire du noyau, utiles à un attaquant ; Debian construit aussi ses noyaux avec cette option. Sans sudo, la sortie est :
dmesg: read kernel buffer failed: Operation not permittedSavoir si le noyau est « teinté »
Le noyau tient un indicateur de teinte (taint) : un ensemble de drapeaux qui signalent qu'il s'est passé quelque chose qui rend son état moins fiable, ou qui sort de ce que les développeurs du noyau acceptent de déboguer.
$ cat /proc/sys/kernel/tainted
4096
0 signifie « non teinté ». Toute autre valeur est une somme de bits, décrits dans la documentation du noyau (Tainted kernels). Les plus courants sur un serveur :
| Bit | Lettre | Valeur | Signification |
|---|---|---|---|
| 0 | P | 1 | module propriétaire chargé (licence non libre) |
| 1 | F | 2 | module chargé de force |
| 4 | M | 16 | erreur matérielle signalée par le processeur (machine check) |
| 5 | B | 32 | mauvaise référence de page mémoire |
| 7 | D | 128 | le noyau a déjà subi un oops ou un BUG |
| 9 | W | 512 | le noyau a émis un avertissement (WARNING) |
| 12 | O | 4096 | module externe aux sources du noyau chargé |
| 13 | E | 8192 | module non signé chargé |
| 14 | L | 16384 | blocage logiciel d'un processeur (soft lockup) |
| 15 | K | 32768 | noyau corrigé à chaud (Livepatch) |
Ici, 4096 : un module hors arbre est chargé (pilote de constructeur, module DKMS). Ce n'est pas une panne, c'est une information : si le noyau se met à planter, le premier suspect est ce module. Les valeurs D, M, B ou L, elles, indiquent qu'il s'est déjà passé quelque chose de grave depuis le démarrage, même si tout semble fonctionner : cherchez le message correspondant avec journalctl -k -b.
Pour décoder une valeur à la main :
t=$(cat /proc/sys/kernel/tainted)
for i in $(seq 0 18); do
(( (t >> i) & 1 )) && echo "bit $i activé"
doneLes sources du noyau fournissent aussi un script complet, tools/debugging/kernel-chktaint.
Les messages à reconnaître
Le tampon du noyau est bavard ; quelques messages, eux, demandent une action. Les formats ci-dessous sont ceux du code source du noyau 6.8 ; les valeurs sont illustratives.
Mémoire épuisée. Un export trop gros sur sig-outils a consommé toute la mémoire :
python3 invoked oom-killer: gfp_mask=0x140cca(GFP_HIGHUSER_MOVABLE|__GFP_COMP), order=0, oom_score_adj=0
...
Tasks state (memory values in pages):
[ pid ] uid tgid total_vm rss rss_anon rss_file rss_shmem pgtables_bytes swapents oom_score_adj name
...
oom-kill:constraint=CONSTRAINT_NONE,nodemask=(null),cpuset=/,mems_allowed=0,global_oom,task_memcg=/system.slice/export-csv.service,task=python3,pid=41873,uid=997
Out of memory: Killed process 41873 (python3) total-vm:3954112kB, anon-rss:3612440kB, file-rss:2048kB, shmem-rss:0kB, UID:997 pgtables:7240kB oom_score_adj:0À lire dans l'ordre : la première ligne nomme le processus qui demandait de la mémoire au moment du manque (pas forcément le coupable) ; le tableau liste tous les processus et leur consommation, en pages ; la ligne oom-kill: dit si le manque était global (global_oom) ou limité à un cgroup, et dans quel cgroup vivait la victime ; la dernière ligne nomme la victime et sa mémoire résidente (anon-rss). Si le manque était celui d'un cgroup, la dernière ligne commence par Memory cgroup out of memory : c'est alors la limite fixée au service qui a été atteinte, sujet de la leçon 11. Côté systemd, le journal du service porte A process of this unit has been killed by the OOM killer.
Disque en erreur.
I/O error, dev sdb, sector 2048 op 0x1:(WRITE) flags 0x800 phys_seg 1 prio class 2
EXT4-fs (sdb1): Remounting filesystem read-onlyLe volume a refusé une écriture ; ext4, configuré pour se protéger, est repassé en lecture seule. L'application voit alors des Read-only file system. Sur une instance Scaleway, un volume réseau détaché ou un incident de stockage côté fournisseur produisent ce genre de messages : vérifiez l'état du volume dans la console avant de soupçonner le système de fichiers.
Processeur ou tâche bloqués.
watchdog: BUG: soft lockup - CPU#1 stuck for 23s! [kworker/1:2:2817]
INFO: task jbd2/sda1-8:312 blocked for more than 122 seconds.Le premier signale un processeur qui n'a pas rendu la main pendant plus de vingt secondes ; sur une machine virtuelle, c'est souvent l'hyperviseur qui ne lui a pas donné de temps de calcul (voisin bruyant, hôte surchargé), plus rarement un bogue de pilote. Le second signale une tâche bloquée en attente d'entrée-sortie pendant deux minutes : le stockage ne répond plus.
Table du suivi de connexion pleine.
nf_conntrack: nf_conntrack: table full, dropping packetLe pare-feu suit chaque connexion, et la table a atteint net.netfilter.nf_conntrack_max : les nouvelles connexions sont abandonnées sans réponse. Symptôme côté clients : des délais d'attente aléatoires. La leçon 8 revient sur le suivi de connexion.
Plantage d'un programme.
gunicorn[1873]: segfault at 0 ip 00007f3a1c2d4e10 sp 00007ffd5e8a2b40 error 4 in libxml2.so.2.9.14[7f3a1c200000+12d000]Le noyau a tué un processus qui accédait à une adresse invalide, et nomme la bibliothèque où se trouvait l'instruction fautive. Ce n'est pas un problème du noyau, mais c'est le seul endroit où cette information apparaît.
Sous le capot
Le chargement d'un module. modprobe lit modules.dep.bin, applique la configuration de modprobe.d, puis, pour chaque module dans l'ordre des dépendances, ouvre le fichier et passe son descripteur au noyau par l'appel système finit_module(). Le noyau décompresse le module si besoin, vérifie la vermagic et les sommes de contrôle des symboles (modversions), vérifie la signature, résout les symboles que le module importe parmi ceux qu'exportent le noyau et les modules déjà chargés, puis appelle sa fonction d'initialisation. Si un symbole manque, le chargement échoue, et c'est le sens du message de modprobe « Unknown symbol in module, or unknown parameter (see dmesg) » : la raison exacte est dans le tampon du noyau. Une fois chargé, le module est du code noyau comme un autre, avec tous les privilèges : il n'y a aucune frontière entre un module et le reste du noyau.
La signature. Les noyaux d'Ubuntu et de Debian signent leurs modules à la compilation, avec une clé générée pour l'occasion (signer: Build time autogenerated kernel key). Un module non signé (pilote compilé localement par DKMS sans clé enregistrée) est accepté sur une machine sans Secure Boot, au prix de la teinte E et du message module verification failed: signature and/or required key missing - tainting kernel. Avec Secure Boot actif, les noyaux de ces distributions passent en mode lockdown, et le module est refusé : le tampon affiche Loading of unsigned module is rejected, et modprobe répond Operation not permitted.
L'appel au modprobe depuis le noyau. Quand du code noyau appelle request_module("fs-xfs"), le noyau crée un processus en espace utilisateur qui exécute le programme dont le chemin est dans /proc/sys/kernel/modprobe, avec l'alias en argument. C'est un usermode helper : le noyau qui lance un programme. Ce chemin est une cible connue des attaquants qui ont obtenu une écriture arbitraire dans la mémoire du noyau, ce qui explique que les guides de durcissement surveillent ce fichier.
Ce que fait une écriture dans /proc/sys. Chaque fichier de /proc/sys correspond à une entrée d'une table (ctl_table) déclarée dans le code du noyau, avec un pointeur vers la variable, des bornes et une fonction de traitement. Écrire 4096 dans somaxconn appelle cette fonction, qui analyse le texte, vérifie les bornes, et range la valeur. Pour les paramètres propres à un espace de noms, la table est dupliquée à la création de chaque espace de noms réseau : le fichier /proc/sys/net/core/somaxconn qu'un processus voit est celui de son espace de noms. C'est pourquoi le même chemin donne des valeurs différentes dans un conteneur et sur l'hôte.
Pourquoi l'ordre modules puis sysctl compte. Les paramètres de net.bridge.* n'existent dans /proc/sys qu'une fois br_netfilter chargé. Si systemd-sysctl passe avant, la clé est inconnue et le réglage est ignoré. L'unité systemd-sysctl.service est ordonnée après systemd-modules-load.service (After=systemd-modules-load.service), ce qui règle le cas des modules listés dans modules-load.d. Pour les interfaces réseau, qui peuvent apparaître bien après, systemd ajoute une règle udev (99-systemd.rules) qui relance systemd-sysctl pour les seules clés net.ipv4.conf.<interface>, net.ipv6.conf.<interface> et leurs voisines, à chaque apparition d'une interface. C'est ce qui permet à un réglage visant eth1 d'être appliqué même si l'interface du réseau privé arrive après le démarrage.
Le tampon et le journal. Les messages du noyau sont écrits dans le tampon circulaire et exposés par /dev/kmsg, un enregistrement par lecture, avec sa priorité, son numéro de séquence et son horodatage en microsecondes depuis le démarrage. dmesg et journald sont deux lecteurs de ce même fichier. Le niveau à partir duquel un message est aussi affiché sur la console est fixé par le premier des quatre nombres de /proc/sys/kernel/printk : c'est le paramètre quiet de la ligne de commande du noyau qui le baisse, pour une console moins bavarde au démarrage.
Pièges courants
Écrire dans /etc/sysctl.conf sur Debian 13. Le fichier n'est plus lu au démarrage. Pire, sudo sysctl --system l'applique encore (l'outil de procps lit /etc/sysctl.conf en dernier s'il existe), ce qui donne l'illusion que tout fonctionne... jusqu'au redémarrage suivant. Mettez tout dans /etc/sysctl.d/, et vérifiez avec systemd-analyze cat-config sysctl.d, qui montre ce que lit le démarrage.
Un réglage écrasé par un fichier qui passe après. Vous écrivez net.core.somaxconn = 8192 dans /etc/sysctl.d/30-tuning.conf, et la valeur au démarrage reste autre. Un fichier dont le nom trie après le vôtre (un 50- ou un 99-, d'un paquet ou de cloud-init) contient la même clé. Cherchez toutes les occurrences : grep -r somaxconn /etc/sysctl.d /run/sysctl.d /usr/lib/sysctl.d /etc/sysctl.conf, et renommez votre fichier en 60- ou plus.
Une clé inconnue. sysctl répond sysctl: cannot stat /proc/sys/net/core/somaxcon: No such file or directory en lecture, ou sysctl: "net.core.somaxcon" is an unknown key à l'application d'un fichier. Faute de frappe, ou clé qui n'existe pas encore (module pas chargé, interface pas encore créée), ou clé retirée dans une version plus récente du noyau. Au démarrage, systemd-sysctl journalise un avertissement de la forme Couldn't write '1' to 'net/bridge/bridge-nf-call-iptables', ignoring: No such file or directory et continue, sans échouer : journalctl -b -u systemd-sysctl après chaque modification. Un préfixe - devant une affectation (-net.bridge.bridge-nf-call-iptables = 1) indique que la clé peut légitimement manquer, et rabaisse l'erreur au niveau débogage.
Permission refusée. sysctl: permission denied on key "net.core.somaxconn" : vous avez oublié sudo, ou vous êtes dans un conteneur où /proc/sys est monté en lecture seule. Dans ce dernier cas, le réglage se fait sur l'hôte ou dans la définition du conteneur, pas de l'intérieur.
Monter somaxconn sans toucher à l'application. La file effective est le minimum de la valeur demandée par l'application et de somaxconn. Si Gunicorn demande 2048, porter somaxconn à 65535 ne change rien. Vérifiez la colonne Send-Q de ss -ltn.
Une option de module sans effet. Trois causes possibles : le module est intégré (grep <module> /lib/modules/$(uname -r)/modules.builtin), et l'option doit passer par la ligne de commande du noyau ; le module était déjà chargé, et l'option ne vaudra qu'au prochain chargement ; ou il est chargé depuis l'initramfs, qu'il faut régénérer. modprobe -n -v <module> montre si l'option est bien lue.
Une liste noire qui ne bloque rien. blacklist n'empêche ni modprobe <module> explicite, ni le chargement comme dépendance. Pour interdire, install <module> /bin/false.
Les messages d'erreur de modprobe. Les textes exacts de kmod et leur sens :
| Message | Diagnostic |
|---|---|
modprobe: FATAL: Module xyz not found in directory /lib/modules/6.8.0-85-generic | le module n'existe pas pour ce noyau : faute de frappe, paquet de modules manquant (sur Ubuntu, certains pilotes sont dans linux-modules-extra-<version>), ou module DKMS pas recompilé pour le nouveau noyau |
modprobe: ERROR: could not insert 'xyz': Operation not permitted | Secure Boot et module non signé, ou kernel.modules_disabled à 1, ou absence de privilèges (conteneur) ; la raison est dans dmesg |
modprobe: ERROR: could not insert 'xyz': Unknown symbol in module, or unknown parameter (see dmesg) | module compilé pour un autre noyau, dépendance manquante, ou option mal orthographiée dans modprobe.d |
modprobe: FATAL: Module xyz is in use. | modprobe -r sur un module encore utilisé ; lsmod montre qui l'utilise |
rmmod: ERROR: Module xyz is in use by: abc | même chose avec rmmod, qui nomme les modules dépendants |
modprobe: FATAL: Module xyz is builtin. | modprobe -r sur un pilote intégré, qu'on ne peut pas décharger |
dmesg vide ou incomplet. Le tampon est circulaire : sur une machine qui tourne depuis des mois, les messages du démarrage ont été écrasés. Utilisez journalctl -k, qui les a conservés.
Lire l'heure de dmesg -T comme une vérité. L'horodatage brut compte depuis le démarrage, la conversion en date est un calcul. Pour corréler avec les journaux d'autres machines, préférez journalctl -k, qui horodate à la réception avec l'horloge système.
Sécurité
- Un module, c'est du code noyau. Charger un module revient à donner à son code tous les privilèges, sans aucune isolation. C'est pourquoi le chargement exige la capacité
CAP_SYS_MODULE(voir capability), qu'un conteneur ne doit jamais recevoir. Un conteneur « privilégié » la reçoit, et peut donc charger un module dans le noyau de l'hôte : c'est une évasion complète. - Signature et verrouillage. Avec Secure Boot, seuls les modules signés par une clé de confiance se chargent. Sans Secure Boot, la signature n'est vérifiée qu'à titre indicatif. Sur un serveur dont tous les modules nécessaires sont chargés après le démarrage,
kernel.modules_disabled = 1interdit tout nouveau chargement jusqu'au redémarrage, sans retour possible : une mesure forte, qui rend aussi impossible le chargement d'un pilote de diagnostic en pleine crise. - Réduire ce qui peut se charger. Chaque module chargeable automatiquement (protocole réseau rare, système de fichiers exotique) est du code accessible à un attaquant local qui sait déclencher son chargement. Plusieurs vulnérabilités du noyau ont été exploitées ainsi. L'interdiction par
install ... /bin/falsedes modules inutiles fait partie des recommandations de l'ANSSI, traitées à la leçon 14. - Les fuites d'information du noyau.
kernel.dmesg_restrictetkernel.kptr_restrict(ce dernier mis à1par le fichier10-kernel-hardening.confd'Ubuntu) empêchent un utilisateur ordinaire de lire les adresses mémoire du noyau, qui facilitent l'exploitation d'une faille. Ne les relâchez pas pour la commodité dedmesgsanssudo. - Les sysctl de durcissement. Une série de paramètres (
kernel.yama.ptrace_scope,kernel.unprivileged_bpf_disabled,net.ipv4.conf.all.rp_filter,fs.protected_*, refus des redirections ICMP...) renforcent le système. Ils sont expliqués un par un à la leçon 14 ; retenez ici qu'ils s'écrivent exactement comme les autres, dans/etc/sysctl.d/. - La teinte comme indice. Une teinte
P,OouEinattendue signifie qu'un module que vous ne connaissez pas a été chargé. Sur un serveur dont le parc de modules est censé être standard, c'est un signal à investiguer, au même titre qu'un binaire inconnu dans/usr/local/bin. - Les conteneurs et les paramètres globaux. Un conteneur qui pourrait écrire dans
/proc/sys/vm/modifierait le comportement de toute la machine et de tous les autres conteneurs. Docker refuse donc, par son option--sysctl, tout paramètre qui n'est pas propre à un espace de noms, et Kubernetes ne permet par défaut qu'une courte liste de paramètres dits « sûrs ».
En production
- Les réglages du noyau sont de la configuration comme une autre. Un fichier par besoin dans
/etc/sysctl.d/et/etc/modules-load.d/, nommé60-à90-, commenté (pourquoi, quand, quelle mesure), versionné et déposé par l'outil d'automatisation (cours Ansible : les fondamentaux).sig-app-1etsig-app-2doivent avoir exactement les mêmes : deux machines derrière le même répartiteur qui ne se comportent pas pareil sous charge sont un cauchemar de diagnostic. - Mesurer avant de régler. Les valeurs par défaut des noyaux récents conviennent à la très grande majorité des serveurs. Les listes de « tuning » qui circulent datent souvent de noyaux d'il y a quinze ans (le
somaxconn = 65535de Camille en est un exemple). Un réglage ne s'ajoute qu'en réponse à un symptôme mesuré (compteur de débordement, épuisement de ports, latence), et se retire quand la cause est traitée autrement. - Que faire du réglage hérité. Pour chaque ligne trouvée : comprendre ce qu'elle fait, mesurer si le symptôme existe, puis la garder dans un fichier commenté ou la supprimer en le notant dans le journal des changements. Pour Signalements :
somaxconnpeut revenir à la valeur par défaut,swappinessest sans objet sans espace d'échange, la plage de ports de1024 65535doit disparaître,overcommit_memory = 1ne revient pas. - Que faire quand le noyau panique. Par défaut,
kernel.panicvaut0dans le noyau amont : après une panique, la machine reste figée indéfiniment. Sur un serveur derrière un répartiteur, on préfère souventkernel.panic = 10(redémarrer dix secondes après une panique), parfois aveckernel.panic_on_oops = 1, pour que la machine revienne d'elle-même. Le prix : la trace de la panique n'est visible que sur la console série au moment des faits, ou dans le journal si elle a eu le temps d'y être écrite. - Les messages du noyau quittent la machine. OOM, erreurs d'entrée-sortie, soft lockups et changements de teinte doivent remonter vers la supervision. Ils sont dans le journal (
journalctl -k), donc ils partent avec le reste verssig-outils(leçon 12) ; reste à poser des alertes sur ces motifs. La valeur de/proc/sys/kernel/taintedest une métrique facile à collecter. - Sur Kubernetes. Les nœuds d'un cluster sont des machines Linux, mais sur Kapsule, le service managé de Scaleway, leur configuration (modules, sysctl globaux) appartient au fournisseur : vous ne la réglez pas. Ce que vous pouvez régler, ce sont les paramètres propres à l'espace de noms réseau de chaque pod, dans son
securityContext.sysctls. Kubernetes autorise d'office une liste de paramètres « sûrs » (dontnet.ipv4.ip_local_port_range) ; les autres, même propres à un espace de noms commenet.core.somaxconn, exigent que l'administrateur du cluster les autorise sur le kubelet par--allowed-unsafe-sysctls. Un paramètre global commevm.swappinessne peut pas être fixé par un pod, et aucunnet.*ne l'est pour un pod qui partage le réseau de l'hôte. Sur un cluster que vous administrez vous-même, le couple «br_netfilterdansmodules-load.dplusnet.bridge.bridge-nf-call-iptables = 1danssysctl.d» fait partie des prérequis classiques d'un nœud.
Exercices
1. De la clé au fichier (niveau 100). Donnez le chemin du fichier qui correspond à vm.overcommit_ratio, puis la clé qui correspond à /proc/sys/net/ipv4/conf/eth0.100/forwarding. Pourquoi la seconde demande-t-elle une précaution ?
Solution
vm.overcommit_ratio correspond à /proc/sys/vm/overcommit_ratio. Pour le second, le nom d'interface eth0.100 contient un point : en notation à points, net.ipv4.conf.eth0.100.forwarding serait ambigu (le point de eth0.100 serait lu comme un séparateur). On écrit la clé avec des barres obliques : sysctl net/ipv4/conf/eth0.100/forwarding, notation acceptée par sysctl comme par les fichiers de sysctl.d.
2. Une teinte à expliquer (niveau 100). cat /proc/sys/kernel/tainted renvoie 4609 sur sig-app-2 et 0 sur sig-app-1. Décodez la valeur et dites par quoi vous commencez.
Solution
4609 = 4096 + 512 + 1 : bits 12 (O, module hors arbre), 9 (W, le noyau a émis un avertissement) et 0 (P, module propriétaire). Les deux machines devraient être identiques : sig-app-2 a un module que sig-app-1 n'a pas. On compare lsmod sur les deux, puis modinfo sur le module en trop (absence de intree: Y, champ license), et on cherche l'avertissement avec journalctl -k -b -p warning. Un pilote installé à la main lors d'un dépannage ancien est une explication courante ; il faut savoir qui l'a installé et pourquoi.
3. Le réglage perdu (niveau 200). Après réinstallation en Debian 13, sig-outils n'a plus vm.overcommit_memory = 1, pourtant présent dans /etc/sysctl.conf. Un collègue lance sudo sysctl --system, voit la valeur appliquée, et considère le problème réglé. Qu'en pensez-vous ? Décrivez la démarche complète.
Solution
Le problème n'est pas réglé : sysctl --system (l'outil de procps) lit encore /etc/sysctl.conf, mais au démarrage c'est systemd-sysctl qui applique la configuration, et sur Debian 13 il ignore ce fichier (notes de publication de Debian 13). La valeur disparaîtra au prochain redémarrage. systemd-analyze cat-config sysctl.d le montre : /etc/sysctl.conf n'y figure pas.
La démarche : d'abord se demander si le réglage doit exister. vm.overcommit_memory = 1 ne se justifie que pour un logiciel qui l'exige (Redis, par exemple). sig-outils n'en héberge pas : on le laisse à 0, on revient à la valeur par défaut tout de suite (sudo sysctl -w vm.overcommit_memory=0), on supprime la ligne de /etc/sysctl.conf pour éviter la confusion, et on note la décision dans la fiche du serveur. Si le réglage avait été justifié, on l'aurait déplacé dans /etc/sysctl.d/60-<raison>.conf, commenté, puis vérifié après un redémarrage de test avec sysctl vm.overcommit_memory.
4. Une file d'attente qui déborde (niveau 200). Pendant un pic, des clients de Signalements reçoivent des délais de connexion. nstat -az TcpExtListenOverflows augmente, ss -ltn 'sport = :8000' montre Recv-Q 2049 et Send-Q 2048. Un collègue propose de porter net.core.somaxconn à 65535. Qu'en pensez-vous ?
Solution
Send-Q 2048 est la taille effective de la file : c'est la valeur demandée par Gunicorn (--backlog, 2048 par défaut), déjà inférieure au somaxconn par défaut de 4096. Augmenter somaxconn seul ne changerait rien. La file est pleine (Recv-Q au-dessus de la limite) parce que les workers n'acceptent pas les connexions assez vite : ils sont tous occupés. Les vraies pistes sont du côté de l'application (nombre de workers, requêtes lentes, temps de réponse de sig-db) et de la capacité (ajouter une instance derrière le répartiteur). Allonger la file, en montant à la fois --backlog et somaxconn, ne ferait qu'allonger l'attente des clients, qui finiraient par abandonner de toute façon. Si on le fait malgré tout pour absorber des pics très courts, les deux valeurs doivent être cohérentes et le réglage noté avec sa mesure.
5. Ports réservés et module persistant (niveau 300). Sur sig-outils, les sauvegardes vers l'Object Storage épuisent les ports source (des Cannot assign requested address apparaissent dans le journal de l'outil de sauvegarde), et vous allez installer un exportateur de métriques qui écoute sur le port 9100. Écrivez le fichier de configuration complet, justifiez chaque valeur, et décrivez comment vous vérifiez qu'il est appliqué au démarrage.
Solution
# /etc/sysctl.d/60-sauvegardes.conf
# Épuisement des ports source pendant les envois vers s3.fr-par.scw.cloud
# (erreurs EADDRNOTAVAIL constatées le 2026-10-06, voir journal des changements).
# Plage élargie, bornes de parités différentes, début au-dessus des services locaux.
net.ipv4.ip_local_port_range = 15001 64000
# L'exportateur de métriques écoute sur 9100 : jamais attribué comme port source.
net.ipv4.ip_local_reserved_ports = 9100Justifications : la plage passe de 28 232 à 49 000 ports ; elle commence au-dessus des ports de service usuels, contrairement à 1024 65535 ; 15001 est impair et 64000 pair, comme le recommande la documentation ; la réservation de 9100 est ici redondante (9100 est hors de la plage) mais protège contre un futur élargissement, et documente l'intention. Avant d'élargir, il faut aussi se demander pourquoi tant de connexions : un outil de sauvegarde qui réutilise ses connexions HTTP (keep-alive) en ouvre beaucoup moins, et c'est souvent la meilleure correction.
Vérification : systemd-analyze cat-config sysctl.d doit montrer le fichier, et aucun fichier triant après lui ne doit redéfinir ces clés (grep -r ip_local /etc/sysctl.d /usr/lib/sysctl.d /run/sysctl.d). On applique à chaud avec sudo sysctl -p /etc/sysctl.d/60-sauvegardes.conf, puis, lors de la prochaine fenêtre de maintenance, on redémarre et on contrôle sysctl net.ipv4.ip_local_port_range net.ipv4.ip_local_reserved_ports et journalctl -b -u systemd-sysctl (aucune erreur). Ces deux paramètres sont propres à l'espace de noms réseau : si l'outil de sauvegarde tournait un jour dans un conteneur, il faudrait les fixer aussi dans la définition du conteneur.
Récapitulatif
- Un pilote est intégré au noyau ou chargé comme module depuis
/lib/modules/$(uname -r)/.lsmodliste les modules chargés,modinfodécrit un module,modules.builtinliste les pilotes intégrés. - Les modules se chargent surtout seuls : udev par l'identifiant matériel (
MODALIAS), ou le noyau lui-même quand il a besoin d'une fonction.modules-load.dforce un chargement au démarrage. - Dans
/etc/modprobe.d/:optionspour les paramètres,blacklistpour écarter un chargement par alias,install <module> /bin/falsepour interdire vraiment. Régénérer l'initramfs après modification. /proc/syscontient les paramètres du noyau ;sysctl -wles change à chaud,/etc/sysctl.d/60-*.confles rend persistants,systemd-sysctlles applique au démarrage. Fichiers triés par nom, la dernière valeur gagne.- Sur Debian 13,
/etc/sysctl.confn'est plus lu au démarrage ; sur Ubuntu 24.04, il l'est encore par un lien de compatibilité.systemd-analyze cat-config sysctl.dmontre ce que lit le démarrage. - Un réglage se justifie par une mesure :
somaxconnest plafonné par ce que demande l'application,swappinessne sert à rien sans échange,file-maxest déjà relevé par systemd,ip_forwarddoit rester à0sur un serveur d'application. /sysdécrit les objets du noyau, une valeur par fichier ; persistance par udev ou systemd-tmpfiles.dmesg -T --level=err,warnlit le tampon du noyau (avecsudo, à cause dedmesg_restrict) ;journalctl -k -b -1lit celui du démarrage précédent./proc/sys/kernel/taintednon nul signale un module externe ou un incident passé ; il faut savoir reconnaître un OOM, une erreur d'entrée-sortie, un soft lockup et une table de suivi de connexion pleine.- Les paramètres
net.*sont propres à chaque espace de noms réseau,vm.*et la plupart des autres sont globaux : un conteneur ne règle que les premiers.
Pour aller plus loin
- La documentation du noyau, Documentation for /proc/sys (
kernel,vm,fs) et IP Sysctl : la référence de chaque paramètre, avec ses valeurs par défaut. - Les pages
modprobe.d(5),modules-load.d(5),sysctl.d(5)etudevadm(8). - Tainted kernels, dans la documentation du noyau, pour le sens complet de chaque drapeau.
- Les notes de publication de Debian 13, section des problèmes à connaître, pour les autres changements de comportement par rapport à Debian 12.
- Le cours Performance et diagnostic, pour mesurer avant de régler, et le cours Le réseau sous Linux, pour les paramètres de la pile réseau.
- La leçon suivante, Mettre à jour le noyau et la distribution.
Sources
- Documentation du noyau Linux, /proc/sys/kernel/
- Documentation du noyau Linux, /proc/sys/vm/
- Documentation du noyau Linux, /proc/sys/fs/
- Documentation du noyau Linux, IP Sysctl
- Documentation du noyau Linux, Tainted kernels
- Code source du noyau 6.8, mm/oom_kill.c
- Code source du noyau 6.8, kernel/module/signing.c et kernel/module/main.c
- Page de manuel modprobe(8)
- Page de manuel modprobe.d(5)
- Page de manuel modules-load.d(5)
- Code source de kmod 31, tools/modprobe.c et tools/rmmod.c
- Page de manuel sysctl(8), procps-ng
- Code source de procps 4.0.4, src/sysctl.c
- Page de manuel sysctl.d(5), systemd
- Code source de systemd 255 : systemd-sysctl.service, 99-systemd.rules, 80-drivers.rules, src/core/main.c
- Page de manuel udevadm(8)
- Page de manuel dmesg(1), util-linux
- Debian 13, notes de publication : /etc/sysctl.conf n'est plus pris en compte
- Ubuntu, paquet systemd de noble : debian/systemd.links et debian/rules
- Ubuntu, paquet procps de noble : fichiers de /etc/sysctl.d
- Ubuntu devel, Proposal: Enabling DMESG_RESTRICT for Groovy Onward (2020)
- Kubernetes, Using sysctls in a Kubernetes Cluster
- Docker, docker container run : option --sysctl
- PostgreSQL, Managing Kernel Resources : Linux Memory Overcommit
- Gunicorn, gunicorn/config.py : réglage backlog