Dans ce TP, nous explorerons deux approches fondamentales pour la gestion des tâches planifiées et des services sous Linux :
Ces deux outils répondent à des besoins similaires (exécution automatique de tâches) mais avec des philosophies et fonctionnalités différentes. Alors que cron est spécialisé uniquement dans la planification temporelle, systemd propose une approche intégrée avec le reste du système.
En tant qu'administrateur système, vous aurez besoin de maîtriser ces deux outils et de comprendre quand utiliser l'un plutôt que l'autre. Certaines distributions Linux privilégient désormais systemd pour la gestion complète du système, y compris les tâches planifiées, tandis que d'autres environnements continuent de s'appuyer sur la simplicité et l'universalité de cron.
À la fin de ce TP, vous serez capable de :
Cron est un service qui permet d'exécuter automatiquement des scripts ou des commandes à des moments précis, de façon récurrente. Les configurations sont stockées dans des fichiers appelés "crontabs".
/var/spool/cron/
/etc/cron.d/
, /etc/cron.daily/
, etc.Un crontab est composé de lignes définissant des tâches planifiées. Chaque ligne suit cette structure :
minute heure jour_du_mois mois jour_de_la_semaine commande
On trouve parfois également cette représentation :
┌───────────── minute (0 - 59)
│ ┌───────────── heure (0 - 23)
│ │ ┌───────────── jour du mois (1 - 31)
│ │ │ ┌───────────── mois (1 - 12)
│ │ │ │ ┌───────────── jour de la semaine (0 - 6) (dimanche = 0 ou 7)
│ │ │ │ │
│ │ │ │ │
* * * * * commande à exécuter
Champ | Valeurs possibles | Description |
---|---|---|
minute | 0-59 | Minute de l'heure |
heure | 0-23 | Heure de la journée (format 24h) |
jour_du_mois | 1-31 | Jour du mois |
mois | 1-12 ou jan-dec | Mois de l'année |
jour_de_la_semaine | 0-6 ou sun-sat | Jour de la semaine (0=dimanche) |
Caractère | Description | Exemple |
---|---|---|
* | Tous les éléments | * * * * * = toutes les minutes |
, | Liste de valeurs | 1,3,5 = 1, 3 et 5 |
- | Plage de valeurs | 1-5 = 1, 2, 3, 4 et 5 |
/ | Pas d'incrémentation | */5 = tous les 5 (0, 5, 10, 15...) |
# Exécuter tous les jours à 2h30
30 2 * * * /chemin/vers/script.sh
# Exécuter toutes les 10 minutes
*/10 * * * * commande
# Exécuter chaque lundi à 8h
0 8 * * 1 commande
# Exécuter le 1er et le 15 du mois à 23h
0 23 1,15 * * commande
# Exécuter du lundi au vendredi à 18h
0 18 * * 1-5 commande
# Exécuter une commande le week-end à 20h
0 20 * * 0,6 commande
# Exécuter une commande tous les quarts d'heure
*/15 * * * * commande
Il existe également des raccourcis intégrés au système :
@yearly (équivalent à "0 0 1 1 *")
@monthly (équivalent à "0 0 1 * *")
@weekly (équivalent à "0 0 * * 0")
@daily (équivalent à "0 0 * * *")
@hourly (équivalent à "0 * * * *")
@reboot (exécuté au démarrage du système)
# Éditer le crontab de l'utilisateur courant
crontab -e
# Lister les tâches programmées
crontab -l
# Supprimer toutes les tâches programmées
crontab -r
# Éditer le crontab d'un autre utilisateur (en tant que root)
crontab -u utilisateur -e
30 2 * * * /chemin/vers/script.sh > /dev/null 2>&1
30 2 * * * /chemin/vers/script.sh >> /var/log/mon_script.log 2>&1
Affichez votre crontab actuel :
crontab -l
Éditez votre crontab :
crontab -e
Créez une tâche qui écrit la date et l'heure actuelles dans un fichier toutes les 5 minutes :
*/5 * * * * date >> ~/date_log.txt
Vérifiez que la tâche s'exécute correctement :
tail -f ~/date_log.txt
Configurez les tâches suivantes :
Une tâche qui s'exécute tous les jours à 8h30 :
30 8 * * * echo "Tâche quotidienne à 8h30" >> ~/cron_test.log
Une tâche qui s'exécute du lundi au vendredi à 18h :
0 18 * * 1-5 echo "Fin de journée de travail" >> ~/cron_test.log
Une tâche qui s'exécute le premier jour de chaque mois à minuit :
0 0 1 * * echo "Nouveau mois !" >> ~/cron_test.log
Créez un script de monitoring simple :
nano ~/system_stats.sh
Contenu du script :
#!/bin/bash
LOGFILE=~/system_stats.log
echo "========== $(date) ==========" >> $LOGFILE
echo "CPU Load:" >> $LOGFILE
uptime >> $LOGFILE
echo "Memory Usage:" >> $LOGFILE
free -h >> $LOGFILE
echo "Disk Usage:" >> $LOGFILE
df -h / >> $LOGFILE
echo "" >> $LOGFILE
Rendez le script exécutable :
chmod +x ~/system_stats.sh
Configurez cron pour exécuter ce script toutes les heures (ou toutes le minutes pour aller plus vite) :
0 * * * * ~/system_stats.sh
Créez un script pour la rotation des logs :
nano ~/rotate_logs.sh
Contenu du script :
#!/bin/bash
# Rotation des logs
if [ -f ~/system_stats.log ]; then
mv ~/system_stats.log ~/system_stats.log.old
fi
# Créer un nouveau fichier de log vide
touch ~/system_stats.log
echo "Log rotation completed at $(date)" >> ~/rotate_logs.log
Rendez le script exécutable :
chmod +x ~/rotate_logs.sh
Configurez cron pour effectuer la rotation des logs tous les dimanches à minuit :
0 0 * * 0 ~/rotate_logs.sh
Systemd est le système d'initialisation par défaut dans la plupart des distributions Linux modernes. Il gère le démarrage et l'arrêt des services, ainsi que leur supervision.
/lib/systemd/system/
: Unités fournies par les paquets/etc/systemd/system/
: Unités personnalisées et modifications/run/systemd/system/
: Unités temporairesListez tous les services systemd :
systemctl list-units --type=service
Vérifiez l'état d'un service spécifique (exemple avec SSH) :
systemctl status sshd
Visualisez la configuration du service ssh :
less /lib/systemd/system/ssh.service
Démarrez, arrêtez et redémarrez un service :
sudo systemctl start sshd
sudo systemctl stop sshd
sudo systemctl restart sshd
Activez et désactivez le démarrage automatique d'un service :
sudo systemctl enable sshd
sudo systemctl disable sshd
Créez un script simple pour notre service :
nano ~/webserver.py
Contenu du script (serveur web minimal) :
#!/usr/bin/env python3
from http.server import BaseHTTPRequestHandler, HTTPServer
class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
message = f"<html><head><meta charset=\"utf-8\"></head><body><h1>Hello from {self.path}</h1><p>Service géré par systemd</p></body></html>"
self.wfile.write(bytes(message, "utf8"))
def run():
server_address = ('', 8000)
httpd = HTTPServer(server_address, SimpleHTTPRequestHandler)
print('Serveur démarré sur le port 8000...')
httpd.serve_forever()
if __name__ == '__main__':
run()
Rendez le script exécutable :
chmod +x ~/webserver.py
Créez un fichier service systemd :
sudo nano /etc/systemd/system/webserver.service
Contenu du fichier service :
[Unit]
Description=Simple Python Web Server
After=network.target
[Service]
Type=simple
User=optimusprime
WorkingDirectory=/home/optimusprime
ExecStart=/home/optimusprime/webserver.py
Restart=on-failure
[Install]
WantedBy=multi-user.target
Remplacez bien entendu optimusprime par votre nom d'utilisateur. tip : avec l'éditeur nano, le remplacement se fait avec ctrl+W suivi de ctrl+R
Rechargez la configuration de systemd :
sudo systemctl daemon-reload
Démarrez et activez le service :
sudo systemctl start webserver
sudo systemctl enable webserver
Vérifiez que le service fonctionne :
systemctl status webserver
curl http://localhost:8000
ou encore depuis le navigateur web (http://192.168.99.101:8000/
) :
Les timers systemd sont une alternative moderne à cron. Ils offrent plusieurs avantages :
Ils sont définis par deux composants :
.service
qui définit l'action à exécuter.timer
qui définit quand l'action doit être exécutéeIl existe deux types principaux de timers:
Timer basé sur le temps réel (wall clock) :
OnCalendar
Timer basé sur les événements/monotonic :
OnBootSec
, OnUnitActiveSec
, etc.Créez un script pour la tâche de démarrage :
nano ~/startup_task.sh
Contenu du script :
#!/bin/bash
echo "Système démarré à $(date)" >> ~/startup_log.txt
echo "Services actifs: $(systemctl --type=service | grep running | wc -l)" >> ~/startup_log.txt
echo "Uptime: $(uptime)" >> ~/startup_log.txt
echo "----------------------------------------" >> ~/startup_log.txt
Rendez le script exécutable :
chmod +x ~/startup_task.sh
Créez un fichier service pour cette tâche :
sudo nano /etc/systemd/system/startup-task.service
Contenu du fichier :
[Unit]
Description=Tâche post-démarrage
After=network.target
[Service]
Type=oneshot
User=optimusprime
ExecStart=/home/optimusprime/startup_task.sh
[Install]
WantedBy=multi-user.target
Créez un timer qui se déclenche uniquement au démarrage :
sudo nano /etc/systemd/system/startup-task.timer
Contenu du timer :
[Unit]
Description=Exécuter la tâche post-démarrage
[Timer]
OnBootSec=2min
RemainAfterElapse=no
[Install]
WantedBy=timers.target
Note: OnBootSec=2min
signifie "2 minutes après le démarrage"
Note: RemainAfterElapse=no
signifie que le timer s'arrête après l'exécution
Activez et démarrez le timer :
sudo systemctl daemon-reload
sudo systemctl enable startup-task.timer
sudo systemctl start startup-task.timer
Pour simuler un redémarrage et tester le timer (sans redémarrer) :
sudo systemctl restart startup-task.timer
Créez un script pour la tâche périodique :
nano ~/periodic_task.sh
Contenu du script :
#!/bin/bash
LOG_FILE=~/periodic_task.log
echo "=== Exécution à $(date) ===" >> $LOG_FILE
echo "Utilisateurs connectés:" >> $LOG_FILE
who >> $LOG_FILE
echo "Top 5 processus par utilisation CPU:" >> $LOG_FILE
ps aux --sort=-%cpu | head -6 >> $LOG_FILE
echo "" >> $LOG_FILE
Rendez le script exécutable :
chmod +x ~/periodic_task.sh
Créez un fichier service pour cette tâche :
sudo nano /etc/systemd/system/periodic-task.service
Contenu du fichier :
[Unit]
Description=Tâche périodique de monitoring
[Service]
Type=oneshot
User=optimusprime
ExecStart=/home/optimusprime/periodic_task.sh
[Install]
WantedBy=multi-user.target
Créez un timer avec plusieurs options de récurrence :
sudo nano /etc/systemd/system/periodic-task.timer
Contenu du timer :
[Unit]
Description=Exécuter la tâche périodique selon plusieurs configurations
[Timer]
# Exécute après 30 secondes après démarrage du timer
OnActiveSec=30s
# Puis toutes les 15 minutes
OnUnitActiveSec=15min
# Précision de 1 seconde (par défaut c'est 1 minute)
AccuracySec=1s
# Persistance: si le système était éteint lors d'une exécution planifiée,
# exécuter la tâche au prochain démarrage
Persistent=true
[Install]
WantedBy=timers.target
Activez et démarrez le timer :
sudo systemctl daemon-reload
sudo systemctl enable periodic-task.timer
sudo systemctl start periodic-task.timer
Vérifiez l'état du timer :
systemctl list-timers
Créez un script pour la tâche planifiée :
nano ~/calendar_task.sh
Contenu du script :
#!/bin/bash
echo "Tâche exécutée selon calendrier à $(date)" >> ~/calendar_task.log
Rendez le script exécutable :
chmod +x ~/calendar_task.sh
Créez un fichier service :
sudo nano /etc/systemd/system/calendar-task.service
Contenu du fichier :
[Unit]
Description=Tâche programmée par calendrier
[Service]
Type=oneshot
User=optimusprime
ExecStart=/home/optimusprime/calendar_task.sh
[Install]
WantedBy=multi-user.target
Créez un timer avec la syntaxe de calendrier :
sudo nano /etc/systemd/system/calendar-task.timer
Contenu du timer :
[Unit]
Description=Exécuter la tâche selon calendrier
[Timer]
# Format: DOW YYYY-MM-DD HH:MM:SS
# Tous les jours de la semaine à 17h30
OnCalendar=*-*-* 17:30:00
# Fenêtre de tolérance pour l'exécution
RandomizedDelaySec=30s
# Exécuter immédiatement si manqué
Persistent=true
[Install]
WantedBy=timers.target
Activez et démarrez le timer :
sudo systemctl daemon-reload
sudo systemctl enable calendar-task.timer
sudo systemctl start calendar-task.timer
La syntaxe de base est : DayOfWeek Year-Month-Day Hour:Minute:Second
Exemples de syntaxe OnCalendar:
*-*-* 4:00:00
: Tous les jours à 4h du matinMon *-*-* 8:00:00
: Tous les lundis à 8h du matinMon..Fri *-*-* 17:00:00
: Du lundi au vendredi à 17h*-*-01 12:00:00
: Le premier jour de chaque mois à midi2023-12-24 18:00:00
: Le 24 décembre 2023 à 18h*-*-* *:00/15:00
: Toutes les 15 minutesPour vérifier l'interprétation d'une expression de calendrier :
systemd-analyze calendar "Mon..Fri *-*-* 9:00:00"
Liste des timers actifs:
systemctl list-timers
Liste de tous les timers (même inactifs):
systemctl list-timers --all
Lancer une tâche manuellement (sans attendre le timer):
sudo systemctl start periodic-task.service
Voir les logs d'une tâche déclenchée par le timer
sudo journalctl | grep periodic-task.service
Consultez les journaux d'un service :
journalctl -u webserver
Suivez les journaux en temps réel :
journalctl -u webserver -f
Analysez les echecs de démarrage :
systemctl status webserver --full
Vérifiez la configuration :
systemd-analyze verify /etc/systemd/system/webserver.service
Pour ce projet, vous allez créer :
Créez un script de collecte :
nano ~/collect_stats.sh
Contenu du script :
#!/bin/bash
# Dossier de destination
STATS_DIR=~/system_stats
# S'assurer que le dossier existe
mkdir -p $STATS_DIR
# Fichier de données JSON
STATS_FILE=$STATS_DIR/stats.json
# Collecter les données
CPU_LOAD=$(uptime | awk -F'[a-z]:' '{ print $2}' | tr -d ' ')
MEM_TOTAL=$(free -m | grep Mem | awk '{print $2}')
MEM_USED=$(free -m | grep Mem | awk '{print $3}')
DISK_TOTAL=$(df -h / | tail -1 | awk '{print $2}')
DISK_USED=$(df -h / | tail -1 | awk '{print $3}')
DISK_PERCENT=$(df -h / | tail -1 | awk '{print $5}')
TIMESTAMP=$(date +%s)
# Créer le JSON
cat > $STATS_FILE << EOL
{
"timestamp": $TIMESTAMP,
"cpu_load": "$CPU_LOAD",
"memory": {
"total": $MEM_TOTAL,
"used": $MEM_USED
},
"disk": {
"total": "$DISK_TOTAL",
"used": "$DISK_USED",
"usage_percent": "$DISK_PERCENT"
}
}
EOL
echo "Statistiques mises à jour : $(date)"
Rendez le script exécutable :
chmod +x ~/collect_stats.sh
Configurez cron pour exécuter ce script toutes les 5 minutes :
*/5 * * * * ~/collect_stats.sh
Créez un script pour le serveur web :
nano ~/stats_server.py
Contenu du script :
#!/usr/bin/env python3
import http.server
import socketserver
import json
import os
import datetime
PORT = 8080
class StatsHandler(http.server.SimpleHTTPRequestHandler):
def do_GET(self):
if self.path == '/':
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
# Lecture du fichier de statistiques
stats_file = os.path.expanduser('~/system_stats/stats.json')
try:
with open(stats_file, 'r') as f:
stats = json.load(f)
timestamp = datetime.datetime.fromtimestamp(stats['timestamp'])
mem_percent = round((stats['memory']['used'] / stats['memory']['total']) * 100, 2)
html = f"""
<!DOCTYPE html>
<html>
<head>
<title>Statistiques Système</title>
<meta http-equiv="refresh" content="60">
<meta charset="utf-8">
<style>
body {{ font-family: Arial, sans-serif; margin: 0; padding: 20px; }}
.card {{ background-color: #f5f5f5; border-radius: 5px; padding: 15px; margin-bottom: 15px; }}
.title {{ font-weight: bold; margin-bottom: 10px; }}
.bar-container {{ background-color: #e0e0e0; border-radius: 3px; height: 20px; }}
.bar {{ background-color: #4CAF50; height: 20px; border-radius: 3px; }}
.warning {{ background-color: #ff9800; }}
.danger {{ background-color: #f44336; }}
</style>
</head>
<body>
<h1>Statistiques du Système</h1>
<p>Dernière mise à jour : {timestamp}</p>
<div class="card">
<div class="title">Charge CPU</div>
<p>{stats['cpu_load']}</p>
</div>
<div class="card">
<div class="title">Utilisation Mémoire</div>
<p>{stats['memory']['used']} MB / {stats['memory']['total']} MB ({mem_percent}%)</p>
<div class="bar-container">
<div class="bar{' warning' if mem_percent > 70 else ' danger' if mem_percent > 85 else ''}" style="width: {mem_percent}%"></div>
</div>
</div>
<div class="card">
<div class="title">Utilisation Disque</div>
<p>{stats['disk']['used']} / {stats['disk']['total']} ({stats['disk']['usage_percent']})</p>
<div class="bar-container">
<div class="bar{' warning' if int(stats['disk']['usage_percent'][:-1]) > 70 else ' danger' if int(stats['disk']['usage_percent'][:-1]) > 85 else ''}" style="width: {stats['disk']['usage_percent']}"></div>
</div>
</div>
</body>
</html>
"""
self.wfile.write(html.encode())
except Exception as e:
self.wfile.write(f"<html><body><h1>Erreur</h1><p>{str(e)}</p></body></html>".encode())
else:
self.send_error(404)
if __name__ == "__main__":
with socketserver.TCPServer(("", PORT),StatsHandler) as httpd:
print(f"Serveur démarré sur le port {PORT}")
httpd.serve_forever()
Rendez le script exécutable :
chmod +x ~/stats_server.py
Créez un fichier service systemd :
sudo nano /etc/systemd/system/stats-server.service
Contenu du fichier service :
[Unit]
Description=Serveur de Statistiques Système
After=network.target
[Service]
Type=simple
User=optimusprime
WorkingDirectory=/home/optimusprime
ExecStart=/home/optimusprime/stats_server.py
Restart=on-failure
[Install]
WantedBy=multi-user.target
Activez et démarrez le service :
sudo systemctl daemon-reload
sudo systemctl enable stats-server
sudo systemctl start stats-server
Vérifiez que le service fonctionne :
systemctl status stats-server
curl http://localhost:8080
et depuis un navigateur web :
À la fin de ce TP, vous avez pu constater que cron et systemd timers peuvent souvent accomplir les mêmes tâches, mais avec des approches différentes. Voici une synthèse pour vous aider à choisir l'outil le plus adapté à chaque situation :
Bien que systemd soit devenu le système d'initialisation par défaut dans la plupart des distributions Linux majeures (Debian, Ubuntu, Fedora, CentOS, RHEL, etc.), plusieurs distributions ont fait le choix délibéré de ne pas l'adopter. Comprendre ces alternatives enrichit votre perspective en tant qu'administrateur système.
Devuan est l'exemple le plus connu de distribution créée spécifiquement en réaction à l'adoption de systemd par Debian. Lancée en 2014, Devuan est un fork de Debian qui remplace systemd par des alternatives comme SysVinit ou OpenRC. Les raisons de cette résistance à systemd sont multiples :
Philosophie UNIX : Les critiques de systemd soutiennent qu'il viole le principe UNIX de "faire une chose et la faire bien". Systemd intègre de nombreuses fonctionnalités au-delà de l'initialisation du système (gestion des journaux, des services, du réseau, etc.).
Complexité : Systemd est considéré comme trop complexe et difficile à déboguer par rapport aux alternatives plus simples. Sa base de code est volumineuse et en constante évolution.
Dépendances : Systemd crée des dépendances fortes au sein du système, liant ensemble des composants qui étaient traditionnellement indépendants.
Portabilité : Systemd est spécifique à Linux et n'est pas compatible avec d'autres systèmes d'exploitation de type UNIX comme BSD.
Contrôle et transparence : Les processus de démarrage et d'arrêt sont moins transparents et plus difficiles à contrôler manuellement qu'avec des systèmes d'init traditionnels.
Outre Devuan, d'autres distributions notables évitent systemd :
Pour l'administrateur système, ces différences ont des implications concrètes :
/etc/init.d/
)service
ou /etc/init.d/[service] start
au lieu de systemctl start
Le débat systemd vs alternatives traditionnelles reste vif dans la communauté Linux. En tant que professionnel, il est important :
Dans les environnements modernes de conteneurisation et d'orchestration (Docker, Kubernetes), les deux approches ont tendance à être remplacées par des planificateurs intégrés à ces plateformes. Cependant, pour les serveurs traditionnels et les systèmes d'exploitation standards, la tendance est à l'adoption progressive de systemd timers dans les distributions qui utilisent systemd comme système d'init.
De nombreuses organisations maintiennent encore des configurations hybrides, utilisant cron pour les tâches simples et historiques, et systemd pour les nouveaux services et tâches qui bénéficient de son intégration plus profonde avec le système.