cClaude.rocks ☕ Le blog

L'informatique et les nouvelles technologies

Menu

Vous êtes-vous déjà demandé comment modifier un script systemd sous Debian et ces dérivés ?

Cela se fait à l’aide de la commande systemctl edit qui assure le redémarrage, et ne modifie pas le code venant du paquet du mainteneur. Cela vous évitera bien des questions lors de la mise à jour de votre système. De plus, cela met en évidence vos modifications.

systemctl edit NOM_DU_SERVICE

Prenons le script BIND9 systemd comme exemple. Un brouillon des meilleures pratiques chez ISC stipule que, de par sa conception et pour des raisons de sécurité, le mode de défaillance le plus courant pour BIND est la fin intentionnelle du processus lorsque celui-ci rencontre un état incohérent. Un processus d’alerte automatique capable de redémarrer intelligemment BIND est recommandé…

Suite à cette recommandation, on veut s’assurer que BIND est redémarré automatiquement en cas d’échec, ce qui n’est pas le cas par défaut. Pour que systemd redémarre un service de manière automatique, vous devez ajouter la directive Restart= au fichier de configuration du service.

Pour comprendre il faut s’attarder sur la documentation de 5ᵉ niveau de systemd :

man 5 systemd.service

On y trouve ce tableau très intéressant à propos de la clause Restart= :


┌──────────────────────┬────┬────────┬────────────┬────────────┬─────────────┬──────────┬─────────────┐
│Restart settings/Exit │ no │ always │ on-success │ on-failure │ on-abnormal │ on-abort │ on-watchdog │
│causes                │    │        │            │            │             │          │             │
├──────────────────────┼────┼────────┼────────────┼────────────┼─────────────┼──────────┼─────────────┤
│Clean exit code or    │    │ X      │ X          │            │             │          │             │
│signal                │    │        │            │            │             │          │             │
├──────────────────────┼────┼────────┼────────────┼────────────┼─────────────┼──────────┼─────────────┤
│Unclean exit code     │    │ X      │            │ X          │             │          │             │
├──────────────────────┼────┼────────┼────────────┼────────────┼─────────────┼──────────┼─────────────┤
│Unclean signal        │    │ X      │            │ X          │ X           │ X        │             │
├──────────────────────┼────┼────────┼────────────┼────────────┼─────────────┼──────────┼─────────────┤
│Timeout               │    │ X      │            │ X          │ X           │          │             │
├──────────────────────┼────┼────────┼────────────┼────────────┼─────────────┼──────────┼─────────────┤
│Watchdog              │    │ X      │            │ X          │ X           │          │ X           │
└──────────────────────┴────┴────────┴────────────┴────────────┴─────────────┴──────────┴─────────────┘

Comme le DNS est très important dans mon infrastructure, j’ai choisi Restart=always ce qui permet d’assurer un redémarrage du service même s’il est sorti avec succès.

Notez que le service ne redémarrera pas automatiquement si vous utilisez service bind9 stop ou systemctl stop bind9.

Pour ajouter Restart= au problème de fichier de service, utilisons la solution prévue par systemd :

sudo systemctl edit bind9.service

et dans l’éditeur qui a été ainsi ouvert, il suffit d’écrire :

[Service]
Restart=always

Enregistrez et quittez le fichier. Jetez un coup d’œil au service à l’aide de :

systemctl status bind9
● bind9.service - BIND Domain Name Server
   Loaded: loaded (/lib/systemd/system/bind9.service; enabled; vendor preset: enabled)
  Drop-In: /etc/systemd/system/bind9.service.d
           └─override.conf
   Active: active (running) since Tue 2019-07-30 11:20:42 CEST; 23s ago
     Docs: man:named(8)
  Process: 9453 ExecStop=/usr/sbin/rndc stop (code=exited, status=0/SUCCESS)
 Main PID: 9458 (named)
    Tasks: 7 (limit: 4915)
   CGroup: /system.slice/bind9.service
           └─9458 /usr/sbin/named -f -4 -u bind

Vous remarquerez que la section Drop-In contient une référence vers override.conf. Choisissons le PID 9458 (tache named) et tuons le service à l’aide de :

sudo kill -9 9458

Ensuite vérifions l’état du service avec systemctl status bind9 :

● bind9.service - BIND Domain Name Server
   Loaded: loaded (/lib/systemd/system/bind9.service; enabled; vendor preset: enabled)
  Drop-In: /etc/systemd/system/bind9.service.d
           └─override.conf
   Active: active (running) since Tue 2019-07-30 11:22:39 CEST; 34s ago
     Docs: man:named(8)
  Process: 11403 ExecStop=/usr/sbin/rndc stop (code=exited, status=1/FAILURE)
 Main PID: 11408 (named)
    Tasks: 7 (limit: 4915)
   CGroup: /system.slice/bind9.service
           └─11408 /usr/sbin/named -f -4 -u bind

