CérénIT - crontabLe blog tech de Nicolas Steinmetz (Time Series, IoT, Web, Ops, Data)Zola2018-01-29T14:15:37+01:00https://cerenit.fr/tags/crontab/atom.xmlSystemd timer pour les Cro(o)ners2018-01-29T14:15:37+01:002018-01-29T14:15:37+01:00
Unknown
https://cerenit.fr/blog/systemd-timer-pour-les-crooners/<h3 id="cron-et-crontab-sont-dans-un-bateau">Cron et Crontab sont dans un bateau...</h3>
<p>Il fut un temps où pour programmer le lancement des actions, notre meilleur ami était "<a rel="noopener" target="_blank" href="https://fr.wikipedia.org/wiki/Cron">cron</a>" et la "<a rel="noopener" target="_blank" href="https://fr.wikipedia.org/wiki/Cron#crontab">crontab</a>". Outre la syntaxe et l'organisation de la crontab peu mémorisable, les tâches cron restaient complexes à déployer et à monitorer dans leur exécution. Si le fait d'avoir des répertoires plus génériques comme <code>/etc/cron.{d,hourly,daily,weekly,monthly}</code> ont un peu amélioré la partie exploitabilité/déployabilité, ils offraient assez peu de souplesses pour une plannification fine des tâches. De nombreux serveurs ont donc eu un mix de crontab classique et de ce format pendant des années.</p>
<p>Debian 9.0 (mais d'autres aussi comme Archlinux par ex) a fait le choix dans le cadre de son adoption de systemd de ne plus proposer un logiciel de type cron mais de s'appuyer sur l'implémentation interne à systemd, à savoir les "<a rel="noopener" target="_blank" href="https://www.freedesktop.org/software/systemd/man/systemd.timer.html">timers</a>". C'est une unité (<a rel="noopener" target="_blank" href="https://www.freedesktop.org/software/systemd/man/systemd.unit.html">unit</a>) spéciale de systemd qui est controllée et supervisée par systemd et qui s'exécute selon une configuration temporelle donnée.</p>
<p>Voyons comment passer de cette crontab :</p>
<pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>0 4 * * * /path/to/scripts/backup.sh >> /var/log/backup.log 2>&1
</span></code></pre>
<p>... ou de <code>/etc/cron.daily/mybackup.sh</code> appelant le même script à son équivalent sous un timer systemd.</p>
<h3 id="implementation-d-un-timer-systemd">Implémentation d'un timer systemd.</h3>
<p>Si vous gérez déjà vos services via systemd, vous avez déjà utilisé des "unit" systemd de type "<a rel="noopener" target="_blank" href="https://www.freedesktop.org/software/systemd/man/systemd.service.html">service</a>". Ces "unit" permettent de définir un process et son mode d'éxécution.</p>
<p>Pour implémenter un "timer" sous systemd, il va nous falloir un fichier "service".</p>
<p>Pour notre tâche à planifier, nous allons avoir au final 3 fichiers :</p>
<ul>
<li>Le script à exécuter ; c'est votre script <code>/path/to/scripts/backup.sh</code> ; il est inchangé par rapport à précédemment.</li>
<li>Le fichier "service" qui va dire quel script exécuter</li>
<li>Le fichier "timer" qui va indiquer quand il doit être exécuté.</li>
</ul>
<p>A noter que par convention, les fichiers service et timer doivent avoir le même nom (<code>foo.service</code>, <code>foo.timer</code>).</p>
<p>Pour le fichier service, une base simple est la suivante :</p>
<p><code>/etc/systemd/system/mybackup.service</code> :</p>
<pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>[Unit]
</span><span>Description=My Daily Backup
</span><span>
</span><span>[Service]
</span><span>Type=simple
</span><span>ExecStart=/path/to/scripts/backup.sh
</span><span>StandardError=journal
</span></code></pre>
<p>Je fournis une description à mon service, indique que c'est un process de type simple, le chemin vers mon script et je rajoute que le flux d'erreur est envoyé dans le journal. Pour plus d'information, se reporter à la doc des <a rel="noopener" target="_blank" href="https://www.freedesktop.org/software/systemd/man/systemd.service.html">services</a> et des <a rel="noopener" target="_blank" href="https://www.freedesktop.org/software/systemd/man/systemd.exec.html">exécutions</a>.</p>
<p>Attention néanmoins, il ne faut pas de section <code>[Install]</code> car le script va être piloté par le fichier timer.</p>
<p>Ensuite, il nous faut un fichier "timer" :</p>
<p><code>/etc/systemd/system/mybackup.timer</code> :</p>
<pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>[Unit]
</span><span>Description=My Daily Backup
</span><span>
</span><span>[Timer]
</span><span># Define a calendar event (see `man systemd.time`)
</span><span>OnCalendar=daily
</span><span>Persistent=true
</span><span>
</span><span>[Install]
</span><span>WantedBy=timers.target
</span></code></pre>
<p>Au-delà de la description, les lignes importantes sont la section <code>[Timer]</code> et <code>[Install]</code> :</p>
<ul>
<li><code>OnCalendar</code> permet d'indiquer l'occurrence et la fréquence d'exécution du script. Il y a les abréviations classiques (daily, weekly, etc) mais vous pouvez avoir des choses plus complexes comme <code>"Mon,Tue *-*-01..04 12:00:00"</code> - voir <a rel="noopener" target="_blank" href="https://www.freedesktop.org/software/systemd/man/systemd.time.html">systemd.time</a></li>
<li><code>Persistent</code> va forcer l'exécution du script si la dernière exécution a été manquée suite à un reboot de serveur ou autre événement.</li>
<li>Enfin, la section Install va créer la dépendance pour que votre "timer" soit bien exécuté et pris en compte par systemd.</li>
</ul>
<p>Il ne reste plus qu'à activer le timer et le démarrer :</p>
<pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>systemctl enable mybackup.timer
</span><span>systemctl start mybackup.timer
</span></code></pre>
<h3 id="gestion-et-suivi-d-un-timer">Gestion et suivi d'un timer</h3>
<p>Pour voir la liste des "timers" actifs sur votre serveur, la date de leur dernière et prochaine exécution :</p>
<pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>systemctl list-timers
</span><span>NEXT LEFT LAST PASSED UNIT ACTIVATES
</span><span>Mon 2018-01-29 17:25:59 CET 2h 27min left Sun 2018-01-28 22:20:35 CET 16h ago apt-daily.timer apt-daily.service
</span><span>Tue 2018-01-30 06:36:33 CET 15h left Mon 2018-01-29 06:37:35 CET 8h ago apt-daily-upgrade.timer apt-daily-upgrade.service
</span><span>Tue 2018-01-30 09:25:48 CET 18h left Mon 2018-01-29 09:25:48 CET 5h 33min ago systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service
</span></code></pre>
<p>et accéder aux logs de vos "timers" :</p>
<pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>journalctl -f -u apt-daily
</span><span>-- Logs begin at Tue 2018-01-23 13:50:12 CET. --
</span><span>Jan 28 22:20:35 codb2d9 systemd[1]: Starting Daily apt download activities...
</span><span>Jan 28 22:20:35 codb2d9 systemd[1]: Started Daily apt download activities.
</span></code></pre>
<p>En cas de modification du <code>.timer</code> ou du <code>.service</code>, ne pas oublier de faire un <code>system daemon-reload</code> pour que la version actualisée de vos fichiers soit prise en compte par systemd.</p>
<p>Passé la petite prise en main de ce nouveau cadre d'exécution, elle me semble plus simple et surtout plus facile à intégrer dans des chaines de déploiements pilotées par Ansible ou autre.</p>