Pics CPU toutes les quelques minutes : la tâche planifiée à vérifier en premier

Cet article vous a aidé ?

Tout va bien… jusqu’à ce que ça ne soit plus le cas. Les graphiques ressemblent à une mer calme puis—toutes les quelques minutes—votre CPU part en sprint. La latence augmente. Les ventilateurs s’emballent. Votre téléphone d’astreinte se met à vibrer jusqu’à choir du chevet.

Si les pics sont périodiques, partez du principe qu’il s’agit d’une tâche planifiée tant que l’on n’a pas prouvé le contraire. Et la tâche planifiée que vous devez vérifier en premier est celle que vous n’avez pas programmée : les timers du système et les agents du fournisseur, pas votre application. Plus précisément : les timers systemd et cron (et leurs amis « utiles » comme logrotate, updatedb, les timers apt, les agents de monitoring et les clients de sauvegarde).

Playbook de diagnostic rapide (les 10 premières minutes)

Les pics périodiques sont un cadeau. Les pics aléatoires sont un roman d’horreur. Les pics périodiques sont une invitation calendrier.

Minute 0–2 : confirmer le motif et capturer le coupable

  1. Confirmer la périodicité. Regardez votre monitoring : les pics arrivent-ils toutes les 1, 5, 10, 15, 60 minutes, ou à :00 ou :30 ? Ce sont des cadences de timers classiques.
  2. Pendant le prochain pic, capturez les principaux responsables. Utilisez top ou pidstat. Ne « regardez pas plus tard » ; le nom du processus est la moitié de la bataille.
  3. Vérifiez la file d’exécution et l’iowait. Un %usr/%sys élevé suggère du travail CPU ; un %wa élevé suggère un goulot d’I/O avec des effets secondaires CPU (compression, checksums, travail type fsck, chiffrement, etc.).

Minute 2–6 : identifier le planificateur

  1. Listez les timers systemd. Cela attrape les cas « je jure qu’il n’y a pas de cron ».
  2. Listez les sources cron. Crontabs utilisateurs, /etc/crontab et les répertoires /etc/cron.*.
  3. Vérifiez les agents et le monitoring du fournisseur. Beaucoup se présentent comme des services systemd et planifient leurs propres exécutions en interne.

Minute 6–10 : corréler avec les logs et prendre une action sûre

  1. Corréler par horodatage. Les journaux du journal autour du pic afficheront généralement l’unité qui s’est lancée.
  2. Atténuation immédiate la plus sûre. Si c’est non critique (updatedb, logrotate, un script de rapport), retardez-le ou ajoutez du jitter. Si c’est critique (sauvegardes, scans de sécurité), réduisez la concurrence ou la portée plutôt que de désactiver.

Idée paraphrasée attribuée à Gene Kim : La fiabilité vient de rendre le travail visible et répétable, pas des exploits à 3 h du matin.

Pourquoi les pics périodiques crient « tâche planifiée »

Le CPU ne monte pas « toutes les cinq minutes » par accident. Les humains programment en nombres ronds. Les systèmes d’exploitation aussi. Les agents « enterprise » aussi, souvent écrits par quelqu’un qui n’a jamais partagé un hyperviseur avec votre base de données.

Les pics CPU périodiques proviennent généralement de l’une de ces catégories :

  • Tâches de maintenance : logrotate, updatedb (mlocate), nettoyage de /tmp, vérifications de mise à jour de paquets, hooks de renouvellement de certificats.
  • Sauvegarde et indexation : scans de systèmes de fichiers, scripts de snapshot, indexation de déduplication, synchronisation d’object-store, scans antivirus.
  • Monitoring et télémétrie : collectes de métriques, inventaires, vérifications de posture de sécurité.
  • Travail CPU côté stockage : compression, checksums, chiffrement, parité RAID, opérations de scrub, calculs de diff de snapshot.
  • « Aide » applicative : réchauffement de cache, génération de rapports périodiques, compaction (bases de données), réindexation.

Piège : vous pouvez regarder les graphiques CPU toute la journée et rater la cause si vous n’alignez pas les horodatages. Chaque tâche périodique laisse une empreinte : un démarrage de processus, une ligne de log, une activation d’unité, un fichier touché, un pic réseau.

Blague #1 : Les tâches planifiées sont comme les anniversaires au bureau—elles arrivent chaque année, et pourtant elles surprennent toujours tout le monde.

La tâche planifiée à vérifier en premier (et pourquoi)