Il a maintenant un nouveau PID.

La commande journalctl -xe donne un peu plus de détails :

Jul 30 11:22:39 framboise10 systemd[1]: bind9.service: Main process exited, code=killed, status=9/KILL
Jul 30 11:22:39 framboise10 rndc[11403]: rndc: connect failed: 127.0.0.1#953: connection refused
Jul 30 11:22:39 framboise10 systemd[1]: bind9.service: Control process exited, code=exited status=1
Jul 30 11:22:39 framboise10 systemd[1]: bind9.service: Unit entered failed state.
Jul 30 11:22:39 framboise10 systemd[1]: bind9.service: Failed with result 'signal'.
Jul 30 11:22:39 framboise10 systemd[1]: bind9.service: Service hold-off time over, scheduling restart.
Jul 30 11:22:39 framboise10 systemd[1]: Stopped BIND Domain Name Server.
-- Subject: Unit bind9.service has finished shutting down
-- Defined-By: systemd
-- Support: https://www.debian.org/support
--
-- Unit bind9.service has finished shutting down.
Jul 30 11:22:39 framboise10 systemd[1]: Started BIND Domain Name Server.
-- Subject: Unit bind9.service has finished start-up
-- Defined-By: systemd
-- Support: https://www.debian.org/support
--
-- Unit bind9.service has finished starting up.
--
-- The start-up result is done.
Jul 30 11:22:39 framboise10 named[11408]: starting BIND 9.10.3-P4-Raspbian <id:ebd72b3> -f -4 -u bind

Notez que systemd a remarqué que BIND a été tué et l’a redémarré.

Bon c’est bien mais comment scripter cela ?

La solution est étonnamment simple…

tmpfile=$( mktemp )
cat <<EOF >"${tmpfile}"
[Service]
Restart=always
EOF

sudo env SYSTEMD_EDITOR="cp ${tmpfile}" systemctl edit bind9.service

Wouhai, bon avec quelques explications se sera plus simple :


Première étape, création d’un fichier temporaire, et on récupère le nom dans la variable tmpfile :

tmpfile=$( mktemp )

La syntaxe $( xxx ) permet d’exécuter la commande xxx et prend comme résultat la sortie standard. On parle de sortie standard pour différencier les affichages qui ne sont pas des messages d’erreurs et qui ne seront pas pris en compte.


Ensuite on initialise le fichier, avec le contenu souhaité :

cat <<EOF >"${tmpfile}"
[Service]
Restart=always
EOF

Ici, la syntaxe cat <<EOF … EOF permet de définir une zone de texte qui sera considérée comme une entrée standard, que l’on redirige vers le fichier à l’aide de >"${tmpfile}". La zone de texte commande après la ligne contenant <<EOF et fini ajuste avant la ligne contenant EOF


Ensuite se trouve l’astuce. Pour comprendre, il faut savoir que systemctl va regarder le contenu de la variable SYSTEMD_EDITOR pour choisir le programme qui sera utilisé pour éditer le fichier. Le lancement de l’éditeur se fera à l’aide : commande_editeur nom_du_fichier_a_editer.

En utilisant la syntaxe SYSTEMD_EDITOR="cp ${tmpfile}", on indique le l’éditeur est la commande cp ${tmpfile}, commande qui sera complétée par le nom du fichier qu’ajoutera systemctl edit.

sudo env SYSTEMD_EDITOR="cp ${tmpfile}" systemctl edit bind9.service

Pour être exhaustif sur la syntaxe de la ligne :

  • La commande sudo indique que l’on souhaite avoir les privilèges administrateurs,
  • ensuite env précise que l’on définit une variable d’environnement (qui n’existera que pour la commande suivante),
  • Après on trouve la définition de la variable, suivant de la commande qui sera effectuée.

Concrètement j'utilise ce code à travers une fonction que j’ai nommée patch_systemd comme suit :

function patch_systemd {
  local tmpfile=

  tmpfile="$( mktemp )"

  cat <<EOF >"${tmpfile}"
[Service]
Restart=always
EOF

  sudo env SYSTEMD_EDITOR="cp ${tmpfile}" systemctl edit bind9.service
}

Il suffit d’appeler la fonction pour mettre à jour la configuration du service.

Références

ᦿ


ℹ 2006 - 2020 | 🕸 Retour à l'accueil du domaine | 🏡 Retour à l'accueil du blog