Optimiser les I/O disque sous Linux : Guide Avancé pour la Production
Quand on travaille avec des bases de données, des systèmes distribués ou des workloads conteneurisés intensifs, les performances disque deviennent rapidement un point de contention. Voici un guide complet pour tuner votre machine Linux et tirer le meilleur des I/O.
Pourquoi optimiser les I/O ?
Sans tuning, un disque rapide (NVMe, SSD) peut être sévèrement bridé par :
- Un mauvais choix de système de fichiers
- Des options de montage par défaut inefficaces
- Un scheduler I/O inadapté
- Une mauvaise configuration du cache mémoire
- Un alignement de partition sous-optimal
Choix du système de fichiers

ext4 : classique mais solide
- Avantages : Bonne compatibilité et robustesse
- Inconvénients : Moins efficace sur les workloads très parallèles
xfs : pour l'écriture séquentielle
- Avantages : Parfait pour les bases de données, logs, fichiers volumineux
- Inconvénients : Moins performant sur les petites écritures aléatoires
zfs : avec de la RAM
- Avantages : Compression, snapshots, intégrité
- Inconvénients : Overhead mémoire non négligeable
Options de montage recommandées
Pour ext4 :
UUID=xxxx /data ext4 defaults,noatime,nodiratime,barrier=0,data=writeback 0 2
Explications :
noatime,nodiratime: désactive la mise à jour des timestamps d'accès (-20% d'I/O)barrier=0: désactive les barrières d'écriture (gain notable sur SSD/NVMe)data=writeback: mode d'écriture asynchrone le plus rapide
Pour xfs :
UUID=xxxx /data xfs defaults,noatime,nodiratime,nobarrier,logbufs=8,logbsize=256k 0 2
Explications :
logbufs=8: augmente le nombre de buffers de loglogbsize=256k: taille optimisée des buffers de log
Pour zfs :
# Configuration via zfs set
zfs set atime=off tank/data
zfs set compression=lz4 tank/data
zfs set recordsize=128k tank/data # Pour bases de données
zfs set primarycache=metadata tank/data # Si peu de RAM
Configuration du scheduler I/O

Le scheduler par défaut n'est pas toujours optimal selon le type de disque.
Vérifier le scheduler actuel :
cat /sys/block/nvme0n1/queue/scheduler
# [mq-deadline] kyber bfq none
Pour SSD/NVMe :
# none (NOOP) - délègue tout au contrôleur SSD
echo none > /sys/block/nvme0n1/queue/scheduler
# Permanent via kernel parameter
# GRUB_CMDLINE_LINUX="elevator=none"
Pour HDD :
# mq-deadline - optimisé pour les disques rotatifs
echo mq-deadline > /sys/block/sda/queue/scheduler
Configuration permanente :
# /etc/udev/rules.d/60-schedulers.rules
# SSD/NVMe
ACTION=="add|change", KERNEL=="nvme[0-9]n[0-9]", ATTR{queue/scheduler}="none"
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="0", ATTR{queue/scheduler}="none"
# HDD
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="1", ATTR{queue/scheduler}="mq-deadline"
Tuning du cache et de la mémoire virtuelle

Paramètres /proc/sys/vm/ critiques :
# /etc/sysctl.d/99-io-performance.conf
# Réduire la swapiness (sauf si swap obligatoire)
vm.swappiness = 1
# Dirty pages - crucial pour les écritures
vm.dirty_background_ratio = 5 # Début écriture asynchrone à 5% RAM
vm.dirty_ratio = 10 # Blocage processus à 10% RAM
vm.dirty_expire_centisecs = 1000 # Flush dirty pages après 10s
vm.dirty_writeback_centisecs = 500 # Réveil pdflush toutes les 5s
# Pour workloads avec beaucoup d'écritures séquentielles
vm.dirty_background_bytes = 67108864 # 64MB
vm.dirty_bytes = 134217728 # 128MB
# Cache de la dentries/inodes
vm.vfs_cache_pressure = 50 # Réduit la pression sur les caches VFS
Application immédiate :
sysctl -p /etc/sysctl.d/99-io-performance.conf
Queue depth et paramètres avancés
Pour NVMe :
# Augmenter la queue depth
echo 32 > /sys/block/nvme0n1/queue/nr_requests
# Désactiver NCQ si problématique
echo 1 > /sys/block/nvme0n1/queue/nomerges
# Read-ahead optimisé pour séquentiel
echo 256 > /sys/block/nvme0n1/queue/read_ahead_kb
Pour SSD SATA :
echo 16 > /sys/block/sda/queue/nr_requests
echo 128 > /sys/block/sda/queue/read_ahead_kb
Alignement des partitions