La première chose que je vérifie est les timers systemd, puis cron. Pas parce que cron est rare—cron est partout—mais parce que les équipes vérifient souvent « cron » et s’arrêtent là. Pendant ce temps, les timers systemd déclenchent discrètement :

  • apt-daily.timer et apt-daily-upgrade.timer (Debian/Ubuntu)
  • fstrim.timer (TRIM SSD ; peut impacter les sous-systèmes de stockage)
  • logrotate.timer (ou logrotate via cron selon la distro)
  • man-db.timer (oui, vraiment)
  • updatedb.timer (scan de système de fichiers)
  • Timers fournisseurs pour agents de sauvegarde, EDR, scanners de conformité

Pourquoi en premier ? Parce que les timers systemd peuvent fonctionner avec un comportement de rattrapage persistant. Si un hôte était éteint au moment prévu, un timer avec Persistent=true peut se déclencher immédiatement au démarrage. C’est ainsi que vous obtenez « des pics CPU toutes les quelques minutes » après une panne : une meute de timers manqués qui rattrapent le temps perdu.

De plus, les timers peuvent être configurés avec des délais aléatoires. C’est bien. Mais quand ils ne le sont pas, les flottes effectuent un travail synchronisé. Si vous avez déjà vu 500 nœuds exécuter la même tâche à la minute 0, vous savez déjà comment l’histoire se termine.

Pratique : 12+ actions avec commandes, sorties et décisions

Ci‑dessous des tâches pratiques que j’exécuterais sur une machine Linux en production. Pour chacune : commande, ce que signifie la sortie, et quelle décision prendre ensuite. Exécutez-les pendant un pic si possible. Sinon, vous pouvez recueillir suffisamment d’éléments entre les pics.

Tâche 1 : Surveiller le CPU par processus dans le temps (attraper le pic)

cr0x@server:~$ pidstat -u -h 1
Linux 6.5.0 (server)  02/05/2026  _x86_64_  (16 CPU)

# Time        UID      PID    %usr %system  %CPU   Command
12:00:01     0       1423     0.00    0.00  0.00   systemd
12:00:02     0      22891    92.00    6.00  98.00  updatedb
12:00:03     0      22891    88.00    5.00  93.00  updatedb

Sens : updatedb monopolise le CPU pendant le pic. Ce n’est pas votre application. C’est une mise à jour d’index du système de fichiers planifiée.

Décision : Identifier qui déclenche updatedb (timer ou cron), puis reprogrammer, limiter ou exclure des chemins.

Tâche 2 : Confirmer le mode CPU global (user/system/iowait)

cr0x@server:~$ mpstat -P ALL 1 5
Linux 6.5.0 (server)  02/05/2026  _x86_64_  (16 CPU)

12:00:01 PM  all  %usr %nice %sys %iowait %irq %soft %steal %idle
12:00:01 PM  all  62.50  0.00 12.30  0.20 0.00  0.50   0.00 24.50
12:00:02 PM  all  89.10  0.00  8.40  0.10 0.00  0.40   0.00  2.00

Sens : Un %usr élevé suggère du calcul pur (hachage/compression/scan) plutôt qu’une attente disque.

Décision : Concentrez-vous sur le processus et sa planification. Si %iowait était élevé, pivotez vers la latence stockage et la profondeur des files d’attente.

Tâche 3 : Voir la charge moyenne vs file d’exécution (contention CPU vs « juste occupé »)

cr0x@server:~$ vmstat 1 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 1  0      0  81234  10240 913024    0    0     2     5  120  300 12  3 85  0  0
12  0      0  80120  10240 913500    0    0     0     0 3400 9800 88  9  3  0  0

Sens : r=12 sur une machine 16 cœurs signifie beaucoup de threads exécutables ; pas bloqués, simplement en compétition pour le CPU.

Décision : Si cela correspond à un timer qui se déclenche, réduisez la concurrence (nice/ionice, paramètres de tâche, ou la tâche elle-même).

Tâche 4 : Lister les timers systemd (les suspects habituels)

cr0x@server:~$ systemctl list-timers --all
NEXT                         LEFT          LAST                         PASSED       UNIT                         ACTIVATES
Mon 2026-02-05 12:05:00 UTC  2min 12s      Mon 2026-02-05 12:00:01 UTC  2min 47s     updatedb.timer               updatedb.service
Mon 2026-02-05 12:10:00 UTC  7min 12s      Mon 2026-02-05 12:00:08 UTC  2min 40s     apt-daily.timer              apt-daily.service
Mon 2026-02-05 12:15:00 UTC  12min 12s     Mon 2026-02-05 12:00:10 UTC  2min 38s     logrotate.timer              logrotate.service

Sens : Plusieurs timers se déclenchent à la minute 0 et se répètent. L’horodatage LAST est de l’or : faites correspondre avec l’heure de votre pic CPU.

Décision : Inspectez la configuration du timer et le service qu’il active. Appliquez du jitter, changez le planning ou désactivez en toute sécurité si approprié.

Tâche 5 : Inspecter la définition d’un timer et d’un service (ce qui s’exécute réellement)

