Tech Blog.

Thoughts, stories, ideas.

Systèmed : dépendances de démarrage et d’arrêt

18. February 2016

Le problème

Un serveur Debian (jessie, systemd 215) exécute une application web qui nécessite un partage samba (cifs) via openvpn (IPv6). Avec le manuel de configuration naïve, le démarrage et l’arrêt fonctionnent bien, mais le redémarrage ne fonctionne pas correctement.

Configuration naïve :

  • Debian Packages: apache2 cifs-utils openvpn
  • cifs mount in /etc/fstab konfiguriert
  • openvpn Konfiguration in /etc/openvpn/client.conf

Si vous démarrez tout individuellement, cela fonctionne parfaitement :

  • systemctl start openvpn@client.service
  • mount /var/www/data
  • systemctl start apache2.service

L’arrêt est également facile à effectuer manuellement :

  • systemctl stop apache2.service
  • umount /var/www/data
  • systemctl stop openvpn@client.service

Mais un redémarrage ne démarre pas.…

  • Au démarrage, la monture est essayée avant que le VPN ne soit disponible et échoue bien sûr.
  • Apache s’exécute, mais l’application web a un problème d’accès aux fichiers dans la monture cifs.

… ni ne s’éteint :

  • Le VPN est interrompu avant que le démontage ne soit terminé.
  • Le système reste en attente pendant 2 minutes jusqu’à ce que le délai d’expiration du démontage soit expiré. C’est très ennuyeux.

Approche #1

La solution ne devrait pas être trop difficile avec[systemd units] (https://www.freedesktop.org/software/systemd/man/systemd.unit.html).

Comme /etc/fstab ne peut pas être utilisé pour formuler explicitement des dépendances, un fichier unité séparé est créé pour le montage. Nous commençons par le fichier unitaire généré automatiquement :

systemctl cat var-www-www-data.mount > /etc/systemd/system/system/system/var-www-data.mount`

Et supprimez les entrées SourcePath' etDocumentation’, de sorte qu’il ne reste que le minimum :

#/etc/systemd/system/var-www-data.mount
[Unit]
Before=remote-fs.target

[Mount]
What=//v6.smb.example.com/WwwDataShare
Where=/var/www/data
Type=cifs
Options=ro,guest,iocharset=utf8

Ensuite, l’entrée est supprimée de /etc/fstab et l’unité est activée avec :

systemctl enable var-www-data.mount ; systemctl daemon-reload`

Il est important de noter qu’il est possible d’utiliser les données d’une manière simple et rapide et d’une manière efficace.

Maintenant nous pouvons formuler la dépendance à openvpn :

#/etc/systemd/system/var-www-data.mount
[Unit]
Before=remote-fs.target
After=openvpn@client.service
Requires=openvpn@client.service

systemctl daemon-reload

Ça ne marche toujours pas, j’en ai peur. Après diverses analyses avec `journalctl -f’, il est apparu clairement que

  • l’unité openvpn prétend être “prête” au démarrage, bien que les routes IPv6 ne soient pas encore correctement définies.
  • l’umount de /var/www/data prétend être prêt quand il s’arrête, bien qu’il y ait toujours une communication réseau en attente.

Solution

Tout d’abord, vous avez besoin d’un script qui vérifie les conditions préalables au démarrage (ping') et à l'arrêt (umount’) :

/etc/openvpn/checks_updown

#!/bin/bash
LOGGER_RED="systemd-cat -t $0 -p err"
LOGGER_BOLD="systemd-cat -t $0 -p notice"
LOGGER_NORM="systemd-cat -t $0 -p info"

case "$1" in
  ping) 
    while true
    do
      if ping6 -q -c 3 v6.smb.example.com > /dev/null 2>&1 
      then
        echo ping6 ok | $LOGGER_BOLD
        exit 0
      else
        echo "." | $LOGGER_NORM
        sleep 4
      fi
    done
    ;;
  umount) 
    while true
    do
      if mount | grep "//v6.smb.example.com/" >/dev/null 2>&1
      then
        echo ";" | $LOGGER_NORM
        sleep 4
      else
        echo umount ok | $LOGGER_BOLD
        exit 0
      fi
    done
    ;;
  *) 
    echo "invalid call: $1" | tee | $LOGGER_RED
    exit 1
    ;;
esac

Pour ce script, nous construisons notre propre unité :

# /etc/systemd/system/vpnbarrier.service
[Unit]
Requires=openvpn@client.service
After=openvpn@client.service

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/etc/openvpn/checks_updown ping
ExecStop=/etc/openvpn/checks_updown umount

[Install]
RequiredBy=var-www-data.mount

Le RemainAfterExit=yes' provoque (aveconeshot’) que ExecStop n’est pas exécuté tant que cette unité n’est pas arrêtée. Sans ce réglage, ExecStop serait exécuté immédiatement après ExecStart, c’est-à-dire au démarrage.

Étendre les dépendances dans l’unité `mount’ :

# /etc/systemd/system/var-www-data.mount
[Unit]
Before=apache2.service
Requires=vpnbarrier.service
After=vpnbarrier.service

[Mount]
...

[Install]
RequiredBy=apache2.service

systemctl enable vpnbarrier.service; systemctl daemon-reload

C’est seulement maintenant que tout fonctionne comme il se doit :

  • systemctl stop openvpn@client.service : D’abord apache2 est arrêté, puis /var/www/data est déconnecté et seulement ensuite le VPN est arrêté.
  • systemctl start apache2.service` : D’abord le VPN est lancé, puis le cifs-mount et enfin apache2.
  • Un redémarrage prend moins de 20 secondes.

Remarques

  • La documentation de systemd est assez bonne, par exemple àdigitalocean. Cependant, l’accent est surtout mis sur la montée en puissance et il est difficile de trouver quoi que ce soit à descendre en voiture.
  • Une[discussion quelque peu différente] (https://github.com/systemd/systemd/issues/2647) a montré qu’il y en a d’autres qui manquent de quelque chose.
    *L’intuition la plus importante : **Utilisez toujours Requires' etAfter’, c’est le seul moyen d’obtenir un contrôle sur l’arrêt.
  • Cause racine : Le problème est-il un problème générique du système ou un bogue dans le paquet openvpn ? Et à l’umount de la C.I.F.S. ?
  • Le problème pourrait-il être résolu un peu plus élégamment, par exemple en se passant d’une unité séparée vpnbarrier.service' et en implémentant le contrôle "ping" ou "umount" par des entrées dans les répertoires*.conf.d` ?
  • De temps en temps, il ne suffisait pas de faire un `systemd daemon-reload’, seulement après un redémarrage, tout fonctionnait comme prévu.