Remplacer les tâches Cron par les timers SystemD
Orchestrer ses jobs avec Cron a ses limites : son environnement d'exécution n'est pas toujours bien configuré, la granularité du temps s'arrête à la minute etc... Regardons comment remplacer un job Cron par un timer SystemD en le déployant avec Ansible !
Intérêt
Les timers SystemD et les jobs Cron sont deux mécanismes différents pour planifier l'exécution de tâches sur un système Linux.
Les avantages d'utiliser un timer SystemD sont :
- Leur intégration à SystemD ce qui centralise sa gestion.
- Ils offrent une granularité de temps plus fine que les jobs Cron, avec une précision pouvant aller jusqu'à la seconde.
- Ils permettent de spécifier des conditions d'activation, telles que l'arrêt d'un autre service, la présence d'un fichier ou la connexion à un réseau.
- De disposer de ses propres journaux de logs par timer avec journalctl.
Comme les timers SystemD peuvent être amenés à être déployés en masse, regardons comment déployer un timer SystemD avec Ansible.
Le rôle Ansible
Notre rôle Ansible est très court et repose principalement sur deux templates Jinja2.
systemctl daemon-reload
systemctl enable --now backup.timer
Voici à quoi ressemble l'arborescence de notre rôle Ansible :
.
└── timer.yml/
├── timer/
├── ├── defaults
├── │ └── main.yml
├── ├── tasks
├── │ └── main.yml
└── └── templates/
├── backup.service.j2
└── backup.timer.j2
Pour l'exemple, nous allons partir sur l'orchestration d'une commande rsync tous les jours à 7h du matin.
L’équivalent du job Cron correspond au contenu de ce fichier placé dans le répertoire /etc/cron.d
:
0 7 * * * root /usr/bin/rsync -avz --delete /var/www/html/ user@remote-server:/backup/
Les variables
Nous allons utiliser des variables qui serviront principalement à notre template Jinja2. En effet, dans le cas d'un changement, il est préférable de modifier un fichier de variables plutôt qu'un template Jinja2 qui peut être parfois complexe.
Créons notre fichier defaults/main.yml
:
---
backup:
path: /usr/bin/rsync
source:
folder: /var/www/html/
destination:
folder: /backup/
user: user
server: remote-server
service:
name: backup.service
timer:
name: backup.timer
orchestration: 07:00:00
Vous avez remarqué que mes variables sont ordonnées dans une liste, cela est plus lisible que des variables du type backup_path
ou encore backup_source
.
Les Templates Jinja2
Pour créer un timer SystemD, il faut créer deux fichiers. Le premier concerne le service SystemD. Je vous invite à consulter mon article dédié à SystemD afin de comprendre de quoi est composé un service SystemD :
Place à la création du Template Jinja2 situé dans templates/backup.service.j2
[Unit]
Description=Backup Service
[Service]
ExecStart={{ backup.path }} -avz --delete {{ backup.source.folder }} {{ backup.destination.user }}@{{ backup.destination.server }}:{{ backup.destination.folder }}
Le rendu final de ce Template Jinja2 correspond à ceci :
[Unit]
Description=Backup Service
[Service]
ExecStart=/usr/bin/rsync -avz --delete /var/www/html/ user@remote-server:/backup/
Le second Template Jinja2 concerne cette fois-ci le timer SystemD. Il faut le voir comme l'équivalent de l'orchestrateur. C'est dans ce fichier que l'on va décrire la périodicité d'exécution du service SystemD ainsi que d'autres paramètres.
Passons à la création du fichier templates/backup.timer.j2
:
[Unit]
Description=Run Backup Service Daily
[Timer]
OnCalendar=*-*-* {{ backup.timer.orchestration }}
Persistent=true
[Install]
WantedBy=timers.target
Le rendu final de ce Template Jinja2 correspond à ceci :
[Unit]
Description=Run Backup Service Daily
[Timer]
OnCalendar=*-*-* 07:00:00
Persistent=true
[Install]
WantedBy=timers.target
Dans le bloc [Timer]
, la variable OnCalendar
correspond à la périodicité d'exécution du service SystemD précédemment créé. C'est l'équivalent de ceci pour un job Cron :
0 7 * * *
Les tasks
Nous allons avoir deux tasks Ansible. Une qui va copier les deux templates Jinja2 sur les serveurs distants et une deuxième qui va activer le nouveau service SystemD ainsi que son timer.
Voici ce que donne le fichier tasks/main.yml
:
- name: Configure systemd backup service
template:
src: "systemd/system/{{ item }}.j2"
dest: "/etc/systemd/system/{{ item }}"
owner: root
group: root
mode: '0755'
loop:
- "{{ backup.service.name }}"
- "{{ backup.timer.name }}"
- name: Enable backup timer service
systemd:
name: "{{ backup.timer.name }}"
daemon_reload: yes
enabled: yes
state: "started"
masked: no
Il est important de noter que j'ai utilisé le module Ansible systemd
et que j'ai défini la variable masked
à no
pour être sûr que le service SystemD ne sera pas masked
.
Avec ce rôle Ansible, vous pourrez déployer autant de timers SystemD que vous le souhaitez !