cr0x@server:~$ systemctl cat updatedb.timer
# /lib/systemd/system/updatedb.timer
[Unit]
Description=Update a database for mlocate

[Timer]
OnCalendar=*:0/5
Persistent=true
RandomizedDelaySec=0

[Install]
WantedBy=timers.target

Sens : Toutes les 5 minutes, rattrapage persistant, et jitter nul. C’est pratiquement une invitation à la douleur synchronisée.

Décision : Ajouter RandomizedDelaySec, réduire la fréquence, ou désactiver si vous n’avez pas besoin de locate sur un serveur.

Tâche 6 : Vérifier cron (système et par utilisateur)

cr0x@server:~$ sudo ls -l /etc/cron.hourly /etc/cron.daily /etc/cron.d
/etc/cron.d:
total 12
-rw-r--r-- 1 root root  240 Jan 10  2026 backup-agent
-rw-r--r-- 1 root root  180 Dec  2  2025 mlocate
-rw-r--r-- 1 root root  210 Nov 18  2025 sysstat

Sens : Cron est toujours en jeu. /etc/cron.d/mlocate pilote probablement updatedb sur des distros qui n’utilisent pas le timer, ou en plus du timer.

Décision : Assurez-vous de ne pas avoir de planification dupliquée (timer + cron). Choisissez-en une et désactivez l’autre.

Tâche 7 : Voir les entrées crontab pour un utilisateur (là où se cachent les surprises)

cr0x@server:~$ sudo crontab -l -u root
*/5 * * * * /usr/local/sbin/inventory-scan --json --upload
15 * * * * /usr/local/sbin/storage-report

Sens : La cadence de cinq minutes est bien là. inventory-scan sent l’« agent », souvent lourd en CPU (parcours de fichiers, interrogation de paquets, hachage).

Décision : Mesurez le temps d’exécution et le CPU, puis réduisez la fréquence, limitez la portée (exclure des répertoires), ou déplacez-le hors des hôtes critiques.

Tâche 8 : Corréler avec les logs du journal à l’heure du pic