Vérification de l'alignement :
# Doit être multiple de 4096 (4K) pour les SSD modernes
sudo fdisk -l /dev/nvme0n1
# Start doit être divisible par 2048 (secteurs de 512B = 1MB)
Création de partition alignée :
# parted avec alignement optimal
sudo parted -s -a optimal /dev/nvme0n1 mklabel gpt
sudo parted -s -a optimal /dev/nvme0n1 mkpart primary 0% 100%
Optimisations spécifiques aux workloads
Base de données (PostgreSQL/MySQL) :
# XFS avec des paramètres spécifiques
mkfs.xfs -f -d agcount=16 -l size=256m -n size=8192 /dev/nvme0n1p1
# Options de montage
mount -o noatime,nodiratime,nobarrier,logbufs=8,logbsize=256k,allocsize=16m /dev/nvme0n1p1 /var/lib/postgresql
Logs et écritures séquentielles :
# ext4 optimisé pour les logs
mkfs.ext4 -F -E stride=32,stripe-width=32 -b 4096 /dev/nvme0n1p2
# Montage avec writeback
mount -o noatime,data=writeback,barrier=0,nobh /dev/nvme0n1p2 /var/log
Conteneurs/Kubernetes :
# XFS pour le runtime containerd/docker
mkfs.xfs -f -n ftype=1 /dev/nvme0n1p3
mount -o noatime,nodiratime,nobarrier,prjquota /dev/nvme0n1p3 /var/lib/containerd
Benchmarking et monitoring

Test des performances avec fio :
Test lecture aléatoire 4K :
fio --name=random-read --ioengine=libaio --rw=randread --bs=4k --numjobs=4 \
--iodepth=32 --runtime=60 --time_based --filename=/data/testfile --size=4G
Test écriture séquentielle :
fio --name=seq-write --ioengine=libaio --rw=write --bs=1M --numjobs=1 \
--iodepth=4 --runtime=60 --time_based --filename=/data/testfile --size=4G
Test mixte réaliste :
fio --name=mixed-workload --ioengine=libaio --rw=randrw --rwmixread=70 \
--bs=8k --numjobs=8 --iodepth=16 --runtime=300 --filename=/data/testfile --size=8G
Monitoring en temps réel :
iostat pour surveiller les performances :
# Statistiques détaillées toutes les 2 secondes
iostat -x 2
# Métriques clés à surveiller :
# %util : utilisation du disque (>80% = saturé)
# await : latence moyenne (ms)
# r/s, w/s : IOPS lecture/écriture
# rkB/s, wkB/s : débit Ko/s
iotop pour identifier les processus gourmands :
iotop -o -d 1
Avec dstat pour vue globale :
dstat -tdD sda,nvme0n1 1
Configuration pour environnements spécifiques
Kubernetes/containerd :
# /etc/systemd/system/kubelet.service.d/20-io-tuning.conf
[Service]
ExecStartPre=/bin/bash -c 'echo none > /sys/block/nvme0n1/queue/scheduler'
ExecStartPre=/bin/bash -c 'echo 32 > /sys/block/nvme0n1/queue/nr_requests'
Base de données haute performance :
# Configuration PostgreSQL complémentaire
# shared_buffers = 25% de la RAM
# effective_cache_size = 75% de la RAM
# checkpoint_completion_target = 0.9
# wal_buffers = 16MB
# wal_compression = on (PG 14+)
Validation des optimisations
Script de test automatisé :
#!/bin/bash
# test-io-performance.sh
DEVICE="/dev/nvme0n1"
MOUNT_POINT="/benchmark"
echo "=== Test performance I/O sur $DEVICE ==="
# Lecture séquentielle
echo "Test lecture séquentielle 1GB..."
dd if=$DEVICE of=/dev/null bs=1M count=1024 iflag=direct 2>&1 | grep -v records
# Écriture séquentielle (ATTENTION : destructif)
echo "Test écriture séquentielle 1GB..."
dd if=/dev/zero of=$MOUNT_POINT/testfile bs=1M count=1024 oflag=direct,sync 2>&1 | grep -v records
# Latence
echo "Test latence aléatoire..."
fio --name=latency-test --ioengine=libaio --rw=randread --bs=4k \
--numjobs=1 --iodepth=1 --runtime=10 --filename=$MOUNT_POINT/testfile \
--size=1G --output-format=terse | awk -F';' '{print "Latence moyenne: " $40 "us"}'
rm -f $MOUNT_POINT/testfile
Checklist de production
✅ Avant mise en production :
- [ ] Scheduler I/O approprié configuré
- [ ] Options de montage optimisées
- [ ] Paramètres vm.dirty_* ajustés
- [ ] Partitions alignées correctement
- [ ] Tests de charge réalisés
- [ ] Monitoring des métriques I/O en place
- [ ] Backup de configuration avant tuning
✅ Surveillance continue :
- [ ] %util < 80% en moyenne
- [ ] await < 10ms pour SSD, < 20ms pour HDD
- [ ] Pas de pics d'I/O wait prolongés
- [ ] IOPS et débit conformes aux attentes
Conclusion
L'optimisation des I/O disque sous Linux nécessite une approche holistique combinant choix du filesystem, tuning du kernel, et configuration adaptée au workload. Les gains peuvent être spectaculaires : +200% en IOPS, -50% en latence.
Points clés à retenir :
- Adapter le scheduler au type de disque (none pour SSD, mq-deadline pour HDD)
- Désactiver
atimesur tous les filesystems de production - Ajuster les paramètres
vm.dirty_*selon la charge d'écriture - Tester et monitorer en continu les performances
N'oubliez pas : toujours tester en environnement de développement avant de déployer en production, et garder une sauvegarde de la configuration de base.