Guide des minuteries Systemd : remplacer les tâches Cron pour une planification fiable
Utilisez les minuteries systemd plutôt que cron lorsque vous avez besoin de journaux, de gestion des exécutions manquées, de dépendances et de contrôles des ressources.
Guide des minuteries Systemd : remplacer les tâches Cron pour une planification fiable
cron reste tout à fait adapté pour de nombreuses tâches. Si vous devez exécuter un script shell chaque nuit et que vous avez déjà une redirection de logs fonctionnelle, il n'y a aucun intérêt à le remplacer. La raison pour laquelle de nombreuses équipes migrent leurs tâches planifiées Linux vers les minuteries systemd n'est pas une question de mode. C'est parce que les minuteries systemd offrent à la tâche une véritable unité de service, des logs prévisibles, une gestion des dépendances, un comportement en cas d'exécution manquée et des limites de ressources.
Cela devient important lorsque la tâche ne se limite pas à « exécuter une commande ». Une tâche de sauvegarde peut nécessiter un disque monté. Un préchauffage de cache peut nécessiter que le réseau soit utilisable. Un exportateur de rapports peut devoir s'exécuter sous un compte de service restreint et laisser des logs lisibles pour la prochaine personne d'astreinte. Une minuterie systemd vous permet de décrire ces besoins au même endroit où vous gérez le reste du cycle de vie du service.
Comprendre les minuteries Systemd
Les minuteries systemd sont des fichiers d'unité systemd qui contrôlent quand d'autres unités systemd, généralement des unités service, sont activées. Contrairement à cron, qui est un démon autonome, les minuteries systemd font partie intégrante du système d'initialisation systemd. Cette intégration profonde apporte plusieurs avantages significatifs, notamment en matière de fiabilité, de journalisation et de gestion des ressources.
Une minuterie systemd fonctionne toujours en conjonction avec une autre unité, le plus souvent une unité service. Le fichier .timer définit quand un événement doit se produire, et le fichier .service correspondant définit quelle action doit être effectuée lorsque cet événement est déclenché. Cette séparation claire des préoccupations rend les minuteries systemd très modulaires et flexibles.
Principaux avantages des minuteries Systemd par rapport à Cron
Bien que cron soit fonctionnel, les minuteries systemd répondent à plusieurs de ses limitations, offrant une solution de planification plus robuste et riche en fonctionnalités :
- Fiabilité et persistance : Si une minuterie calendaire utilise
Persistent=trueet que le système est éteint pendant une exécution planifiée, systemd enregistre que l'exécution a été manquée et démarre le service associé après le prochain démarrage. Cron simple ne rattrape généralement pas ce retard sans un outil séparé comme anacron. - Intégration avec
systemd: Les minuteries bénéficient de la puissante journalisation desystemd(viajournalctl), de la gestion des dépendances et du contrôle des ressources (cgroups). Cela permet une meilleure surveillance, un signalement d'erreurs plus clair et la possibilité de définir des séquences de démarrage complexes ou des limites de ressources pour les tâches planifiées. - Reproductibilité et contrôle de version : Les fichiers d'unité
systemdsont des fichiers texte brut qui peuvent être facilement stockés dans des systèmes de contrôle de version. Cela permet des déploiements reproductibles et un suivi plus facile des modifications apportées aux tâches planifiées sur plusieurs systèmes. - Planification basée sur les événements : Au-delà de la simple planification temporelle, les minuteries
systemdpeuvent être déclenchées par rapport au démarrage du système (OnBootSec) ou après la dernière activation d'une unité (OnUnitActiveSec), offrant des options de planification plus dynamiques. - Expressions temporelles flexibles :
systemdoffre un ensemble riche d'expressions d'événements calendaires, souvent plus lisibles et polyvalentes que la syntaxe decron, incluant des expressions horaires, quotidiennes, hebdomadaires et des dates/heures spécifiques. - Gestion des ressources et dépendances : Les services
systemdlancés par les minuteries héritent de l'environnementsystemd, y compris les paramètres cgroup, et peuvent déclarer des dépendances envers d'autres unitéssystemd(par exemple, attendre que le réseau ou une base de données soit disponible avant de s'exécuter). - Gestion de la sortie standard/des erreurs :
systemdcapture automatiquementstdoutetstderrdes services lancés par les minuteries et les dirige vers le journal système, ce qui rend le débogage et l'audit beaucoup plus simples qu'avec la sortie par email decronou la redirection manuelle.
Configuration des minuteries Systemd
Configurer une minuterie systemd implique la création de deux fichiers d'unité : une unité de service (.service) et une unité de minuterie (.timer). Ces fichiers sont généralement placés dans /etc/systemd/system/ pour les minuteries à l'échelle du système ou dans ~/.config/systemd/user/ pour les minuteries spécifiques à un utilisateur.
1. L'unité de service (fichier .service)
L'unité de service définit la commande ou le script réel à exécuter. C'est un fichier de service systemd standard, mais souvent conçu pour être exécuté de manière non interactive et pour effectuer une tâche spécifique.
Exemple : /etc/systemd/system/mytask.service
[Unit]
Description=Mon service de tâche planifiée
[Service]
Type=oneshot
ExecStart=/usr/local/bin/mytask.sh
User=myuser
Group=mygroup
# Optionnel : Limiter les ressources sur les versions récentes de systemd
# CPUWeight=50
# MemoryMax=1G
[Install]
WantedBy=multi-user.target
Explication :
[Unit]: Contient des informations génériques sur l'unité.Description: Une description lisible par un humain.
[Service]: Définit la configuration spécifique au service.Type=oneshot: Indique que le service exécute une seule commande puis se termine. C'est courant pour les tâches planifiées.ExecStart: La commande ou le script à exécuter. Fournissez le chemin complet.User,Group: Définit l'utilisateur et le groupe sous lesquels la commande s'exécutera. Exécutez toujours les tâches avec les privilèges les plus bas nécessaires.CPUWeight,MemoryMax: Contrôles cgroup optionnels. Ils sont utiles lorsqu'une tâche planifiée ne doit pas affamer le reste de l'hôte.
[Install]: Définit comment l'unité doit être activée.WantedBy=multi-user.target: Bien que présent, cette section est souvent moins critique pour les services déclenchés par minuterie car l'unité de minuterie elle-même détermine généralement l'activation. Cependant, cela peut être utile si vous souhaitez également que le service soit activable manuellement ou pour l'intégrer à d'autres ciblessystemd.
2. L'unité de minuterie (fichier .timer)
L'unité de minuterie définit quand l'unité de service correspondante doit être activée. Elle doit avoir le même nom que son homologue de service (par exemple, mytask.timer pour mytask.service).
Exemple : /etc/systemd/system/mytask.timer
[Unit]
Description=Exécute mytask.service quotidiennement
[Timer]
OnCalendar=daily
Persistent=true
RandomizedDelaySec=600
AccuracySec=1min
[Install]
WantedBy=timers.target
Explication :
[Unit]: Informations génériques.Description: Une description pour la minuterie.
[Timer]: Définit la configuration spécifique à la minuterie.OnCalendar: Le paramètre le plus courant, définissant un événement calendaire. Il utilise des expressions comme :daily: Tous les jours à minuit.weekly: Tous les lundis à minuit.monthly: Le premier jour de chaque mois à minuit.hourly: Toutes les heures à la minute zéro.*-*-* 03:00:00: Tous les jours à 3h00.Mon..Fri 08:00..17:00: Jours de semaine entre 8h et 17h.Mon *-*-* 03:00:00: Tous les lundis à 3h.
OnBootSec: Active le service après un temps spécifié à partir du démarrage du système. Par exemple,OnBootSec=10min.OnUnitActiveSec: Active le service après un temps spécifié à partir de la dernière activation du service. Par exemple,OnUnitActiveSec=1hpour s'exécuter toutes les heures après la fin de l'exécution précédente.Persistent=true: Crucial pour la fiabilité. Si le système est éteint pendant une exécution planifiée, le service sera déclenché peu après le prochain démarrage.RandomizedDelaySec=600: Ajoute un délai aléatoire allant jusqu'à 600 secondes. Ceci est utile lorsque plusieurs machines partagent la même minuterie et que vous ne voulez pas que tous les hôtes frappent une base de données, une API ou un serveur de sauvegarde exactement à la même seconde.
Une migration réelle de Cron vers Timer
Supposons que vous ayez actuellement cette entrée cron root :
15 2 * * * /usr/local/sbin/backup-app.sh >> /var/log/backup-app.log 2>&1
Cela fonctionne sur une machine calme, mais cela présente les faiblesses habituelles. Si le disque de sauvegarde n'est pas monté, le script peut échouer en cours de route. Si le serveur est éteint à 2h15, l'exécution est sautée. Si le script écrit une erreur utile, quelqu'un doit se souvenir quel fichier de log personnalisé vérifier. Si le script commence à utiliser trop de mémoire, cron ne vous aidera pas à le contenir.
La version systemd sépare la commande du planning :
# /etc/systemd/system/backup-app.service
[Unit]
Description=Sauvegarder les données de l'application
RequiresMountsFor=/mnt/backups
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
User=backup
Group=backup
WorkingDirectory=/srv/app
ExecStart=/usr/local/sbin/backup-app.sh
MemoryMax=1G
CPUWeight=40
Nice=10
# /etc/systemd/system/backup-app.timer
[Unit]
Description=Exécuter la sauvegarde de l'application chaque nuit
[Timer]
OnCalendar=*-*-* 02:15:00
Persistent=true
RandomizedDelaySec=15min
AccuracySec=1min
Unit=backup-app.service
[Install]
WantedBy=timers.target
Il y a quelques détails à noter. RequiresMountsFor=/mnt/backups indique à systemd que le chemin doit être monté avant le démarrage du service. After=network-online.target et Wants=network-online.target ne sont utiles que si votre gestionnaire de réseau fournit effectivement un service d'attente en ligne ; sur de nombreuses distributions, ce service est désactivé par défaut. Si la sauvegarde écrit uniquement sur un disque local, laissez de côté la dépendance réseau.
Type=oneshot convient aux scripts qui effectuent leur travail puis se terminent. Ne l'utilisez pas pour un démon qui reste en cours d'exécution. WorkingDirectory= vous évite les scripts qui dépendent accidentellement du fait d'être lancés depuis un shell dans un répertoire particulier. User=backup est généralement préférable à l'exécution de la tâche en tant que root en espérant que chaque commande à l'intérieur du script soit prudente.
Après avoir enregistré les fichiers :
sudo systemctl daemon-reload
sudo systemctl enable --now backup-app.timer
systemctl list-timers backup-app.timer
Pour tester la tâche immédiatement, démarrez le service, pas la minuterie :
sudo systemctl start backup-app.service
journalctl -u backup-app.service -n 100 --no-pager
Cette distinction évite beaucoup de confusion. Démarrer backup-app.timer arme le planning. Démarrer backup-app.service exécute la sauvegarde réelle.
Choisir la bonne expression de minuterie
OnCalendar= est le remplacement le plus proche de la syntaxe cron, mais il se lit différemment. Vous pouvez vérifier ce que systemd pense qu'une expression signifie avant de la déployer :
systemd-analyze calendar 'Mon..Fri 03:30'
systemd-analyze calendar '*-*-01 04:00:00'
systemd-analyze calendar 'Sun *-*-* 23:00:00'
Utilisez les minuteries calendaires pour les travaux liés à l'heure réelle : sauvegardes nocturnes, rapports hebdomadaires, nettoyage mensuel, vérifications de certificats et autres tâches où le calendrier humain compte. Utilisez les minuteries monotones pour les comportements de type « exécuter après qu'un événement s'est produit » :
[Timer]
OnBootSec=10min
OnUnitActiveSec=1h
Ce modèle démarre le service dix minutes après le démarrage, puis à nouveau une heure après la dernière activation. C'est un bon choix pour le sondage, le nettoyage local et les boucles de maintenance légères. Ce n'est pas la même chose que « à la minute zéro de chaque heure ». Si la tâche prend douze minutes, la prochaine exécution est comptée à partir du moment de l'activation, et non de votre attente sur l'horloge murale.
Pensez également au chevauchement. Pour une unité de service normale, systemd ne démarrera pas une deuxième copie de la même unité active simplement parce que le prochain événement de minuterie est arrivé. Si votre tâche peut prendre plus de temps que son intervalle, décidez si cela est acceptable. Parfois, la bonne réponse est un verrou dans le script, comme flock, car il peut produire un message clair « l'exécution précédente est toujours active ». Parfois, la bonne réponse est d'allonger l'intervalle.
Habitudes opérationnelles qui font gagner du temps
La vue des minuteries est votre premier tableau de bord :
systemctl list-timers --all
Elle montre la dernière exécution, la prochaine exécution et l'unité que chaque minuterie active. Si la minuterie est listée mais que le service ne s'exécute jamais, vérifiez l'expression calendaire et si la minuterie est activée. Si le service s'exécute et échoue, ignorez la minuterie un instant et inspectez le service :
systemctl status backup-app.service
journalctl -u backup-app.service --since today
Lorsque vous modifiez l'un ou l'autre fichier d'unité, exécutez :
sudo systemctl daemon-reload
sudo systemctl restart backup-app.timer
Redémarrer la minuterie après des changements de planning est une bonne habitude car cela rafraîchit immédiatement l'heure de la prochaine activation. Si vous avez seulement modifié le script lui-même, vous n'avez généralement pas besoin de daemon-reload.
Pour les minuteries utilisateur, utilisez systemctl --user et placez les unités sous ~/.config/systemd/user/. Elles sont utiles pour les postes de travail des développeurs et l'automatisation par utilisateur, mais elles ont un inconvénient important : par défaut, les services utilisateur sont liés à la session de connexion de l'utilisateur. Si vous avez besoin qu'une minuterie utilisateur continue de s'exécuter après la déconnexion, activez la persistance avec loginctl enable-linger username. C'est un choix administratif délibéré, pas quelque chose à cacher dans l'article comme une solution magique.
Quand Cron est encore le meilleur outil
Ne migrez pas tout aveuglément. Cron est plus facile à lire pour les petites tâches locales à l'utilisateur, en particulier sur les serveurs plus anciens ou les conteneurs minimaux où systemd n'est pas PID 1. Si votre seule exigence est « exécuter cette commande inoffensive toutes les cinq minutes », cron est peut-être la réponse la plus claire.
Les minuteries Systemd sont rentables lorsque la tâche a des besoins de type service : identité contrôlée, logs dans le journal, limites de ressources, dépendances, comportement de rattrapage ou déploiement standard via des fichiers d'unité. Dans la pratique, j'utilise les minuteries lorsque la tâche planifiée réveillerait quelqu'un si elle échouait. Le fichier d'unité supplémentaire en vaut la peine lorsqu'il donne à l'opérateur suivant un chemin direct de « qu'est-ce qui a tourné ? » à « qu'est-ce qui a échoué ? » à « qu'est-ce qui a changé ? ».
Une dernière habitude à adopter lors des migrations : gardez l'ancienne entrée cron commentée à proximité seulement jusqu'à ce que la minuterie ait fonctionné avec succès quelques fois, puis supprimez-la. Les plannings en double sont une source silencieuse de dommages. Deux tâches de sauvegarde peuvent se disputer le même verrou, deux tâches de nettoyage peuvent supprimer des fichiers plus tôt que prévu, et deux tâches de rapport peuvent envoyer des emails en double. Après avoir activé la minuterie, vérifiez systemctl list-timers --all, confirmez le journal du service et assurez-vous que l'ancien chemin cron n'est plus actif.