cr0x@server:~$ sudo journalctl --since "2026-02-05 11:58:00" --until "2026-02-05 12:02:00" --no-pager
Feb 05 12:00:01 server systemd[1]: Started Update a database for mlocate.
Feb 05 12:00:01 server updatedb[22891]: updatedb: pruning "/var/lib/docker/overlay2"
Feb 05 12:00:10 server systemd[1]: Started Rotate log files.
Feb 05 12:00:10 server logrotate[22940]: rotating pattern: /var/log/*.log  forced from command line (1 rotations)

Sens : Vous avez maintenant une chronologie. Ce n’est pas une « charge mystère » ; ce sont deux jobs de maintenance qui se superposent.

Décision : Échelonnez-les. La collision de tâches de maintenance est le classique « pourquoi les pics sont pires à minuit ? ».

Tâche 9 : Identifier quel cgroup/unité appartient à un PID chaud

cr0x@server:~$ cat /proc/22891/cgroup
0::/system.slice/updatedb.service

Sens : Le processus est détenu par updatedb.service. Cela facilite le réglage avec les options systemd.

Décision : Appliquer CPUQuota, Nice, ou IOSchedulingClass via un drop-in si vous ne pouvez pas modifier la tâche elle‑même.

Tâche 10 : Voir la ligne de commande exacte du processus

cr0x@server:~$ ps -p 22891 -o pid,ppid,ni,etimes,cmd
  PID  PPID  NI ELAPSED CMD
22891     1   0      18 /usr/bin/updatedb.mlocate --prunepaths=/tmp /var/lib/docker /var/lib/kubelet

Sens : Il scanne des répertoires lourds (Docker, kubelet). Ces chemins brassent beaucoup d’inodes. C’est CPU plus I/O métadonnées.

Décision : Exclure ces chemins ou arrêter updatedb sur des hôtes containers. « locate » n’est pas un SLO de production.

Tâche 11 : Vérifier les pics CPU historiques avec sar (prouver la périodicité)

cr0x@server:~$ sar -u -s 11:30:00 -e 12:10:00
Linux 6.5.0 (server)  02/05/2026  _x86_64_  (16 CPU)

11:35:00 AM     %user     %system     %iowait      %idle
11:35:00 AM      6.20        1.10        0.10      92.60
11:40:00 AM     68.30        8.90        0.20      22.60
11:45:00 AM      7.10        1.20        0.10      91.60
11:50:00 AM     70.40        9.10        0.10      20.40
11:55:00 AM      6.90        1.00        0.10      92.00
12:00:00 PM     71.20        9.00        0.10      19.70

Sens : Pics toutes les 10 minutes (ou toutes les 5 selon l’échantillonnage). Cela correspond aux timers et aux crons.

Décision : Cessez de débattre « est-ce l’appli ? » et commencez à faire correspondre les plannings à ces horodatages.

Tâche 12 : Utiliser perf pour vérifier où va le temps CPU (quand ce n’est pas évident)

cr0x@server:~$ sudo perf top -p 22891
Samples: 2K of event 'cpu-clock', Event count (approx.): 2000000000
  35.12%  updatedb   libc.so.6        [.] __memmove_avx_unaligned_erms
  22.40%  updatedb   libz.so.1        [.] deflate_slow
  10.08%  updatedb   updatedb         [.] hash_path

Sens : La compression et le hachage dominent. C’est lourd en CPU par conception, pas un bug du noyau.

Décision : Réduire les données scannées, réduire la fréquence, ajouter des limites cgroup, ou déporter le travail hors de la machine.

Tâche 13 : Trouver quelle unité a démarré récemment (grep rapide dans le journal)

cr0x@server:~$ sudo journalctl -S "12:00:00" -U "12:01:00" -g "Started " --no-pager
Feb 05 12:00:01 server systemd[1]: Started Update a database for mlocate.
Feb 05 12:00:10 server systemd[1]: Started Rotate log files.

Sens : C’est la vue la plus rapide « qu’est-ce qui vient de démarrer ? » quand vous pouvez borner l’heure du pic.

Décision : Investiguer l’unité démarrée avant de plonger dans le profilage applicatif.

Tâche 14 : Ralentir un service systemd en toute sécurité (override drop-in)

cr0x@server:~$ sudo systemctl edit updatedb.service
# (creates a drop-in override)
cr0x@server:~$ sudo cat /etc/systemd/system/updatedb.service.d/override.conf
[Service]
Nice=10
CPUQuota=20%
IOSchedulingClass=best-effort
IOSchedulingPriority=7
cr0x@server:~$ sudo systemctl daemon-reload
cr0x@server:~$ sudo systemctl restart updatedb.service

Sens : Vous n’avez pas « corrigé » updatedb ; vous avez fait en sorte qu’il cesse d’intimider le reste de l’hôte.

Décision : Utiliser la limitation comme mitigation, puis effectuer la vraie correction : replanifier, ajouter du jitter et réduire le périmètre du scan.

Tâche 15 : Ajouter du jitter à un timer (stopper la ruée de la flotte)

cr0x@server:~$ sudo systemctl edit updatedb.timer
cr0x@server:~$ sudo cat /etc/systemd/system/updatedb.timer.d/override.conf
[Timer]
RandomizedDelaySec=180
cr0x@server:~$ sudo systemctl daemon-reload
cr0x@server:~$ sudo systemctl restart updatedb.timer

Sens : Le job s’exécute toujours, mais pas simultanément sur toute la flotte et pas exactement à la minute près.

Décision : Si de nombreux nœuds ont des pics simultanés, le jitter est souvent le changement avec le meilleur ROI.

Cron vs timers systemd vs « agents » : comportement réel

Pas besoin de guerres religieuses sur les planificateurs. Il vous faut un modèle mental correct.

Cron : simple, omniprésent, et facile à dupliquer accidentellement

Cron exécute des commandes à horaire fixe. C’est tout. Sa simplicité explique sa présence partout—et pourquoi il devient la décharge pour des scripts « temporaires » qui vivent cinq ans.

Où cron cache du travail :

  • Crontab système : /etc/crontab
  • Répertoires drop-in : /etc/cron.d/, /etc/cron.hourly/, /etc/cron.daily/, etc.
  • Crontabs utilisateur : dans /var/spool/cron ou /var/spool/cron/crontabs

Mode d’échec classique de cron en flotte : synchronisation — chaque machine exécute la même chose au même moment parce que tout le monde a copié-collé la même entrée crontab.

Timers systemd : plus visibles, plus puissants, et capables de surprendre

Les timers sont des objets systemd de première classe. Ils ont des logs. Ils ont un état. Ils peuvent rattraper après une panne. Ils peuvent ajouter du jitter. Ils peuvent avoir des fenêtres d’exactitude qui retardent légèrement le déclenchement pour coalescer les wakeups.

Mode d’échec classique des timers : persistance + tempêtes de démarrage. Après maintenance, reboot ou autoscaling, une série de timers manqués se déclenche. Si chaque timer est « petit », la charge combinée ne l’est pas.

Agents : « on tourne toutes les 5 minutes » n’est pas un contrat, c’est une menace

Agents de monitoring, collecteurs d’inventaire, sécurité endpoint, agents de sauvegarde et outils de conformité implémentent souvent leurs propres plannings en interne. Il se peut que vous ne voyiez ni timer ni cron ; vous verrez un démon longue durée qui se réveille et fait beaucoup de travail.

Astuce diagnostique :

  • Burst réguliers de CPU dans un seul daemon.
  • Burst réguliers de processus enfants forkés par ce daemon.
  • Lignes de log qui se répètent toutes les N minutes.

L’angle stockage : des pics CPU déguisés en travail I/O

En tant qu’ingénieur stockage, je le dis tout net : beaucoup de « pics CPU » sont des tâches de stockage revêtant un costume CPU.

Les parcours de métadonnées sont du travail CPU

Des jobs comme updatedb, les scans antivirus, l’énumération de fichiers de sauvegarde et les scanners d’intégrité effectuent de vastes traversées d’annuaires. Même si vos disques sont rapides, transformer des millions de dentries et inodes en une liste coûte du CPU. Sur des systèmes de fichiers réseau c’est pire : chaque appel de métadonnée devient un aller-retour réseau, plus parsage et cache côté client.

Compression et checksumming sont du travail CPU

Si vous avez activé la compression « pour économiser de l’espace », félicitations : vous avez aussi accepté de payer en CPU à chaque écriture (et parfois à la lecture). Beaucoup d’outils de sauvegarde compressent par défaut. Les pipelines d’acheminement de logs compressent. Même logrotate peut compresser des archives. Ces jobs s’alignent souvent sur des bornes temporelles et créent des rafales.

TRIM et scrubs ne sont pas gratuits

fstrim peut provoquer une activité notable sur le périphérique. Un scrub RAID ou filesystem peut causer de l’usage CPU dans des threads noyau, surtout si des checksums sont impliqués. Vous verrez peut‑être le pic CPU, mais la cause racine est une maintenance planifiée du stockage qui coïncide avec votre trafic peak.

Le chiffrement est prévisible—et en rafales quand planifié

Le chiffrement au repos et les sauvegardes chiffrées consomment des cycles CPU. C’est acceptable quand c’est régulier. C’est pénible quand c’est rafaleux. Une sauvegarde nocturne qui chiffre et compresse en même temps peut ressembler à un « pic CPU mystère » pour qui ne consulte pas l’agenda des sauvegardes.

Blague #2 : Rien ne dit « prêt pour l’entreprise » comme un agent de sauvegarde qui benchmarke votre CPU à midi sans demander.

Trois mini-histoires d’entreprise (vécues)

Mini-histoire 1 : L’incident causé par une fausse hypothèse (« Nous n’utilisons pas cron »)

Une entreprise de taille moyenne exécutait un ensemble de serveurs API derrière un load balancer. Toutes les cinq minutes, les taux d’erreur augmentaient légèrement. Pas une panne totale—juste assez pour griller l’SLO comme une fuite lente. L’astreinte a d’abord pensé au classique : garbage collector ou un voisin bruyant sur l’hyperviseur.

Ils ont vérifié les logs applicatifs. Rien. Ils ont vérifié les déploiements. Rien. Ils ont vérifié « cron ». Aucune entrée pertinente dans crontab -l. Ils ont déclaré, confiants, « Nous n’utilisons pas cron sur ces machines. » L’enquête s’est égarée en profilage d’endpoints et réglage de pools de threads.

La percée est survenue quand quelqu’un a comparé les horodatages des pics CPU avec systemctl list-timers. Là : updatedb.timer toutes les cinq minutes, persistant, sans jitter. Il venait d’une image OS de base qui avait été « durcie » en tout sauf l’endroit où elle scannait le système de fichiers en boucle.

Pourquoi cela a-t-il affecté l’API ? Les serveurs hébergeaient aussi des runtimes de conteneurs. Le scan updatedb parcourait d’énormes arbres overlay. Il n’a pas seulement utilisé le CPU ; il a brassé le page cache et les caches de métadonnées. L’appli devenait légèrement plus lente, puis récupérait—encore et encore, comme une petite attaque par déni de service interne.

La correction a été banale : désactiver updatedb sur ces hôtes et exiger qu’une politique documente les timers activés dans les images de base. Le postmortem n’était pas sur locate. Il portait sur les hypothèses. « Pas de cron » ne voulait pas dire « pas de tâches planifiées ». Cela voulait dire « nous n’avons pas regardé le bon planificateur ».

Mini-histoire 2 : L’optimisation qui s’est retournée contre l’équipe (compression partout)

Une équipe plateforme interne voulait réduire les coûts de stockage. Les logs étaient volumineux, les sauvegardes plus encore, et le CFO avait découvert le mot « efficacité ». L’équipe a activé une compression agressive dans le pipeline de sauvegarde, a programmé le job toutes les 15 minutes pour un meilleur RPO, et a fêté les gains d’espace.

Puis sont arrivés les pics CPU : tranchants, rythmiques et laids. La latence des requêtes a augmenté et des timeouts se sont produits sur des services partageant les mêmes nœuds que le client de sauvegarde. L’équipe de sauvegarde jurait que rien n’avait changé « pour l’appli », ce qui était techniquement vrai et opérationnellement inutile.

Ce qui s’est réellement passé : compresser des petits lots toutes les 15 minutes a provoqué des rafales CPU constantes plutôt qu’un seul pic nocturne prévisible. Les rafales se superposaient à d’autres timers plateforme—logrotate, compaction métriques, vérifs de paquets—et créaient une « minute occupée » récurrente. Le système n’était pas surchargé en moyenne ; il était surchargé selon un motif répété.

L’optimisation tentée—sauvegardes incrémentales très fréquentes avec compression maximale—a échoué car elle a ignoré la contention et la co-location. Ils ont réglé le problème en baissant le niveau de compression, en appliquant des quotas CPU au service de sauvegarde, et en ajoutant des délais aléatoires. L’utilisation d’espace a légèrement augmenté. La stabilité s’est beaucoup améliorée. Le CFO a eu un joli graphique descendant. L’astreinte a retrouvé le sommeil.

Mini-histoire 3 : La pratique ennuyeuse qui a sauvé la situation (jitter et budgets)

Une entreprise financière gérait une large flotte Linux avec des exigences de latence strictes. Ils avaient appris, à la dure, que la « maintenance à minuit » est juste une façon de créer des catastrophes synchronisées à minuit. Ils traitaient donc le travail en arrière-plan comme du trafic de production : il avait besoin de budgets, d’observabilité et d’étalement.

Chaque nouvelle tâche planifiée devait déclarer : fréquence, durée estimée, profil CPU (approximatif) et si elle pouvait être retardée. Les tâches étaient ajoutées comme timers systemd avec jitter par défaut. Ils utilisaient RandomizedDelaySec sur tout ce qui n’était pas urgent et évitaient de tout programmer pile à l’heure.

Un jour, ils ont déployé une mise à jour d’agent de conformité plus lourde que prévu. Elle a fait un inventaire de système de fichiers et des vérifications cryptographiques. L’utilisation CPU a augmenté—mais elle n’a pas généré de pics synchronisés sur la flotte. Pourquoi ? Le timer avait un planning de 10 minutes avec un délai aléatoire de 6 minutes, et le service avait un quota CPU. Le travail s’est étalé. Les load balancers n’ont jamais vu de chute synchronisée.

Ce n’était pas de l’ingénierie glamour. C’était l’équivalent opérationnel de ranger ses outils après utilisation. Mais cela a transformé un incident potentiellement sévère en non-événement, ce qui est l’effet le plus agréable en production.

Erreurs fréquentes : symptôme → cause racine → fix

Voici la partie où la plupart des équipes gaspillent des heures. Ne le faites pas.

1) Pics toutes les 5 minutes, « pas de jobs cron », rien dans les logs applicatifs

  • Symptôme : CPU monte à 80–100 % toutes les 5 minutes. La latence applicative augmente. Rien d’évident dans les logs de l’appli.
  • Cause racine : timer systemd (souvent updatedb.timer, timers d’agents, timers de mise à jour) ou un démon qui se réveille périodiquement.
  • Fix : systemctl list-timers --all, corréler avec journalctl, ajouter du jitter ou désactiver les timers non essentiels ; plafonner le CPU avec un override drop-in.

2) Pics à :00 sur de nombreux hôtes (synchronisés)

  • Symptôme : Le cluster entier a des pics CPU au début de l’heure ; les services en aval rapportent des pics de latence p95.
  • Cause racine : plannings synchronisés (cron ou timers sans RandomizedDelaySec), souvent dus à des configurations d’image identiques.
  • Fix : ajouter du jitter, échelonner les plannings, utiliser l’exactitude et le délai aléatoire des timers systemd ; éviter la convention « minute 0 ».

3) Les pics CPU coïncident avec un disque occupé, mais le CPU semble coupable

  • Symptôme : Pics CPU, mais %iowait augmente aussi ; alarmes de latence stockage ; threads applicatifs bloqués.
  • Cause racine : maintenance I/O planifiée (compression logrotate, sauvegarde, scrub/trim) déclenchant du travail CPU (compression, checksums) et saturant les files stockage.
  • Fix : replanifier les tâches I/O hors pic ; réduire le parallélisme ; utiliser ionice/IOSchedulingClass ; vérifier la santé du stockage et la profondeur des files.

4) Pics démarrés après activation d’un outil « scan de sécurité » ou d’inventaire

  • Symptôme : rafales CPU périodiques, nombreux processus courts, beaucoup de statfs.
  • Cause racine : agent EDR/complicité parcourant le système de fichiers, hachant des binaires, scannant des conteneurs.
  • Fix : affiner les exclusions (répertoires de conteneurs, caches de build), réduire la fréquence des scans, pousser le fournisseur pour un mode moins intrusif ; isoler sur des nœuds dédiés si nécessaire.

5) Pics uniquement après un reboot ou une période d’indisponibilité

  • Symptôme : le boot semble normal, puis dans les minutes qui suivent CPU pique de façon répétée, parfois plusieurs tâches l’une après l’autre.
  • Cause racine : timers systemd avec Persistent=true qui « rattrapent » les exécutions manquées ; plusieurs timers manqués s’exécutent au démarrage.
  • Fix : ajouter RandomizedDelaySec, revoir le paramètre Persistent, et s’assurer que les services critiques au démarrage ne concourent pas avec des tâches de maintenance.

6) Les pics disparaissent quand vous lancez le job manuellement « pour tester »

  • Symptôme : vous exécutez le script suspect à la main et tout semble normal ; pourtant les pics continuent ensuite.
  • Cause racine : l’environnement planifié diffère : PATH différent, nice/ionice différent, arguments différents, répertoire de travail différent, ou il s’exécute côte à côte avec d’autres tâches.
  • Fix : capturez la ligne de commande et l’environnement exacts depuis l’unité systemd ou les logs cron ; reproduisez avec les mêmes paramètres et la même concurrence.

Listes de contrôle / plan étape par étape

Checklist A : Hôte unique avec pics périodiques

  1. Notez la cadence du pic (toutes les 5 minutes ? à :00 ?).
  2. Capturez les processus au CPU pendant un pic (pidstat ou top -H).
  3. Vérifiez le mode CPU (mpstat) et la file d’exécution (vmstat).
  4. Listez les timers systemd et faites correspondre les horodatages (systemctl list-timers --all).
  5. Listez les sources cron (/etc/cron.d, crontab -l pour les utilisateurs clés).
  6. Corrélez avec les logs (journalctl autour du pic).
  7. Confirmez l’unité/cgroup du PID chaud (/proc/PID/cgroup).
  8. Mitigez en sécurité (CPUQuota/Nice, ajouter du jitter, replanifier).
  9. Corrigez correctement (exclure des chemins, réduire la fréquence, supprimer les doublons).
  10. Vérifiez sur au moins 3 intervalles de pic avec sar ou votre monitoring.

Checklist B : Pics synchronisés à l’échelle de la flotte

  1. Confirmez la synchronisation sur plusieurs nœuds (même minute, même forme).
  2. Identifiez les timers communs activés dans l’image de base (systemctl list-timers sur plusieurs nœuds).
  3. Recherchez les agents fournisseurs déployés partout (inventory/EDR/sauvegarde).
  4. Appliquez du jitter universellement pour les tâches non urgentes.
  5. Fixez des budgets CPU pour le travail en arrière-plan (cgroups via overrides systemd).
  6. Échelonnez les tâches lourdes par rôle (ex. horaires différents pour web vs db).
  7. Re-vérifiez la latence et le taux d’erreur après les changements.

Checklist C : Quand le coupable est « travail stockage » et non « travail CPU »

  1. Vérifiez l’iowait et l’utilisation disque pendant les pics.
  2. Identifiez les jobs effectuant compression/hachage (sauvegarde, logrotate, scanners).
  3. Déplacez la compression lourde hors de l’hôte ou hors pic quand possible.
  4. Limitez la classe I/O des jobs d’arrière-plan (ionice ou paramètres de scheduling I/O systemd).
  5. Empêchez les parcours d’arborescence de scanner les répertoires de conteneurs et caches de build.

Faits intéressants et histoire utile en production

  • Fait 1 : Le design de cron remonte aux premiers Unix ; son comportement « exécuter à la minute exacte » explique pourquoi les flottes se précipitent encore à :00.
  • Fait 2 : Les timers systemd peuvent être persistants, ce qui signifie qu’ils exécuteront les jobs manqués après une panne—utile pour des laptops, épicé pour des serveurs.
  • Fait 3 : Beaucoup de distributions ont migré des tâches cron classiques (comme logrotate) vers des timers systemd, donc « nous avons vérifié cron » n’est plus suffisant depuis des années.
  • Fait 4 : La base de données updatedb / locate existe pour accélérer la recherche de noms de fichiers—utile sur des machines dev, souvent inutile en production.
  • Fait 5 : logrotate peut compresser les logs pivotés, et la compression est coûteuse en CPU ; un gros fichier log peut provoquer un pic plus important qu’une douzaine de petits.
  • Fait 6 : TRIM (fstrim) est planifié hebdomadairement sur beaucoup de systèmes Linux ; sur certains backends stockage il peut générer des rafales notables.
  • Fait 7 : Les pics périodiques sont plus faciles à diagnostiquer que la charge constante car vous pouvez les corréler avec des métadonnées du planificateur—si vous regardez vraiment.
  • Fait 8 : Le délai aléatoire (jitter) existe précisément pour éviter les thundering herds ; ne pas l’utiliser en flotte est une auto-défaillance évitable.
  • Fait 9 : Beaucoup d’« agents » exécutent leurs propres plannings internes et peuvent ne pas apparaître dans cron/timers du tout, donc vous devez observer leur comportement CPU directement.

FAQ

1) Quelle est la première chose à vérifier pour des pics CPU périodiques ?

Les timers systemd : systemctl list-timers --all. C’est le moyen le plus rapide d’attraper les tâches de maintenance OS et les timers fournisseurs oubliés.

2) J’ai vérifié cron et je n’ai rien trouvé. Et maintenant ?

Vérifiez les timers systemd, puis cherchez des daemons longue durée (agents) qui se réveillent périodiquement. Corrélez avec journalctl à l’heure du pic.

3) Pourquoi les pics arrivent-ils exactement toutes les 5 minutes ?

Parce que quelqu’un a mis */5 * * * * dans cron ou OnCalendar=*:0/5 dans un timer. Les ordinateurs sont littéraux. Les humains aiment les nombres ronds.

