← Contextes /
cron-vs-daemon-systemd.md 219 lignes · 6.1 KB
Personnaliser Télécharger
# Cron vs daemon systemd — templates et arbre de décision

Contexte CLAUDE.md pour choisir entre cron, systemd timer et daemon Node.js.
Contient les templates de configuration commentés et l'arbre de décision.

## Section 1 : Arbre de décision

Trois questions pour choisir :

**1. La tâche doit-elle réagir à un événement en moins d'une minute ?**
- Oui → **daemon** (cron et timer ne peuvent pas faire mieux qu'1 min)
- Non → question suivante

**2. A-t-elle besoin d'état entre les runs ?**
- Oui → **daemon** (process continu avec état en mémoire)
- Non → question suivante

**3. Est-ce que les logs structurés ou la supervision crash comptent ?**
- Oui → **systemd timer** (journald natif, restart, dépendances, sans event loop à écrire)
- Non → **cron** (3 lignes de config, rien d'autre)

Résumé :
- Cron : tâche atomique périodique, simple, pas de logs critiques
- systemd timer : tâche périodique + logs journald + supervision
- Daemon : polling sub-minute, état en mémoire, réaction on-demand, file de jobs

## Section 2 : Template systemd .service (daemon Node.js)

```ini
# ~/.config/systemd/user/mon-daemon.service
[Unit]
Description=Mon daemon Node.js
After=network.target

[Service]
Type=simple
# Répertoire de travail — tous les chemins relatifs dans le script partent d'ici
WorkingDirectory=/home/USER/work/mon-projet

# Commande de démarrage
ExecStart=/usr/bin/node scripts/mon-daemon.js

# Redémarrage automatique en cas de crash (pas sur arrêt volontaire)
Restart=on-failure
RestartSec=10

# Variables d'environnement (alternative : EnvironmentFile=.env)
Environment=NODE_ENV=production

# Logs dans journald automatiquement — pas besoin de redirection
StandardOutput=journal
StandardError=journal

[Install]
# Démarre avec la session utilisateur
WantedBy=default.target
```

Commandes utiles :
```bash
# Recharger la config systemd après modification du .service
systemctl --user daemon-reload

# Activer au démarrage
systemctl --user enable mon-daemon

# Démarrer maintenant
systemctl --user start mon-daemon

# Voir les logs en temps réel
journalctl --user -u mon-daemon -f

# Statut + dernières lignes de log
systemctl --user status mon-daemon
```

Pour que les services utilisateur démarrent sans session ouverte (server headless) :
```bash
loginctl enable-linger $USER
```

## Section 3 : Template systemd timer (.service + .timer)

```ini
# ~/.config/systemd/user/mon-job.service
[Unit]
Description=Mon job périodique
After=network.target

[Service]
# oneshot = s'exécute et s'arrête (contrairement à simple qui tourne en continu)
Type=oneshot
WorkingDirectory=/home/USER/work/mon-projet
ExecStart=/usr/bin/node scripts/mon-job.js
```

```ini
# ~/.config/systemd/user/mon-job.timer
[Unit]
Description=Déclenche mon-job.service périodiquement

[Timer]
# Délai après le démarrage du système (évite les déclenchements trop précoces au boot)
OnBootSec=5min

# Intervalle entre deux exécutions (à partir de la fin de la dernière)
OnUnitActiveSec=6h

# Alternatives :
# OnCalendar=daily          → tous les jours à minuit
# OnCalendar=Mon *-*-* 9:00 → tous les lundis à 9h
# OnCalendar=*:0/15         → toutes les 15 minutes

# Rattraper les exécutions manquées (ex: machine éteinte pendant la fenêtre)
Persistent=true

[Install]
WantedBy=timers.target
```

Commandes utiles :
```bash
systemctl --user daemon-reload
systemctl --user enable --now mon-job.timer

# Voir tous les timers actifs et leur prochaine exécution
systemctl --user list-timers

# Forcer une exécution immédiate (sans attendre le timer)
systemctl --user start mon-job.service
```

## Section 4 : Template crontab

```bash
# Éditer avec : crontab -e
# Vérifier avec : crontab -l

# Syntaxe : minute heure jour-du-mois mois jour-de-la-semaine commande
# *       = tous
# */n     = tous les n
# 1,3,5   = valeurs spécifiques

# Publier sur dev.to à 9h tous les jours
0 9 * * * bash /home/USER/work/cv/scripts/devto-cron.sh >> /home/USER/work/cv/logs/devto-cron.log 2>&1

# Publier sur LinkedIn à 10h tous les jours
0 10 * * * /usr/bin/node /home/USER/work/cv/scripts/linkedin-cron.js >> /home/USER/work/cv/logs/linkedin-cron.log 2>&1

# Toutes les 15 minutes (avec flock pour éviter l'overlap)
*/15 * * * * flock -n /tmp/mon-job.lock /usr/bin/node /home/USER/scripts/mon-job.js >> /tmp/mon-job.log 2>&1

# Variables d'environnement disponibles dans crontab
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

# Charger un fichier .env avant le script (bash requis)
0 9 * * * bash -c 'set -a; source /home/USER/work/cv/scripts/.mon-env; set +a; node /home/USER/work/cv/scripts/mon-script.js' >> /home/USER/logs/mon-script.log 2>&1
```

Notes importantes :
- Le cron tourne dans un environnement minimal — pas de PATH, PS1, ou variables de session
- Toujours utiliser des chemins absolus (`/usr/bin/node`, pas `node`)
- `2>&1` redirige stderr vers stdout (dans le même fichier log)
- `flock -n` échoue silencieusement si le lock est déjà pris (évite l'overlap)

## Section 5 : Commandes de debug

```bash
# --- DAEMON systemd ---

# Logs en temps réel
journalctl --user -u mon-daemon -f

# Logs des dernières 100 lignes
journalctl --user -u mon-daemon -n 100

# Logs depuis une date
journalctl --user -u mon-daemon --since "2026-03-25 10:00"

# Statut complet (state, PID, dernières lignes)
systemctl --user status mon-daemon

# Redémarrer
systemctl --user restart mon-daemon

# --- TIMER systemd ---

# Voir tous les timers et leur prochaine exécution
systemctl --user list-timers

# Forcer exécution immédiate sans attendre le timer
systemctl --user start mon-job.service

# Logs du dernier run
journalctl --user -u mon-job.service -n 50

# --- CRON ---

# Voir les jobs en cours d'exécution
crontab -l

# Logs du cron système (selon la distro)
journalctl -u cron -f
# ou
tail -f /var/log/syslog | grep CRON

# Tester un script cron dans les mêmes conditions (env minimal)
env -i HOME=$HOME /bin/bash --noprofile --norc -c 'source /home/USER/.bashrc; mon-script.sh'

# --- OVERLAP (flock) ---

# Vérifier si un lock est actif
flock -n /tmp/mon-job.lock echo "libre" || echo "déjà en cours"

# Voir quel process tient le lock
lsof /tmp/mon-job.lock
```