4) Dois‑je simplement désactiver le timer fautif ?

Parfois oui (par ex. updatedb sur un serveur de production). Parfois non (scans de sécurité, sauvegardes). Préférez : réduire la fréquence, ajouter du jitter, exclure les chemins lourds, et plafonner le CPU.

5) Comment prouver que c’est un timer la cause et non une simple corrélation ?

Faites correspondre le temps de la dernière exécution du timer (LAST) à l’horodatage du pic, puis vérifiez l’unité démarrée dans journalctl et confirmez que le PID chaud appartient à ce cgroup.

6) Le pic est surtout en %sys (CPU noyau). Qu’est-ce que cela suggère ?

Travail noyau lourd : churn métadonnées, overhead réseau, chiffrement, ou threads noyau faisant de la maintenance stockage. Cherchez des scans, sauvegardes, scrubs ou logs intensifs.

7) Pourquoi les pics ont empiré après un reboot ?

Les timers persistants peuvent « rattraper » après une panne. Plusieurs tâches manquées peuvent s’exécuter peu après le démarrage, superposant la charge CPU.

8) Comment arrêter des pics synchronisés sur toute la flotte ?

Ajoutez du jitter (RandomizedDelaySec) et évitez de tout programmer à :00. Pour les tâches lourdes, imposez des quotas CPU pour que le travail d’arrière-plan ne puisse pas priver votre service.

9) Le stockage peut-il provoquer des pics CPU même si les disques semblent OK ?

Oui. Les parcours d’annuaires, checksums, compression et chiffrement sont du travail CPU déclenché par des tâches liées au stockage. « Le disque est OK » ne signifie pas « il n’y a pas de travail stockage en cours ».

10) Et si le nom du processus est peu parlant, comme « python » ?

Récupérez la ligne de commande complète (ps -p PID -o cmd), vérifiez le parent et l’appartenance au cgroup. Si c’est une unité systemd, systemctl status UNIT révélera souvent le chemin du script.

Conclusion : prochaines étapes pratiques

Si vos pics CPU sont périodiques, agissez comme un SRE, pas comme un voyant. Traitez‑les comme du travail planifié tant que vous n’avez pas de preuve contraire.

  1. Sur un hôte : capturez le processus chaud pendant un pic avec pidstat, puis mappez‑le à une unité ou à une entrée cron.
  2. Sur le planificateur : listez d’abord les timers systemd, puis cron. Recherchez les motifs toutes les cinq minutes et en début d’heure.
  3. Atténuez en sécurité : limitez avec des drop-ins systemd (CPUQuota/Nice) et ajoutez du jitter pour arrêter les pics synchronisés de flotte.
  4. Corrigez la racine : excluez les chemins lourds, réduisez la fréquence, supprimez les planifications dupliquées et ne laissez pas les commodités dev tourner en production.
  5. Vérifiez : observez au moins quelques intervalles et confirmez que le pic a disparu—ou du moins qu’il n’impacte plus votre budget de latence.

Faites cela correctement et le graphe cesse de ressembler à un moniteur cardiaque. Ce qui est bien, parce que ça veut dire que vous pouvez arrêter de traiter votre infrastructure comme un patient en triage.

← Précédent
Pare-feu Linux : la disposition nftables claire et lisible à 500 règles
Suivant →
DNS : Split‑Horizon mal configuré — la solution qui met fin à la folie « intérieur/extérieur »

Laisser un commentaire